Skip to content

Commit 64682e8

Browse files
committed
#101 Added support for in-memory image provider in StyleXML
1 parent 408d9df commit 64682e8

File tree

4 files changed

+75
-36
lines changed

4 files changed

+75
-36
lines changed

Sources/SwiftRichString/Extensions/AttributedString+Attachments.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,15 @@ public extension AttributedString {
7676
}
7777

7878
let image = Image(named: imageNamed)
79-
let boundsRect = CGRect(string: bounds)
80-
self.init(image: image, bounds: boundsRect)
79+
self.init(image: image, bounds: bounds)
8180
}
8281

8382
/// Initialize a new attributed string from an image.
8483
///
8584
/// - Parameters:
8685
/// - image: image to use.
8786
/// - bounds: location and size of the image, if `nil` the default bounds is applied.
88-
convenience init?(image: Image?, bounds: CGRect? = nil) {
87+
convenience init?(image: Image?, bounds: String? = nil) {
8988
guard let image = image else {
9089
return nil
9190
}
@@ -110,8 +109,8 @@ public extension AttributedString {
110109
}
111110
#endif
112111

113-
if let bounds = bounds {
114-
attachment.bounds = bounds
112+
if let boundsRect = CGRect(string: bounds) {
113+
attachment.bounds = boundsRect
115114
}
116115

117116
self.init(attachment: attachment)

Sources/SwiftRichString/Style/StyleGroup.swift

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ import AppKit
3535
import UIKit
3636
#endif
3737

38-
public class StyleGroup: StyleProtocol {
38+
public typealias StyleGroup = StyleXML
39+
40+
public class StyleXML: StyleProtocol {
3941

40-
// The following attributes are ignored for StyleGroup because are read from the sub styles.
42+
// The following attributes are ignored for StyleXML because are read from the sub styles.
4143
public var attributes: [NSAttributedString.Key : Any] = [:]
4244
public var fontData: FontData? = nil
4345
public var textTransforms: [TextTransform]? = nil
@@ -49,23 +51,30 @@ public class StyleGroup: StyleProtocol {
4951
/// to the existing source.
5052
public var baseStyle: StyleProtocol?
5153

52-
/// Parsing options.
54+
/// XML Parsing options.
5355
public var xmlParsingOptions: XMLParsingOptions = []
5456

57+
/// Mapped image for memory based images.
58+
public private(set) var imagesMap: [String: UIImage]? = nil
59+
5560
/// Dynamic attributes resolver.
5661
/// By default the `StandardXMLAttributesResolver` instance is used.
5762
public var xmlAttributesResolver: XMLDynamicAttributesResolver = StandardXMLAttributesResolver()
5863

5964
// MARK: - Initialization
6065

61-
/// Initialize a new `StyleGroup` with a dictionary of style and names.
66+
/// Initialize a new `StyleXML` with a dictionary of style and names.
6267
/// Note: Ordered is not guarantee, use `init(_ styles:[(String, StyleProtocol)]` if you
6368
/// need to keep the order of the styles.
6469
///
65-
/// - Parameter styles: styles dictionary
66-
public init(base: StyleProtocol? = nil, _ styles: [String: StyleProtocol]) {
70+
/// - Parameters:
71+
/// - base: base style applied to the entire string.
72+
/// - styles: styles dictionary used to map your xml tags to styles definitions.
73+
/// - images: images used in XML. If a named image is not assigned here the default assets is used (aka `UIImage(named:)`).
74+
public init(base: StyleProtocol? = nil, _ styles: [String: StyleProtocol] = [:], images: [String: UIImage]? = nil) {
6775
self.styles = styles
6876
self.baseStyle = base
77+
self.imagesMap = images
6978
}
7079

7180
// MARK: - Public Methods
@@ -133,9 +142,7 @@ public class StyleGroup: StyleProtocol {
133142
/// - Returns: modified attributed string, same instance of the `source`.
134143
public func apply(to attrStr: AttributedString, adding: Bool, range: NSRange?) -> AttributedString {
135144
do {
136-
let xmlParser = XMLStringBuilder(string: attrStr.string, options: xmlParsingOptions,
137-
baseStyle: baseStyle, styles: styles,
138-
xmlAttributesResolver: xmlAttributesResolver)
145+
let xmlParser = XMLStringBuilder(styleXML: self, string: attrStr.string)
139146
return try xmlParser.parse()
140147
} catch {
141148
debugPrint("Failed to generate attributed string from xml: \(error)")

Sources/SwiftRichString/Support/XMLDynamicAttributesResolver.swift

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ import UIKit
3838
// MARK: - XMLDynamicAttributesResolver
3939

4040
public protocol XMLDynamicAttributesResolver {
41+
42+
/// When an `img` tag is found this function is called to return requested image.
43+
/// Default implementation of this method receive the `name` attribute of the `img` tag along with any mapping
44+
/// provided by calling `StyleXML`, if `mapping` does not contains requested image for given name
45+
/// the `UIImage(named:)` is called and image is searching inside any bundled `xcasset` file.
46+
///
47+
/// - Parameters:
48+
/// - name: name of the image to get.
49+
/// - fromStyle: caller instance of `StyleXML.
50+
func imageWithName(_ name: String, fromStyle style: StyleXML) -> UIImage?
4151

4252
/// You are receiving this event when SwiftRichString correctly render an existing tag but the tag
4353
/// contains extra attributes you may want to handle.
@@ -47,7 +57,8 @@ public protocol XMLDynamicAttributesResolver {
4757
/// - Parameters:
4858
/// - attributedString: attributed string. You will receive it after the style is applied.
4959
/// - xmlStyle: xml style information with tag, applied style and the dictionary with extra attributes.
50-
func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle)
60+
/// - fromStyle: caller instance of `StyleXML.
61+
func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle, fromStyle: StyleXML)
5162

5263
/// You will receive this event when SwiftRichString can't found a received style name into provided group tags.
5364
/// You can decide to handle it. The default receiver for example uses the `a` tag to render passed url if `href`
@@ -57,15 +68,29 @@ public protocol XMLDynamicAttributesResolver {
5768
/// - tag: tag name received.
5869
/// - attributedString: attributed string received.
5970
/// - attributes: attributes of the tag received.
60-
func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?)
71+
/// - fromStyle: caller instance of `StyleXML.
72+
func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?, fromStyle: StyleXML)
6173

6274
}
6375

76+
extension XMLDynamicAttributesResolver {
77+
78+
public func imageWithName(_ name: String, fromStyle style: StyleXML) -> UIImage? {
79+
guard let mappedImage = style.imagesMap?[name] else {
80+
return UIImage(named: name) // xcassets fallback
81+
}
82+
83+
// origin xml style contains mapped image.
84+
return mappedImage
85+
}
86+
87+
}
88+
6489
// MARK: - StandardXMLAttributesResolver
6590

6691
open class StandardXMLAttributesResolver: XMLDynamicAttributesResolver {
6792

68-
public func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle) {
93+
public func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle, fromStyle: StyleXML) {
6994
let finalStyleToApply = Style()
7095
xmlStyle.enumerateAttributes { key, value in
7196
switch key {
@@ -80,7 +105,7 @@ open class StandardXMLAttributesResolver: XMLDynamicAttributesResolver {
80105
attributedString.add(style: finalStyleToApply)
81106
}
82107

83-
public func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?) {
108+
public func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?, fromStyle: StyleXML) {
84109
let finalStyleToApply = Style()
85110
switch tag {
86111
case "a": // href support
@@ -99,8 +124,9 @@ open class StandardXMLAttributesResolver: XMLDynamicAttributesResolver {
99124
#if os(iOS) || os(OSX)
100125
// Local Image support
101126
if let imageName = attributes?["named"] {
102-
if let image = AttributedString(imageNamed: imageName, bounds: attributes?["rect"]) {
103-
attributedString.append(image)
127+
if let image = imageWithName(imageName, fromStyle: fromStyle),
128+
let imageString = AttributedString(image: image, bounds: attributes?["rect"]) {
129+
attributedString.append(imageString)
104130
}
105131
}
106132
#endif

Sources/SwiftRichString/Support/XMLStringBuilder.swift

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,47 +65,54 @@ public class XMLStringBuilder: NSObject, XMLParserDelegate {
6565
private var xmlParser: XMLParser
6666

6767
/// Parsing options.
68-
private var options: XMLParsingOptions
68+
private var options: XMLParsingOptions {
69+
return styleXML.xmlParsingOptions
70+
}
6971

7072
/// Produced final attributed string
7173
private var attributedString: AttributedString
7274

7375
/// Base style to apply as common style of the entire string.
74-
private var baseStyle: StyleProtocol?
76+
private var baseStyle: StyleProtocol? {
77+
return styleXML.baseStyle
78+
}
7579

7680
/// Styles to apply.
77-
private var styles: [String: StyleProtocol]
81+
private var styles: [String: StyleProtocol] {
82+
return styleXML.styles
83+
}
7884

7985
/// Styles applied at each fragment.
8086
private var xmlStylers = [XMLDynamicStyle]()
8187

8288
/// XML Attributes resolver
83-
public var xmlAttributesResolver: XMLDynamicAttributesResolver
89+
public var xmlAttributesResolver: XMLDynamicAttributesResolver {
90+
return styleXML.xmlAttributesResolver
91+
}
8492

8593
// The XML parser sometimes splits strings, which can break localization-sensitive
8694
// string transforms. Work around this by using the currentString variable to
8795
// accumulate partial strings, and then reading them back out as a single string
8896
// when the current element ends, or when a new one is started.
8997
var currentString: String?
9098

99+
/// StyleXML instance.
100+
private weak var styleXML: StyleXML!
101+
91102
// MARK: - Initialization
92103

93-
public init(string: String, options: XMLParsingOptions,
94-
baseStyle: StyleProtocol?, styles: [String: StyleProtocol],
95-
xmlAttributesResolver: XMLDynamicAttributesResolver) {
96-
let xml = (options.contains(.doNotWrapXML) ? string : "<\(XMLStringBuilder.topTag)>\(string)</\(XMLStringBuilder.topTag)>")
104+
public init(styleXML: StyleXML, string: String) {
105+
self.styleXML = styleXML
106+
107+
let xml = (styleXML.xmlParsingOptions.contains(.doNotWrapXML) ? string : "<\(XMLStringBuilder.topTag)>\(string)</\(XMLStringBuilder.topTag)>")
97108
guard let data = xml.data(using: String.Encoding.utf8) else {
98109
fatalError("Unable to convert to UTF8")
99110
}
100111

101-
self.options = options
102112
self.attributedString = NSMutableAttributedString()
103-
self.xmlAttributesResolver = xmlAttributesResolver
104113
self.xmlParser = XMLParser(data: data)
105-
self.baseStyle = baseStyle
106-
self.styles = styles
107114

108-
if let baseStyle = baseStyle {
115+
if let baseStyle = styleXML.baseStyle {
109116
self.xmlStylers.append( XMLDynamicStyle(tag: XMLStringBuilder.topTag, style: baseStyle) )
110117
}
111118

@@ -180,11 +187,11 @@ public class XMLStringBuilder: NSObject, XMLParserDelegate {
180187
newAttributedString = newAttributedString.add(style: style)
181188
// Also apply the xml attributes if needed
182189
if xmlStyle.xmlAttributes != nil {
183-
xmlAttributesResolver.applyDynamicAttributes(to: &newAttributedString, xmlStyle: xmlStyle)
190+
xmlAttributesResolver.applyDynamicAttributes(to: &newAttributedString, xmlStyle: xmlStyle, fromStyle: styleXML)
184191
}
185192
} else {
186193
// it's an unknown tag, we can call the resolver for unknown tags
187-
xmlAttributesResolver.styleForUnknownXMLTag(xmlStyle.tag, to: &newAttributedString, attributes: xmlStyle.xmlAttributes)
194+
xmlAttributesResolver.styleForUnknownXMLTag(xmlStyle.tag, to: &newAttributedString, attributes: xmlStyle.xmlAttributes, fromStyle: styleXML)
188195
}
189196

190197
}
@@ -203,7 +210,7 @@ public class XMLDynamicStyle {
203210
/// Tag read for this style.
204211
public let tag: String
205212

206-
/// Style found in receiver `StyleGroup` instance.
213+
/// Style found in receiver `StyleXML` instance.
207214
public let style: StyleProtocol?
208215

209216
/// Attributes found into the xml tag.

0 commit comments

Comments
 (0)