StyledTextKit is a declarative attributed string library for fast rendering and easy string building.

Overview

StyledTextKit is a declarative attributed string library for fast rendering and easy string building. It serves as a simple replacement to NSAttributedString and UILabel for background-thread sizing and bitmap caching.

Features

  • Declarative attributed string building API
  • Find text sizes on a background thread without sanitizer warnings
  • Cache rendered text bitmaps for improved performance
  • Custom attribute interaction handling (link taps, etc)

Installation

Just add StyledTextKit to your Podfile and install. Done!

pod 'StyledTextKit'

Usage

Building NSAttributedStrings

StyledTextKit lets you build complex NSAttributedStrings:

  • Append NSAttributedStrings or Strings while re-using the string's current attributes, saving you from repetitive .font and .foregroundColor styling.
  • Intermix complex font traits like bold and italics to get bold italics.
  • Handle dynamic text size at string render time. Lets you build the string once and re-render it on device text-size changes.
  • Call save() and restore() to push/pop style settings, letting you build complex text styles without complex code.
let attributedString = StyledTextBuilder(text: "Foo ")
  .save()
  .add(text: "bar", traits: [.traitBold])
  .restore()
  .add(text: " baz!")
  .build()
  .render(contentSizeCategory: .large)

Foo bar baz!

The basic steps are:

  • Create a StyledTextBuilder
  • Add StyledText objects
  • Call build() when finished to generate a StyledTextString object
  • Call render(contentSizeCategory:) to create an NSAttributedString

Rendering Text Bitmaps

Create a StyledTextRenderer for sizing and rendering text by initializing it with a StyledTextString and a UIContentSizeCategory.

let renderer = StyledTextRenderer(
  string: string,
  contentSizeCategory: .large
)

Once created, you can easily get the size of the text constrained to a width:

let size = renderer.size(in: 320)

You can also get a bitmap of the text:

let result = renderer.render(for: 320)
view.layer.contents = result.image

StyledTextView

To make rendering and layout of text in a UIView simpler, use StyledTextView to manage display as well as interactions. All you need is a StyledTextRenderer and a width and you're set!

let view = StyledTextView()
view.configure(with: renderer, width: 320)

Set a delegate on the view to handle tap and long presses:

view.delegate = self

// StyledTextViewDelegate
func didTap(view: StyledTextView, attributes: [NSAttributedStringKey: Any], point: CGPoint) {
  guard let link = attributes[.link] else { return }
  show(SFSafariViewController(url: link))
}

Background Rendering

StyledTextKit exists to do background sizing and rendering of text content so that scrolling large amounts of text is buttery smooth. The typical pipeline to do this is:

  1. Get the current width and UIContentSizeCategory
  2. Go to a background queue
  3. Build text
  4. Warm caches
  5. Return to the main queue
  6. Configure your views
// ViewController.swift

let width = view.bounds.width
let contentSizeCategory = UIApplication.shared.preferredContentSizeCategory

DispatchQueue.global().async {
  let builder = StyledTextBuilder(...)
  let renderer = StyledTextRenderer(string: builder.build(), contentSizeCategory: contentSizeCategory)
    .warm(width: width) // warms the size cache

  DispatchQueue.main.async {
    self.textView.configure(with: renderer, width: width)
  }
}

FAQ

Why not use UITextView?

Prior to iOS 7, UITextView just used WebKit under the hood and was terribly slow. Now that it uses TextKit, it's significantly faster but still requires all sizing and rendering be done on the main thread.

For apps with lots of text embedded in UITableViewCells or UICollectionViewCells, UITextView bring scrolling to a grinding halt.

Acknowledgements

