Skip to content

Commit 7d4d2d4

Browse files
committed
Merge branch 'release/2.1.0'
2 parents 473d208 + 3f466e4 commit 7d4d2d4

File tree

15 files changed

+342
-54
lines changed

15 files changed

+342
-54
lines changed

.swift-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

Documentation_Assests/Logo.sketch

112 KB
Binary file not shown.

ExampleiOS/ViewController.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class ViewController: UIViewController {
1616

1717
override func viewDidLoad() {
1818
super.viewDidLoad()
19-
19+
2020
let bodyHTML = try! String(contentsOfFile: Bundle.main.path(forResource: "file", ofType: "txt")!)
2121

2222
let headerStyle = Style {
@@ -26,6 +26,13 @@ class ViewController: UIViewController {
2626
}
2727
let boldStyle = Style {
2828
$0.font = UIFont.boldSystemFont(ofSize: self.baseFontSize)
29+
if #available(iOS 11.0, *) {
30+
$0.dynamicText = DynamicText {
31+
$0.style = .body
32+
$0.maximumSize = 35.0
33+
$0.traitCollection = UITraitCollection(userInterfaceIdiom: .phone)
34+
}
35+
}
2936
}
3037
let italicStyle = Style {
3138
$0.font = UIFont.italicSystemFont(ofSize: self.baseFontSize)
@@ -54,13 +61,8 @@ class ViewController: UIViewController {
5461
}])
5562

5663
self.textView?.attributedText = bodyHTML.set(style: style)
64+
if #available(iOS 10.0, *) {
65+
self.textView?.adjustsFontForContentSizeCategory = true
66+
}
5767
}
58-
59-
override func didReceiveMemoryWarning() {
60-
super.didReceiveMemoryWarning()
61-
// Dispose of any resources that can be recreated.
62-
}
63-
64-
6568
}
66-

README.md

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
<p align="center" >
2-
<img src="https://raw.githubusercontent.com/malcommac/SwiftRichString/master/SwiftRichString.png" width=300px alt="SwiftRichString" title="SwiftRichString">
2+
<img src="https://raw.githubusercontent.com/malcommac/SwiftRichString/master/swiftrichstring.png" width=450px alt="SwiftRichString" title="SwiftRichString">
33
</p>
44

