# SwiftRichString **Repository Path**: szlsay/SwiftRichString ## Basic Information - **Project Name**: SwiftRichString - **Description**: No description available - **Primary Language**: Swift - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-11 - **Last Updated**: 2021-09-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
Elegant Attributed String composition in Swift sauce
SwiftRichString is a lightweight library which allows to create and manipulate attributed strings easily both in iOS, macOS, tvOS and even watchOS. It provides convenient way to store styles you can reuse in your app's UI elements, allows complex tag-based strings rendering and also includes integration with Interface Builder. ## Main Features | | Features Highlights | |--- |--------------------------------------------------------------------------------- | | 🦄 | Easy styling and typography managment with coincise declarative syntax | | 🏞 | Attach local images (lazy/static) and remote images inside text | | 🧬 | Fast & high customizable XML/HTML tagged string rendering | | 🌟 | Apply text transforms within styles | | 📐 | Native support for iOS 11 Dynamic Type | | 🖇 | Support for Swift 5.1's function builder to compose strings | | ⏱ | Compact code base with no external dependencies. | | 🐦 | Fully made in Swift 5 from Swift ❥ lovers | ### Easy Styling ```swift let style = Style { $0.font = SystemFonts.AmericanTypewriter.font(size: 25) // just pass a string, one of the SystemFonts or an UIFont $0.color = "#0433FF" // you can use UIColor or HEX string! $0.underline = (.patternDot, UIColor.red) $0.alignment = .center } let attributedText = "Hello World!".set(style: style) // et voilà! ``` ### XML/HTML tag based rendering SwiftRichString allows you to render complex strings by parsing text's tags: each style will be identified by an unique name (used inside the tag) and you can create a `StyleXML` (was `StyleGroup`) which allows you to encapsulate all of them and reuse as you need (clearly you can register it globally). ```swift // Create your own styles let normal = Style { $0.font = SystemFonts.Helvetica_Light.font(size: 15) } let bold = Style { $0.font = SystemFonts.Helvetica_Bold.font(size: 20) $0.color = UIColor.red $0.backColor = UIColor.yellow } let italic = normal.byAdding { $0.traitVariants = .italic } let myGroup = StyleXML(base: normal, ["bold": bold, "italic": italic]) let str = "Hello
## Documentation
- [Introduction to `Style`, `StyleXML` & `StyleRegEx`](#styleStyleXML)
- [String & Attributed String concatenation](#concatenation)
- [Apply styles to `String` & `Attributed String`](#manualstyling)
- [Fonts & Colors in `Style`](#fontscolors)
- [Derivating a `Style`](#derivatingstyle)
- [Support Dynamic Type](#dynamictype)
- [Render XML tagged strings](#customizexmlstrings)
- [Customize XML rendering: react to tag's attributes and unknown tags](#xmlstrings)
- [Custom text transforms](#texttransforms)
- [Local & Remote Images inside text](#images)
- [The `StyleManager`](#stylemanager)
- [Register globally available styles](#globalregister)
- [Defer style creation on demand](#defer)
- [Assign style using Interface Builder](#ib)
- [All properties of `Style`](#props)
Other info:
- [Requirements](#requirements)
- [Installation](#installation)
- [Contributing](#contributing)
- [Copyright](#copyright)
## Introduction to `Style`, `StyleXML`, `StyleRegEx`
The main concept behind SwiftRichString is the use of `StyleProtocol` as generic container of the attributes you can apply to both `String` and `NSMutableAttributedString`.
Concrete classes derivated by `StyleProtocol` are: `Style`, `StyleXML` and `StyleRegEx`.
Each of these classes can be used as source for styles you can apply to a string, substring or attributed string.
### `Style`: apply style to strings or attributed strings
A `Style` is a class which encapsulate all the attributes you can apply to a string. The vast majority of the attributes of both AppKit/UIKit are currently available via type-safe properties by this class.
Creating a `Style` instance is pretty simple; using a builder pattern approach the init class require a callback where the self instance is passed and allows you to configure your properties by keeping the code clean and readable:
```swift
let style = Style {
$0.font = SystemFonts.Helvetica_Bold.font(size: 20)
$0.color = UIColor.green
// ... set any other attribute
}
let attrString = "Some text".set(style: style) // attributed string
```
### `StyleXML`: Apply styles for tag-based complex string
`Style` instances are anonymous; if you want to use a style instance to render a tag-based plain string you need to include it inside a `StyleXML`. You can consider a `StyleXML` as a container of `Styles` (but, in fact, thanks to the conformance to a common `StyleProtocol`'s protocol your group may contains other sub-groups too).
```swift
let bodyStyle: Style = ...
let h1Style: Style = ...
let h2Style: Style = ...
let group = StyleXML(base: bodyStyle, ["h1": h1Style, "h2": h2Style])
let attrString = "Some
## String & Attributed String concatenation
SwiftRichString allows you to simplify string concatenation by providing custom `+` operator between `String`,`AttributedString` (typealias of `NSMutableAttributedString`) and `Style`.
This a an example:
```swift
let body: Style = Style { ... }
let big: Style = Style { ... }
let attributed: AttributedString = "hello ".set(style: body)
// the following code produce an attributed string by
// concatenating an attributed string and two plain string
// (one styled and another plain).
let attStr = attributed + "\(username)!".set(style:big) + ". You are welcome!"
```
You can also use `+` operator to add a style to a plain or attributed string:
```swift
// This produce an attributed string concatenating a plain
// string with an attributed string created via + operator
// between a plain string and a style
let attStr = "Hello" + ("\(username)" + big)
```
Finally you can concatente strings using function builders:
```swift
let bold = Style { ... }
let italic = Style { ... }
let attributedString = AttributedString.composing {
"hello".set(style: bold)
"world".set(style: italic)
}
```
## Apply styles to `String` & `Attributed String`
Both `String` and `Attributed String` (aka `NSMutableAttributedString`) has a come convenience methods you can use to create an manipulate attributed text easily via code:
### Strings Instance Methods
- `set(style: String, range: NSRange? = nil)`: apply a globally registered style to the string (or a substring) by producing an attributed string.
- `set(styles: [String], range: NSRange? = nil)`: apply an ordered sequence of globally registered styles to the string (or a substring) by producing an attributed string.
- `set(style: StyleProtocol, range: NSRange? = nil)`: apply an instance of `Style` or `StyleXML` (to render tag-based text) to the string (or a substring) by producting an attributed string.
- `set(styles: [StyleProtocol], range: NSRange? = nil)`: apply a sequence of `Style`/`StyleXML` instance in order to produce a single attributes collection which will be applied to the string (or substring) to produce an attributed string.
Some examples:
```swift
// apply a globally registered style named MyStyle to the entire string
let a1: AttributedString = "Hello world".set(style: "MyStyle")
// apply a style group to the entire string
// commonStyle will be applied to the entire string as base style
// styleH1 and styleH2 will be applied only for text inside that tags.
let styleH1: Style = ...
let styleH2: Style = ...
let StyleXML = StyleXML(base: commonStyle, ["h1" : styleH1, "h2" : styleH2])
let a2: AttributedString = "Hello
where the `b` tag's blue color was overriden by the color tag attributes and the link in 'here' is clickable.
## Custom Text Transforms
Sometimes you want to apply custom text transforms to your string; for example you may want to make some text with a given style uppercased with current locale.
In order to provide custom text transform in `Style` instances just set one or more `TextTransform` to your `Style`'s `.textTransforms` property:
```swift
let allRedAndUppercaseStyle = Style({
$0.font = UIFont.boldSystemFont(ofSize: 16.0)
$0.color = UIColor.red
$0.textTransforms = [
.uppercaseWithLocale(Locale.current)
]
})
let text = "test".set(style: allRedAndUppercaseStyle) // will become red and uppercased (TEST)
```
While `TextTransform` is an enum with a predefined set of transform you can also provide your own function which have a `String` as source and another `String` as destination:
```swift
let markdownBold = Style({
$0.font = UIFont.boldSystemFont(ofSize: 16.0)
$0.color = UIColor.red
$0.textTransforms = [
.custom({
return "**\($0)**"
})
]
})
```
All text transforms are applied in the same ordered you set in `textTransform` property.
## Local & Remote Images inside text
SwiftRichString supports local and remote attached images along with attributed text.
You can create an attributed string with an image with a single line:
```swift
// You can specify the bounds of the image, both for size and the location respecting the base line of the text.
let localTextAndImage = AttributedString(image: UIImage(named: "rocket")!, bounds: CGRect(x: 0, y: -20, width: 25, height: 25))
// You can also load a remote image. If you not specify bounds size is the original size of the image.
let remoteTextAndImage = AttributedString(imageURL: "http://...")
// You can now compose it with other attributed or simple string
let finalString = "...".set(style: myStyle) + remoteTextAndImage + " some other text"
```
Images can also be loaded by rending an XML string by using the `img` tag (with `named` tag for local resource and `url` for remote url).
`rect` parameter is optional and allows you to specify resize and relocation of the resource.
```swift
let taggedText = """
Some text and this image:
Sometimes you may want to provide these images lazily. In order to do it just provide a custom implementation of the `imageProvider` callback in `StyleXML` instance:
```swift
let xmlText = "-
## Assign style using Interface Builder
SwiftRichString can be used also via Interface Builder.
- `UILabel`
- `UITextView`
- `UITextField`
has three additional properties:
- `styleName: String` (available via IB): you can set it to render the text already set via Interface Builder with a style registered globally before the parent view of the UI control is loaded.
- `style: StyleProtocol`: you can set it to render the text of the control with an instance of style instance.
- `styledText: String`: use this property, instead of `attributedText` to set a new text for the control and render it with already set style. You can continue to use `attributedText` and set the value using `.set()` functions of `String`/`AttributedString`.
Assigned style can be a `Style`, `StyleXML` or `StyleRegEx`:
- if style is a `Style` the entire text of the control is set with the attributes defined by the style.
- if style is a `StyleXML` a base attribute is set (if `base` is valid) and other attributes are applied once each tag is found.
- if style is a `StyleRegEx` a base attribute is set (if `base` is valid) and the attribute is applied only for matches of the specified pattern.
Typically you will set the style of a label via `Style Name` (`styleName`) property in IB and update the content of the control by setting the `styledText`:
```swift
// use `styleName` set value to update a text with the style
self.label?.styledText = "Another text to render" // text is rendered using specified `styleName` value.
```
Otherwise you can set values manually:
```swift
// manually set the an attributed string
self.label?.attributedText = (self.label?.text ?? "").set(myStyle)
// manually set the style via instance
self.label?.style = myStyle
self.label?.styledText = "Updated text"
```
## Properties available via `Style` class
The following properties are available:
| PROPERTY | TYPE | DESCRIPTION |
|-------------------------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
| size | `CGFloat` | font size in points |
| font | `FontConvertible` | font used in text |
| color | `ColorConvertible` | foreground color of the text |
| backColor | `ColorConvertible` | background color of the text |
| shadow | `NSShadow` | shadow effect of the text |
| underline | `(NSUnderlineStyle?,ColorConvertible?)` | underline style and color (if color is nil foreground is used) |
| strikethrough | `(NSUnderlineStyle?,ColorConvertible?)` | strikethrough style and color (if color is nil foreground is used) |
| baselineOffset | `Float` | character’s offset from the baseline, in point |
| paragraph | `NSMutableParagraphStyle` | paragraph attributes |
| lineSpacing | `CGFloat` | distance in points between the bottom of one line fragment and the top of the next |
| paragraphSpacingBefore | `CGFloat` | distance between the paragraph’s top and the beginning of its text content |
| paragraphSpacingAfter | `CGFloat` | space (measured in points) added at the end of the paragraph |
| alignment | `NSTextAlignment` | text alignment of the receiver |
| firstLineHeadIndent | `CGFloat` | distance (in points) from the leading margin of a text container to the beginning of the paragraph’s first line. |
| headIndent | `CGFloat` | The distance (in points) from the leading margin of a text container to the beginning of lines other than the first. |
| tailIndent | `CGFloat` | this value is the distance from the leading margin, If 0 or negative, it’s the distance from the trailing margin. |
| lineBreakMode | `LineBreak` | mode that should be used to break lines |
| minimumLineHeight | `CGFloat` | minimum height in points that any line in the receiver will occupy regardless of the font size or size of any attached graphic |
| maximumLineHeight | `CGFloat` | maximum height in points that any line in the receiver will occupy regardless of the font size or size of any attached graphic |
| baseWritingDirection | `NSWritingDirection` | initial writing direction used to determine the actual writing direction for text |
| lineHeightMultiple | `CGFloat` | natural line height of the receiver is multiplied by this factor (if positive) before being constrained by minimum and maximum line height |
| hyphenationFactor | `Float` | threshold controlling when hyphenation is attempted |
| ligatures | `Ligatures` | Ligatures cause specific character combinations to be rendered using a single custom glyph that corresponds to those characters |
| speaksPunctuation | `Bool` | Enable spoken of all punctuation in the text |
| speakingLanguage | `String` | The language to use when speaking a string (value is a BCP 47 language code string). |
| speakingPitch | `Double` | Pitch to apply to spoken content |
| speakingPronunciation | `String` | |
| shouldQueueSpeechAnnouncement | `Bool` | Spoken text is queued behind, or interrupts, existing spoken content |
| headingLevel | `HeadingLevel` | Specify the heading level of the text |
| numberCase | `NumberCase` | "Configuration for the number case, also known as ""figure style""" |
| numberSpacing | `NumberSpacing` | "Configuration for number spacing, also known as ""figure spacing""" |
| fractions | `Fractions` | Configuration for displyaing a fraction |
| superscript | `Bool` | Superscript (superior) glpyh variants are used, as in footnotes_. |
| `subscript` | `Bool` | Subscript (inferior) glyph variants are used: v_. |
| ordinals | `Bool` | Ordinal glyph variants are used, as in the common typesetting of 4th. |
| scientificInferiors | `Bool` | Scientific inferior glyph variants are used: H_O |
| smallCaps | `Set