Comments
  • Add UI test to tap link

    Add UI test to tap link

    didTap and didLongPress are not working in the current version (f4927d2). StyledTextView doesn't receive touch events because its layer has no content. The content has been moved to contentLayer at the commit 1271289. I added hitTest to forward hit testing to contentLayer to fix that.

    opened by ryohey 9
  • Improvement/render-highlight

    Improvement/render-highlight

    Implemented a way to always have the highlight view always present around a range of text, instead of just having to tap on the text to get it to appear.

    opened by kylelol 5
  • CATextLayer?

    CATextLayer?

    First, thanks for this amazing library.

    This is a question though, not an issue.

    Doesn't CATextLayer provide a similar functionality? I specifically mean the async drawing bit, great work!

    CATextLayer has an inherited drawsAsynchronously property. The docs says:

    When this property is set to true, the graphics context used to draw the layer’s contents queues drawing commands and executes them on a background thread rather than executing them synchronously. Performing these commands asynchronously can improve performance in some apps. However, you should always measure the actual performance benefits before enabling this capability.

    Aside from caching, what advantage does asynchronously outputting to a CGImage have over what CATextLayer provides?

    Thanks!

    opened by ahmedk92 3
  • Add an example project

    Add an example project

    Hi @rnystrom, thanks for this library and let me build attributed string much easier than before. 🎉

    I want to add an example project for it, can I make a PR?

    ezgif-4-ebb48f83c0

    And also ask a question here, is it possible to let .link used other styles? Thanks again! ❤️

    opened by marcuswu0814 3
  • Disable StyledTextView layer's contents transition with animation

    Disable StyledTextView layer's contents transition with animation

    Hi, Thank you for this amazing library.

    When I use StyledTextView in UICollectionViewCell, I found a reuse problem when reloading renderer, and found that the animatable of layer’s contents caused the view content to follow the effect of an animation transition.

    https://github.com/GitHawkApp/StyledTextKit/blob/fe3232c980c374d59e520b086b04869e3c920f86/Source/StyledTextView.swift#L190

    Suggested to change to:

       CATransaction.begin()
       CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
       layer.contents = result.0
       CATransaction.commit()
    
    opened by wleii 2
  • Leak in LRUCache

    Leak in LRUCache

    I believe that the doubly-linked list in the LRUCache impl has a retain cycle in its nodes:

    https://github.com/ocrickard/StyledTextKit/blob/master/Source/LRUCache.swift#L24

    opened by ocrickard 2
  • Make NSAttributedString accessibility a first-class feature

    Make NSAttributedString accessibility a first-class feature

    cc @BasThomas https://developer.apple.com/documentation/foundation/nsattributedstringkey

    These accessibility keys are kind of incredible. It'd be really amazing to bake these into the framework somehow.

    Can probably get away w/ enumerating all attributes on render() and injecting mapped accessibility attributes? Most of them are straightforward:

    • Font
    • Foreground color
    • Background color
    • Strikethrough
    • Underline
    • Link
    opened by rnystrom 2
  • Use UIFontMetrics to scale fonts

    Use UIFontMetrics to scale fonts

    I saw your comment about the article I wrote in #52 and figured I had to take a quick stab at fixing that for you. 😄 Let me know if you'd like to see more tests, you can come up with a nicer name than scalingTextStyle, or if there is any documentation I should update.

    On iOS 11 and up, use UIFontMetrics to scale font sizes. Falls back to a calculation using the current content size category / large as a multiplier.

    Also adds the scalingTextStyle attribute to TextStyle with a default value of body so that users may specify how they'd prefer to scale the specific font.

    This should not be a breaking change for anyone since it's an added field with a default value. All tests still pass without adding this new value anywhere.

    opened by davelyon 1
  • RubyText supported?

    RubyText supported?

    i'm working on a Japanese project , it has a feature to show/hide Hurigana(Ruby) text. i've some knowledge that it uses kCTRubyAnnotation but it is harsh on main thread..

    so, is this library supports RubyText?

    opened by vatsal1992 1
  • Add a drawsAsync property to the text view

    Add a drawsAsync property to the text view

    This is my first pass at a threadsafe async drawing system.

    Problems to resolve before merge:

    1. If the bitmap is cached, there's no purpose to this, it should short-circuit instead of trampolining.
    2. The logic inside this function feels a little icky. We should clean it up a little bit.

    Known problems:

    1. Testing, as with all async modes, is nearly impossible.
    opened by ocrickard 1
  • Add test to check keys match and delete dead code

    Add test to check keys match and delete dead code

    I'm getting a reproducible but isolated cache miss in GitHawk. Did some digging, nothing obvious. But added this test just as a safety.

    GitHawk key's are unequal b/c the UIFont instances are different. Pretty weird.

    screen shot 2018-11-10 at 4 55 11 pm
    opened by rnystrom 0
  • Safer locking

    Safer locking

    I learned about the use of unfair locks from this library. Thanks!

    Anyway, today I stumbled upon this StackOverflow answer that mentioned that the direct use of unfair locks in Swift can fail at runtime. The fix is really simple; a pointer wrapper that provides a stable memory address (the assumption the C API makes). Any thoughts about this?

    opened by ahmedk92 0
  • Is it possible to create a string with mixed fonts / font sizes?

    Is it possible to create a string with mixed fonts / font sizes?

    I'm trying to create a string that has a mix of two font sizes, but I can't seem to make it work. Is this supported by StyledTextKit? (if not, would this be addressed by issue #61 ?)

    Here's a sample of what I tried. Adding calls to save() / restore() don't affect the behavior, nor does changing the contentSizeCategory

    let attrStr = StyledTextBuilder(text: "")
            .add(text: "Foo", attributes: [.font: UIFont.systemFont(ofSize: 32)])
            .add(text: "Bar", attributes: [.font: UIFont.systemFont(ofSize: 14)])
            .build()
            .render(contentSizeCategory: .unspecified)
    
    opened by eliburke 0
  • Separate render and style params from building

    Separate render and style params from building

    Love this blog post! Removing all color/font from building would be amazing. Instead:

    • Use "style builder" pattern that registers styles
      • Need to still be able to "push" and "pop" styles
      • Should all props be in one style (.font, .foregroundColor, etc), or should there be a style per prop? Latter seems harder to maintain
    • Remove UIContentSizeCategory from all params until render

    Necessary to make progress towards GitHawkApp/GitHawk#198 so you can have themes/styles and re-render (missing all caches) w/out having to rebuild everything (which you'd have to do today).

    enhancement 
    opened by rnystrom 0
Declarative text styles and streamlined Dynamic Type support for iOS

StyledText StyledText is a library that simplifies styling dynamic text in iOS applications. Instead of having to use attributed strings every time yo

Blue Apron 233 Oct 18, 2022
TTextField is developed to help developers can initiate a fully standard textfield including title, placeholder and error message in fast and convinient way without having to write many lines of codes

TTextField is developed to help developers can initiate a fully standard textfield including title, placeholder and error message in fast and convinient way without having to write many lines of codes

Nguyen Duc Thinh 7 Aug 28, 2022
Easy-to-use token field that is used in the Venmo app.

VENTokenField VENTokenField is the recipients token field that is used in the Venmo compose screen. Installation The easiest way to get started is to

Venmo 797 Dec 6, 2022
A TextView that provides easy to use tagging feature for Mention or Hashtag

Tagging A TextView that provides easy to use tagging feature for Mention or Hashtag. Introduction Tagging is a UIView that encloses a TextView that co

DongHee Kang 109 Dec 5, 2022
⚡️ A library of widgets and helpers to build instant-search applications on iOS.

By Algolia. InstantSearch family: InstantSearch iOS | InstantSearch Android | React InstantSearch | InstantSearch.js | Angular InstantSearch | Vue Ins

Algolia 568 Jan 4, 2023
RichTextKit is a Swift-based library for working with rich text in UIKit, AppKit and SwiftUI.

About RichTextKit RichTextKit is a Swift-based library that lets you work with rich text in UIKit, AppKit and SwiftUI. RichTextKit is under developmen

Daniel Saidi 282 Dec 28, 2022
AEOTPTextField - A beautiful iOS OTP Text Field library, written in Swift with full access customization in UI.

AEOTPTextField - A beautiful iOS OTP Text Field library, written in Swift with full access customization in UI.

Abdelrhman Kamal 79 Jan 3, 2023
VMaskTextField is a library which create an input mask for iOS.

VMaskTextField An inputmask helps the user with the input by ensuring a predefined format. This can be useful for dates, numerics, phone numbers etc U

Vinícius Oliveira 384 Jul 20, 2022
Library that allows you binding `enabled` property of button with textable elements (TextView, TextField)

What is NxEnabled? It's a fairly common case, when the enabled state of button depends on some textable elements such as TextView, TextField. So this

Nikita Ermolenko 33 Sep 20, 2021
VKPinCodeView is simple and elegant UI component for input PIN. You can easily customise appearance and get auto fill (OTP) iOS 12 feature right from the box.

Features Variable PIN length Underline, border and custom styles The error status with / without shake animation Resetting the error status manually,

Vladimir Kokhanevich 95 Nov 24, 2022
A Credit Amount and EMI User Interface build in Swift and SwiftUI

App Usage An iPhone application build in swift . Overview Supported on All iPhone Screen Sizes Dynamic Data following MVVM Design Pattern Used Transit

Mohammad Yasir 4 Apr 20, 2022
An auto-layout base UITextView subclass which automatically grows with user input and can be constrained by maximal and minimal height - all without a single line of code

Deprecated This library is no longer maintained and is deprecated. The repository might be removed at any point in the future. MBAutoGrowingTextView A

Matej Balantič 125 Jan 13, 2022
Awesome TextField is a nice and simple libriary for iOS and Mac OSX

Awesome TextField is a nice and simple libriary for iOS and Mac OSX. It's highly customisable and easy-to-use tool. Works perfectly for any registration or login forms in your app.

Alexander Shoshiashvili 225 Nov 21, 2022
A Float Input View with smooth animation and supporting icon and seperator written with Swift

RSFloatInputView Features Smooth animation using CoreText Support optional left icon Support optional seperator Configurable padding, size, fonts and

null 103 Nov 13, 2022
A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments

InputBarAccessoryView Features Autocomplete text with @mention, #hashtag or any other prefix A self-sizing UITextView with an optional fixed height (c

Nathan Tannar 968 Jan 2, 2023
A Swift framework for parsing, formatting and validating international phone numbers. Inspired by Google's libphonenumber.

PhoneNumberKit Swift 5.3 framework for parsing, formatting and validating international phone numbers. Inspired by Google's libphonenumber. Features F

Roy Marmelstein 4.7k Dec 29, 2022
DTTextField is a custom textfield with floating placeholder and error label

DTTextField Introduction DTTextField is a UITextField library with floating placeholder and error label. Floating placeholder inspired from JVFloatLab

Dhaval Thanki 310 Jan 5, 2023
A custom TextField with a switchable icon which shows or hides the password and enforce good password policies

PasswordTextField A custom TextField with a switchable icon which shows or hides the password and enforces good password policies, written in Swift. ⭐

Chris Jimenez 304 Dec 29, 2022
A beautiful and flexible text field control implementation of "Float Label Pattern". Written in Swift.

SkyFloatingLabelTextField SkyFloatingLabelTextField is a beautiful, flexible and customizable implementation of the space saving "Float Label Pattern"

Skyscanner 4k Jan 1, 2023