55
[![Version](https://img.shields.io/cocoapods/v/SwiftRichString.svg?style=flat)](http://cocoadocs.org/docsets/SwiftRichString) [![License](https://img.shields.io/cocoapods/l/SwiftRichString.svg?style=flat)](http://cocoadocs.org/docsets/SwiftRichString) [![Platform](https://img.shields.io/cocoapods/p/SwiftRichString.svg?style=flat)](http://cocoadocs.org/docsets/SwiftRichString)
66
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SwiftRichString.svg)](https://img.shields.io/cocoapods/v/SwiftRichString.svg)
77
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
88
[![Twitter](https://img.shields.io/badge/[email protected]?style=flat)](http://twitter.com/danielemargutti)
99

10-
<p align="center" >★★ <b>Star me to follow the project! </b> ★★<br>
11-
Created by <b>Daniele Margutti</b> - <a href="http://www.danielemargutti.com">danielemargutti.com</a>
12-
</p>
13-
1410

1511
SwiftRichString is a lightweight library which allows to create and manipulate attributed strings easily both in iOS, macOS, tvOS and even watchOS.
16-
It provides convenient way to store styles you can reuse in your app's UI lements, allows complex tag-based strings rendering and also includes integration with Interface Builder.
12+
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.
13+
14+
It even support **iOS 11's Dynamic Type**!
1715

1816
If you manipulate `NSAttributedString` in Swift, SwiftRichString allows you to keep your code manteniable, readable and easy to evolve.
1917

@@ -79,24 +77,6 @@ That's the result!
7977

8078
<img src="Documentation_Assests/image_2.png" alt="" style="width: 300px;"/>
8179

82-
--
83-
84-
## Other Libraries You May Like
85-
86-
I'm also working on several other projects you may like.
87-
Take a look below:
88-
89-
90-
| Library | Description |
91-
|-----------------|--------------------------------------------------|
92-
| [**SwiftDate**](https://github.com/malcommac/SwiftDate) | The best way to manage date/timezones in Swift |
93-
| [**Hydra**](https://github.com/malcommac/Hydra) | Write better async code: async/await & promises |
94-
| [**FlowKit**](https://github.com/malcommac/FlowKit) | A new declarative approach to table/collection managment. Forget datasource & delegates. |
95-
| [**SwiftRichString**](https://github.com/malcommac/SwiftRichString) | Elegant & Painless NSAttributedString in Swift |
96-
| [**SwiftLocation**](https://github.com/malcommac/SwiftLocation) | Efficient location manager |
97-
| [**SwiftMsgPack**](https://github.com/malcommac/SwiftMsgPack) | Fast/efficient msgPack encoder/decoder |
98-
</p>
99-
10080
--
10181

10282
## Documentation
@@ -115,6 +95,8 @@ Full changelog is available in [CHANGELOG.md](CHANGELOG.md) file.
11595
- [Apply styles to `String` & `Attributed String`](#manualstyling)
11696
- [Fonts & Colors in `Style`](#fontscolors)
11797
- [Derivating a `Style`](#derivatingstyle)
98+
- [Support iOS's `Dynamic Type`](#dynamictype)
99+
- [Dynamic Attributes from Tag's Params](#dynamicattributes)
118100
- [The `StyleManager`](#stylemanager)
119101
- [Register globally available styles](#globalregister)
120102
- [Defer style creation on demand](#defer)
@@ -359,7 +341,47 @@ let subStyle = bold.byAdding {
359341
$0.alignment = center
360342
$0.color = UIColor.red
361343
}
344+
```
345+
346+
<a name="dynamictype"/>
347+
348+
### Conforming to `Dynamic Type`
349+
350+
To support your fonts/text to dynammically scale based on the users preffered content size, you can implement style's `dynamicText` property. UIFontMetrics properties are wrapped inside `DynamicText` class.
351+
352+
353+
```swift
354+
let style = Style {
355+
$0.font = UIFont.boldSystemFont(ofSize: 16.0)
356+
$0.dynamicText = DynamicText {
357+
$0.style = .body
358+
$0.maximumSize = 35.0
359+
$0.traitCollection = UITraitCollection(userInterfaceIdiom: .phone)
360+
}
361+
}
362362
```
363+
364+
<a name="dynamicattributes"/>
365+
366+
### Dynamic Attributes from Tag's Params
367+
368+
SwiftRichString also support some dynamic elements in style applied by reading specific tag parameter's value.
369+
The following example render the `linkURL` property by reading the value from the source string inside `href` tag (like in real HTML text):
370+
371+
```
372+
let normal = Style {
373+
$0.color = UIColor.black
374+
}
375+
let link = Style {
376+
$0.color = UIColor.red
377+
$0.linkURL = URLRepresentable.tagAttribute("href")
378+
}
379+
let group = StyleGroup.init(base: normal, ["a" : link])
380+
381+
let bodyHTML = "Go to <a href=\"http://www.apple.com\">Apple</a> web site!"
382+
self.textView?.attributedText = bodyHTML.set(style: group)
383+
```
384+
363385
<a name="stylemanager"/>
364386

365387
## The `StyleManager`
@@ -619,4 +641,20 @@ SwiftRichString is available under the MIT license. See the LICENSE file for mor
619641

620642
Daniele Margutti: [hello@danielemargutti.com](mailto:hello@danielemargutti.com), [@danielemargutti](https://twitter.com/danielemargutti)
621643

644+
## Other Libraries You May Like
645+
646+
I'm also working on several other projects you may like.
647+
Take a look below:
648+
649+
650+
| Library | Description |
651+
|-----------------|--------------------------------------------------|
652+
| [**SwiftDate**](https://github.com/malcommac/SwiftDate) | The best way to manage date/timezones in Swift |
653+
| [**Hydra**](https://github.com/malcommac/Hydra) | Write better async code: async/await & promises |
654+
| [**FlowKit**](https://github.com/malcommac/FlowKit) | A new declarative approach to table/collection managment. Forget datasource & delegates. |
655+
| [**SwiftRichString**](https://github.com/malcommac/SwiftRichString) | Elegant & Painless NSAttributedString in Swift |
656+
| [**SwiftLocation**](https://github.com/malcommac/SwiftLocation) | Efficient location manager |
657+
| [**SwiftMsgPack**](https://github.com/malcommac/SwiftMsgPack) | Fast/efficient msgPack encoder/decoder |
658+
</p>
659+
622660

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// DynamicText.swift
3+
// SwiftRichString-iOS
4+
//
5+
// Created by Felipe Lefèvre Marino on 1/17/19.
6+
// Copyright © 2019 SwiftRichString. All rights reserved.
7+
//
8+
9+
#if os(tvOS) || os(watchOS) || os(iOS)
10+
import UIKit
11+
12+
/// DynamicText encapsulate the attributes for fonts to automatically scale to match the current Dynamic Type settings. It uses UIFontMetrics.
13+
public class DynamicText {
14+
15+
/// Set the dynamic size text style.
16+
/// You can pass any `UIFont.TextStyle` value, if nil UIFontMetrics.default will be used,
17+
/// which uses the body text style.
18+
public var style: UIFont.TextStyle?
19+
20+
#if os(OSX) || os(iOS) || os(tvOS)
21+
/// The trait collection to use when determining compatibility. The returned
22+
/// font is appropriate for use in an interface that adopts the specified traits.
23+
public var traitCollection: UITraitCollection?
24+
#endif
25+
26+
/// Set the maximum size
27+
/// allowed for the font/text. Use this value to constrain the font to
28+
/// the specified size when your interface cannot accommodate text that is any larger.
29+
public var maximumSize: CGFloat?
30+
31+
public typealias InitHandler = ((DynamicText) -> (Void))
32+
33+
//MARK: - INIT
34+
35+
/// Initialize a new dynamic text with optional configuration handler callback.
36+
///
37+
/// - Parameter handler: configuration handler callback.
38+
public init(_ handler: InitHandler? = nil) {
39+
handler?(self)
40+
}
41+
}
42+
43+
#endif

Sources/SwiftRichString/Attributes/FontData.swift

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,21 @@ internal let WATCHOS_SYSTEMFONT_SIZE: CGFloat = 12.0
2121
/// User don't interact with this object directly but via `Style`'s properties.
2222
/// Using the `attributes` property this object return a valid instance of the attributes to describe
2323
/// required behaviour.
24-
public class FontData {
24+
public struct FontData {
2525

2626
private static var DefaultFont = Font.systemFont(ofSize: 12.0)
2727

2828
/// Font object
2929
var font: FontConvertible? { didSet { self.style?.invalidateCache() } }
30-
30+
31+
#if os(tvOS) || os(watchOS) || os(iOS)
32+
// Dynamic text atributes
33+
public var dynamicText: DynamicText? { didSet { self.style?.invalidateCache() } }
34+
35+
/// Returns if font should adapt to dynamic type
36+
private var adpatsToDynamicType: Bool? { return dynamicText != nil }
37+
#endif
38+
3139
/// Size of the font
3240
var size: CGFloat? { didSet { self.style?.invalidateCache() } }
3341

@@ -101,7 +109,7 @@ public class FontData {
101109
/// - Parameter size: ignored. It will be overriden by `fontSize` property.
102110
/// - Returns: instance of the font
103111
var attributes: [NSAttributedString.Key:Any] {
104-
guard !self.explicitFont else {
112+
guard self.explicitFont == true else {
105113
return [:]
106114
}
107115
return attributes(currentFont: self.font, size: self.size)
@@ -121,7 +129,7 @@ public class FontData {
121129
guard self.explicitFont else {
122130
return
123131
}
124-
132+
125133
/// Enumerate fonts in string and attach the attributes
126134
let scanRange = (range ?? NSMakeRange(0, source.length))
127135
source.enumerateAttribute(.font, in: scanRange, options: []) { (fontValue, fontRange, shouldStop) in
@@ -183,9 +191,39 @@ public class FontData {
183191
finalAttributes[.kern] = tracking.kerning(for: finalFont)
184192
}
185193
#endif
186-
187-
finalAttributes[.font] = finalFont // assign composed font
194+
195+
#if os(tvOS) || os(watchOS) || os(iOS)
196+
// set scalable custom font if adapts to dynamic type
197+
if #available(iOS 11.0, watchOS 4.0, tvOS 11.0, *), adpatsToDynamicType == true {
198+
finalAttributes[.font] = scalableFont(from: finalFont)
199+
} else {
200+
finalAttributes[.font] = finalFont
201+
}
202+
#else
203+
finalAttributes[.font] = finalFont
204+
#endif
205+
188206
return finalAttributes
189207
}
208+
209+
#if os(tvOS) || os(watchOS) || os(iOS)
210+
/// Returns a custom scalable font based on the received font
211+
///
212+
/// - Parameter font: font in which the custom font will be based
213+
/// - Returns: dynamic scalable font
214+
@available(iOS 11.0, tvOS 11.0, iOSApplicationExtension 11.0, watchOS 4, *)
215+
private func scalableFont(from font: Font) -> Font {
216+
var fontMetrics: UIFontMetrics?
217+
if let textStyle = dynamicText?.style {
218+
fontMetrics = UIFontMetrics(forTextStyle: textStyle)
219+
}
220+
221+
#if os(OSX) || os(iOS) || os(tvOS)
222+
return (fontMetrics ?? UIFontMetrics.default).scaledFont(for: font, maximumPointSize: dynamicText?.maximumSize ?? 0.0, compatibleWith: dynamicText?.traitCollection)
223+
#else
224+
return (fontMetrics ?? UIFontMetrics.default).scaledFont(for: font, maximumPointSize: dynamicText?.maximumSize ?? 0.0)
225+
#endif
226+
}
227+
#endif
190228

191229
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// URLConvertible.swift
3+
// SwiftRichString
4+
//
5+
// Created by dan on 19/01/2019.
6+
// Copyright © 2019 SwiftRichString. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// Dynamic tag support protocol
12+
public protocol DynamicTagComposable {
13+
func dynamicValueFromTag(_ tag: StyleGroup.TagAttribute?) -> Any?
14+
}
15+
16+
/// Dynamic value for NSAttributedKey.Link and .linkURL attribute.
17+
/// If link is wrong and cannot be transformed to `URL` instance style's `link` is not applied.
18+
///
19+
/// - url: fixed url value specified by URL instance.
20+
/// - string: fixed url string specified by a simple string.
21+
/// - tagAttribute: value of a specified tag in source html-tagged style.
22+
public enum URLRepresentable: DynamicTagComposable {
23+
case url(URL)
24+
case string(String)
25+
case tagAttribute(String)
26+
27+
public func dynamicValueFromTag(_ tag: StyleGroup.TagAttribute?) -> Any? {
28+
switch self {
29+
case .url(let url):
30+
return url
31+
case .string(let string):
32+
return URL(string: string)
33+
case .tagAttribute(let tagKey):
34+
guard let value = tag?.parameters?[tagKey] else {
35+
return nil
36+
}
37+
return URL(string: value)
38+
}
39+
}
40+
41+
}

Sources/SwiftRichString/Style/Style.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ public class Style: StyleProtocol {
8787
}
8888
}
8989

90+
#if os(tvOS) || os(watchOS) || os(iOS)
91+
/// Set the dynamic text attributes to adapt the font/text to the current Dynamic Type settings.
92+
/// **Note**: in order to be used you must also set the `.font`/`.size` attribute of the style.
93+
@available(iOS 11.0, tvOS 11.0, iOSApplicationExtension 11.0, watchOS 4, *)
94+
public var dynamicText: DynamicText? {
95+
set { self.fontData?.dynamicText = newValue }
96+
get { return self.fontData?.dynamicText }
97+
}
98+
#endif
99+
90100
/// Set the text color of the style.
91101
/// You can pass any `ColorConvertible` conform object, it will be transformed to a valid `UIColor`/`NSColor`
92102
/// automatically. Both `UIColor`/`NSColor` and `String` are conform to this protocol.
@@ -424,7 +434,7 @@ public class Style: StyleProtocol {
424434

425435
/// The value of this attribute is an NSURL object (preferred) or an NSString object.
426436
/// The default value of this property is nil, indicating no link.
427-
public var linkURL: URL? {
437+
public var linkURL: URLRepresentable? {
428438
set { self.set(attribute: newValue, forKey: .link) }
429439
get { return self.get(attributeForKey: .link) }
430440
}
@@ -595,7 +605,9 @@ public class Style: StyleProtocol {
595605
return (self.innerAttributes[key] as? T)
596606
}
597607

598-
/// Return all attributes defined by the style.
608+
/// Return attributes defined by the style.
609+
/// Not all attributes are returned, fonts attributes may be omitted.
610+
/// Refer to `attributes` to get the complete list.
599611
public var attributes: [NSAttributedString.Key : Any] {
600612
if let cachedAttributes = self.cachedAttributes {
601613
return cachedAttributes

0 commit comments

Comments
 (0)