BonMot is a Swift attributed string library

Overview

BonMot Logo

Swift 4.2 + 5.0 CircleCI Version License Platform Carthage compatible codecov

BonMot (pronounced Bon Mo, French for good word) is a Swift attributed string library. It abstracts away the complexities of the iOS, macOS, tvOS, and watchOS typography tools, freeing you to focus on making your text beautiful.

To run the example project, run pod try BonMot, or clone the repo, open BonMot.xcodeproj, and run the Example-iOS target.

Note

If you are migrating a project from BonMot 3 to BonMot 4, please see the Migration Guide.

Usage

In any Swift file where you want to use BonMot, simply import BonMot.

Basics

Use a StringStyle to specify the style of your attributed string. Then, use the styled(with:) method on String to get your attributed string:

let quote = """
    I used to love correcting people’s grammar until \
    I realized what I loved more was having friends.
    -Mara Wilson
    """

let style = StringStyle(
    .font(UIFont(name: "AmericanTypewriter", size: 17)!),
    .lineHeightMultiple(1.8)
)

let attributedString = quote.styled(with: style)

// You can also get the style’s attributes dictionary
// if you’re using an API that requires it.
let attributes = style.attributes

Glossary

These are the types with which you will most commonly interact when using BonMot to build attributed strings.

  • StringStyle: a collection of attributes which can be used to style a string. These include basics, like font and color, and more advanced settings like paragraph controls and OpenType features. To get a good idea of the full set of features that BonMot supports, look at the interface for this struct.
  • StringStyle.Part: an enum which can be used to concisely construct a StringStyle. You will typically interact with these, rather than constructing StringStyles attribute by attribute.
  • Composable: a protocol defining any type that knows how to append itself to an attributed string. BonMot provides functions, such as the one in this example, to join together multiple Composable values.
  • NamedStyles: use this to register custom, reusable styles in a global namespace.
  • Special: a utility to include special, ambiguous, and non-printing characters in your strings without making your code unreadable.

Style Inheritance

Styles can inherit from each other, which lets you create multiple styles that share common attributes:

let baseStyle = StringStyle(
    .lineHeightMultiple(1.2),
    .font(UIFont.systemFont(ofSize: 17))
)

let redStyle = baseStyle.byAdding(.color(.red))
let blueStyle = baseStyle.byAdding(.color(.blue))

let redBirdString = "bird".styled(with: redStyle)
let blueBirdString = "bird".styled(with: blueStyle)

Styling Parts of Strings with XML

Are you trying to style just part of a string, perhaps even a localized string which is different depending on the locale of the app? No problem! BonMot can turn custom XML tags and simple HTML into attributed strings:

red fish,blue fish" let redStyle = StringStyle(.color(.red)) let blueStyle = StringStyle(.color(.blue)) let fishStyle = StringStyle( .font(UIFont.systemFont(ofSize: 17)), .lineHeightMultiple(1.8), .color(.darkGray), .xmlRules([ .style("red", redStyle), .style("blue", blueStyle), ]) ) let attributedString = string.styled(with: fishStyle) ">
// This would typically be a localized string
let string = "one fish, two fish, red fish,blue fish"

let redStyle = StringStyle(.color(.red))
let blueStyle = StringStyle(.color(.blue))

let fishStyle = StringStyle(
    .font(UIFont.systemFont(ofSize: 17)),
    .lineHeightMultiple(1.8),
    .color(.darkGray),
    .xmlRules([
        .style("red", redStyle),
        .style("blue", blueStyle),
        ])
)

let attributedString = string.styled(with: fishStyle)

This will produce:

Note the use of to specify a special character within the string. This is a great way to add special characters to localized strings, since localizers might not know to look for special characters, and many of them are invisible or ambiguous when viewed in a normal text editor. You can use any characters in the Special enum, or use or &#a1338;

XML Parsing with Error Handling

If the above method encounters invalid XML, the resulting string will be the entire original string, tags and all. If you are parsing XML that is out of your control, e.g. variable content from a server, you may want to use this alternate parsing mechanism, which allows you to handle errors encountered while parsing:

let rules: [XMLStyleRule] = [
    .style("strong", strongStyle),
    .style("em", emStyle),
]

let xml = // some XML from a server

do {
    let attrString = try NSAttributedString.composed(ofXML: xml, rules: rules)
}
catch {
    // Handle errors encountered by Foundation's XMLParser,
    // which is used by BonMot to parse XML.
}

Image Attachments

BonMot uses NSTextAttachment to embed images in strings. You can use BonMot’s NSAttributedString.composed(of:) API to chain images and text together in the same string:

let someImage = ... // some UIImage or NSImage

let attributedString = NSAttributedString.composed(of: [
    someImage.styled(with: .baselineOffset(-4)), // shift vertically if needed
    Special.noBreakSpace, // a non-breaking space between image and text
    "label with icon", // raw or attributed string
    ])

Note the use of the Special type, which gives you easy access to Unicode characters that are commonly used in UIs, such as spaces, dashes, and non-printing characters.

Outputs:

If you need to wrap multiple lines of text after an image, use Tab.headIndent(...) to align the whole paragraph after the image:

let attributedString = NSAttributedString.composed(of: [
    someImage.styled(with: .baselineOffset(-4.0)), // shift vertically if needed
    Tab.headIndent(10), // horizontal space between image and text
    "This is some text that goes on and on and spans multiple lines, and it all ends up left-aligned",
    ])

Outputs:

Dynamic Type

You can easily make any attributed string generated by BonMot respond to the system text size control. Simply add .adapt to any style declaration, and specify whether you want the style to scale like a .control or like .body text.

let style = StringStyle(
    .adapt(.control)
    // other style parts can go here as needed
)

someLabel.attributedText = "Label".styled(with: style).adapted(to: traitCollection)

If you want an attributed string to adapt to the current content size category, when setting it on a UI element, use .adapted(to: traitCollection) as in the above example.

Responding to Content Size Category Changes

If you call UIApplication.shared.enableAdaptiveContentSizeMonitor() at some point in your app setup code, BonMot will update common UI elements as the preferred content size category changes. You can opt your custom controls into automatic updating by conforming them to the AdaptableTextContainer protocol.

If you want more manual control over the adaptation process and are targeting iOS 10+, skip enabling the adaptive content size monitor, and call .adapted(to: traitCollection) inside traitCollectionDidChange(_:). iOS 10 introduced a preferredContentSizeCategory property on UITraitCollection.

Scaling Behaviors

The .control and .body behaviors both scale the same, except that when enabling the "Larger Dynamic Type" accessibility setting, .body grows unbounded. Here is a graph of the default behaviors of the system Dynamic Type styles:

Graph of iOS Dynamic Type scaling behavior, showing that Control text tops out at the XXL size, but Body text keeps growing all the way up to AccessibilityXXL

Storyboard and XIB Integration

You can register global named styles, and use them in Storyboards and XIBs via IBInspectable:

let style = StringStyle(
    .font(UIFont(name: "Avenir-Roman", size: 24)!),
    .color(.red),
    .underline(.styleSingle, .red)
)
NamedStyles.shared.registerStyle(forName: "MyHeadline", style: style)

You can then use MyHeadline in Interface Builder’s Attributes Inspector on common UIKit controls such as buttons and labels:

Editing the Bon Mot Style Name attribute in the Attributes Inspector of Interface Builder, and setting the value to MyHeadline

These same named styles will also be picked up if they are used as tag names in parsed XML.

Debugging & Testing Helpers

Use bonMotDebugString and bonMotDebugAttributedString to print out a version of any attributed string with all of the special characters and image attachments expanded into human-readable XML:

MondayFriday ">
NSAttributedString.composed(of: [
	image,
	Special.noBreakSpace,
	"Monday",
	Special.enDash,
	"Friday"
	]).bonMotDebugString

// Result:
// MondayFriday

You can use XML Rules to re-parse the resulting string (except for images) back into an attributed string. You can also save the output of bonMotDebugString and use it to validate attributed strings in unit tests.

Vertical Text Alignment

UIKit lets you align labels by top, bottom, or baseline. BonMot includes TextAlignmentConstraint, a layout constraint subclass that lets you align labels by cap height and x-height. For some fonts, this is essential to convey the designer’s intention:

Illustration of different methods of aligning text vertically

TextAlignmentConstraint works with any views that expose a font property. It uses Key-Value Observing to watch for changes to the font property, and adjust its internal measurements accordingly. This is ideal for use with Dynamic Type: if the user changes the font size of the app, TextAlignmentConstraint will update. You can also use it to align a label with a plain view, as illustrated by the red dotted line views in the example above.

Warning: TextAlignmentConstraint holds strong references to its firstItem and secondItem properties. Make sure that a view that is constrained by this constraint does not also hold a strong reference to said constraint, because it will cause a retain cycle.

You can use TextAlignmentConstraint programmatically or in Interface Builder. In code, use it like this:

TextAlignmentConstraint.with(
    item: someLabel,
    attribute: .capHeight,
    relatedBy: .equal,
    toItem: someOtherLabel,
    attribute: .capHeight).isActive = true

In Interface Builder, start by constraining two views to each other with a top constraint. Select the constraint, and in the Identity Inspector, change the class to TextAlignmentConstraint:

setting the class in the Identity Inspector

Next, switch to the Attributes Inspector. TextAlignmentConstraint exposes two text fields through IBInspectables. Type in the attributes you want to align. You will get a run-time error if you enter an invalid value.

setting the alignment attributes in the Attributes Inspector

The layout won’t change in Interface Builder (IBDesignable is not supported for constraint subclasses), but it will work when you run your code.

Note: some of the possible alignment values are not supported in all configurations. Check out Issue #37 for updates.

Objective-C Compatibility

BonMot is written in Swift, but you have a few options if you must use it in an Objective-C code base:

  • For legacy Objective-C code bases, consider using BonMot 3.2, the last major release to be written in Objective-C. Make sure you reference the ReadMe from that tagged release, since the syntax is different than BonMot 4.0 and later.
  • If you are mixing Objective-C and Swift, you can create named styles as in the Interface Builder section, and then access those styles in Objective-C:
UILabel *label = [[UILabel alloc] init];
label.bonMotStyleName = @"MyHeadline";

BonMot 3 → 4+ Migration Guide

BonMot 4 is a major update, but there are some common patterns that you can use to ease the transition. Note that this is mostly for Swift projects that were using BonMot 3. BonMot 4+ has only limited support for Objective-C, so please check that section before attempting to upgrade if you need to maintain Objective-C compatibility.

Separating Style from Content

BonMot 4 introduces the StringStyle struct, which encapsulates style information. When you apply a StringStyle to a plain String, the result is an NSAttributedString. This differs from BonMot 3, where a BONChain or BONText contained both style and string information. The decoupling of content from style follows in the footsteps of HTML/CSS, and makes it easier to test and reason about each component separately from the other.

Inline Styling

The changes required to support inline styling are minimal. It won’t be a completely mechanical process due to some renaming that took place in 4.0, but it should be fairly straightforward:

BonMot 3
let chain = BONChain()
   .color(myColor)
   .font(myFont)
   .figureSpacing(.Tabular)
   .alignment(.Center)
   .string(text)
label.attributedText = chain.attributedString
BonMot 4
label.attributedText = text.styled(
    with:
    .color(myColor),
    .font(myFont),
    .numberSpacing(.monospaced), // renamed in 4.0
    .alignment(.center)
)

Saved Styles

In BonMot 3, you may have stored BONChains for later use. You can accomplish the same thing with BonMot 4’s StringStyle, with one main difference: while a BONChain can contain a string, a StringStyle never does. It is applied to a string, producing an NSAttributedString:

BonMot 3
struct Constants {

    static let myChain = BONChain()
        .color(myColor)
        .font(myFont)
        .tagStyles([
            "bold": myBoldChain,
            ])

}

// and then, later:

let attrString = myChain.string("some string").attributedString
BonMot 4
struct Constants {

    static let myStyle = StringStyle(
        .color(myColor),
        .font(myFont),
        .xmlRules([
            .style("bold", myBoldStyle),
            ]))

}

// and then, later:

let attrString = "some string".styled(with: Constants.myStyle)

Installation

CocoaPods

BonMot is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'BonMot'

Carthage

BonMot is also compatible with Carthage. To install it, simply add the following line to your Cartfile:

github "Rightpoint/BonMot"

Contributing

Issues and pull requests are welcome! Please ensure that you have the latest SwiftLint installed before committing and that there are no style warnings generated when building.

Contributors are expected to abide by the Contributor Covenant Code of Conduct.

Author

Zev Eisenberg: @ZevEisenberg

Logo by Jon Lopkin: @jonlopkin

License

BonMot is available under the MIT license. See the LICENSE file for more info.

Comments
  • Add categories on UILabel, UITextView, and UITextField to accept a BONTextable object

    Add categories on UILabel, UITextView, and UITextField to accept a BONTextable object

    This allows assigning an id <BONChainable> directly to a UILabel, UITextView, or UITextField in the same way you would assign a font. The BonMot styling assigned to the label/textView/textField will be applied to any future updates to the label/textView/textField's text property.

    This works via swizzling the setText: and setAttributedText: methods of these classes to allow applying the BonMot styling when appropriate.

    Fixes #68

    opened by Imperiopolis 29
  • BonMot disables IBActions dragging from storyboard

    BonMot disables IBActions dragging from storyboard

    When Bonmot is included in my project, all my UIButton 'Touch Up Inside' events in the connections inspector via Utilities show warning signs. I suspect it has something with this stack overflow issue: http://stackoverflow.com/questions/28732903/cant-connect-ibaction-in-xcode.

    screen shot 2016-11-26 at 12 21 13 pm

    As one of the commenters suggested in the link, the culprit seems to be the UIButton extensions in BonMot's framework.

    I commented the several extensions for UIButton out and now it works fine.

    Going to work with clone bonmot into own project in meantime, but would love for this to be addressed!

    opened by leojkwan 19
  • Add ability to add child BONChainables to style text between tags.

    Add ability to add child BONChainables to style text between tags.

    This adds the ability to parse and style substrings between arbitrary tags with assigned BONChainable objects. This potentially resolves #69.

    Excerpt from the readme:

    Tag Styles

    BonMot can style text between arbirtrary tags using a <tag></tag> format and \ as an escape character.

    BONChain *boldChain = BONChain.new.fontNameAndSize(@"Baskerville-Bold", 15.0f);
    BONChain *italicChain = BONChain.new.fontNameAndSize(@"Baskerville-Italic", 15.0f);
    
    BONChain *chain = BONChain.new.fontNameAndSize(@"Baskerville", 17.0f)
        .tagStyles(@[BONTagMake(@"bold", boldChain), BONTagMake(@"italic", italicChain)])
        .string(@"<bold>This text is wrapped in a \\<bold> tag.</bold>\n<italic>This text is wrapped in an \\<italic> tag.</italic>");
    
    NSAttributedString *string = chain.attributedString;
    

    Outputs:

    BonMot can also style text between any arbitrary start and end strings using any escape string.

    BONChain *boldChain = BONChain.new.fontNameAndSize(@"Baskerville-Bold", 15.0f);
    BONChain *italicChain = BONChain.new.fontNameAndSize(@"Baskerville-Italic", 15.0f);
    
    BONChain *chain = BONChain.new.fontNameAndSize(@"Baskerville", 17.0f)
    .tagStyles(@[BONTagComplexMake(@"~start~", @"!end", @"escape", boldChain)])
    .string(@"~start~This text is wrapped in a escape~start~ tag.!end");
    
    NSAttributedString *string = chain.attributedString;
    

    Note: Tag styles do not support nested or interleaved tags. The first tag matched will be applied, any additional tags between the start end end will be ignored.

    opened by Imperiopolis 19
  • Support use in app extensions

    Support use in app extensions

    See this similar request for Anchorage. When I enable the APPLICATION_EXTENSION_API_ONLY flag, I get this:

            if UIApplication.shared.delegate != nil {
                             ^~~~~~
    UIKit.UIApplication:5:20: note: 'shared' has been explicitly marked unavailable here
        open class var shared: UIApplication { get }
                       ^
    BonMot/Sources/UIKit/UIKit+Helpers.swift:62:34: error: 'shared' is unavailable: Use view controller based solutions where appropriate instead.
                return UIApplication.shared.preferredContentSizeCategory
                                     ^~~~~~
    UIKit.UIApplication:5:20: note: 'shared' has been explicitly marked unavailable here
        open class var shared: UIApplication { get }
                       ^
    
    opened by ZevEisenberg 18
  • cocopod install BonMot 3.2 not 5.3

    cocopod install BonMot 3.2 not 5.3

    I'm new to programming, but when I tried installing BonMot CocoPods installed 3.2. I did a pod update and it does not upgrade. I'm running Xcode 9.4, How can i upgrade to the latest version with cocopods

    opened by markatlarge 16
  • New XMLStyleRule variants for dynamic styling

    New XMLStyleRule variants for dynamic styling

    Hello and thank you for this fine library!

    I propose addition of several XMLStyleRules to dynamically generate and apply styles, enter- and exit-insertions of elements.

    With these changes, you can use them like this:

    // We want to style an xml that contains tags with some important attributes
    
    var quoteAuthorStack = [String]()
    
    let attributedString = try NSAttributedString.composed(
        ofXML: rawXml,
        rules: [
            .style("quote", quoteStyle),
    
            // We want to insert an image with a beautiful quote before the actual quote text.
            // Also we have an author's name in "author" attribute and we want 
            // to append this name after the quote text.
            .enterBlock(element: "quote") { attributes in
                // Saving author's name for later use
                quoteAuthorStack.append(attributes["author"]!)
    
                // "author" handling was hacky, so to show `attributes` in action,
                // why don't we have different quote images for different occasions...
                return NSAttributedString.composed(of: [ 
                    UIImage(named: attributes["quotestyle"]!)!, 
                    Special.lineFeed 
                ])
            },
            
            .exitBlock(element: "quote") {
                // Using our saved "author" attribute here to append the name after the quote
                let author = quoteAuthorStack.popLast()!
    
                return NSAttributedString.composed(of: [ 
                    Special.lineFeed, author.styled(with: authorStyle) 
                ])
            },
    
            // Also we have a "link" tag with some attributes that will help us
            // to generate an URL
            .styleBlock("link") { attributes in
                guard let type = attributes["type"], [ "event", "person" ].contains(type),
                    let id = attributes["id"],
                    let link = URL(string: "\(Const.Domain)/\(type)/\(id)") else { return defaultStyle }
    
                return defaultStyle.byAdding(.link(link))
            }
        ]
    )
    

    We have a BonMot patched with these changes for quite some time, they are very convenient for us :)

    opened by kosyak 11
  • Styling Embedded <a> tags whilst making them clickable

    Styling Embedded tags whilst making them clickable

    Hey - I wonder if someone can help clarify if this is possible...

    I am using HTML tags to style my text with a custom XMLStyler and this is working fine for something similar to: <strong>Some text</strong><a href="https://www.website.com>link</a>" however the html I am styling is coming through with links embedded within tags such as <strong>Some text <a href="https://www.website.com>link</a></strong>" I am able to add styles to the embedded link, however the link is not tappable.

    My styler looks like :

    class HTMLStyler: XMLStyler {
    var body = StringStyle(
                .font(UIFont(titillium: weight, size: 14)),
                .color(.red),
                .lineSpacing(-3),
                .paragraphSpacingAfter(14)
            )
    func style(forElement name: String, attributes: [String: String], currentStyle: StringStyle) -> StringStyle? {
     switch name {
     case "strong":
                    let bolder = body.byAdding(
                        .xmlRules([
                            .style("em", body.byAdding(.font(UIFont(boldFont: .boldItalic, size: 14))))
                        ])
                    )
                    return bolder
    case "a":
                    guard let href = attributes["href"],
                        let url = URL(string: href) else {
                            return body
                    }
                    
                    return body.byAdding(.link(url))
                default:
                    return body
                }
     }
    

    The problem I have is that the strong tag is picked up for the styling and returns the style before the 'a' tag, which means the .link(url) is not attached

    Is there a way to get around this? maybe triggering a new styler somehow in the xml rules for the parent tag?

         case "strong":
                    let bolder = body.byAdding(
                        .xmlRules([
                            .style("em", body.byAdding(.font(UIFont(boldFont: .boldItalic, size: 14)))),
    //awesome method here to pass link attributes to new styler and return styled actionable link here?
    
                        ])
                    )
                    return bolder
    

    I have been stuck on this for a while and the only way I can think to get around it if it doesn't work is to manually parse the string and close off the HTML tag prior to styling - which I would really like to avoid

    Thanks so much - great framework that does everything else that I need

    opened by russnettle 10
  • Compile error. Command failed due to signal: Abort trap: 6.

    Compile error. Command failed due to signal: Abort trap: 6.

    Hello, I use you library in my project and after updating Xcode I have next error: String+AttributedString.swift.zip

    Showing Recent Messages CompileSwift normal x86_64 /Users/user/Documents/work/4spar/FourSpar/Sources/Extensions/String+AttributedString.swift cd /Users/user/Documents/work/4spar/FourSpar /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c -filelist /var/folders/tg/p8d1jmc9691501v8v1qyq11h0000gn/T/sources-5c964b -primary-file /Users/user/Documents/work/4spar/FourSpar/Sources/Extensions/String+AttributedString.swift -target x86_64-apple-ios10.0 -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.0.sdk -I /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator -F /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator -F /Users/user/Documents/work/4spar/FourSpar/../Carthage/Build/iOS -F /Users/user/Documents/work/4spar/FourSpar/Fabric -enable-testing -g -module-cache-path /Users/user/Library/Developer/Xcode/DerivedData/ModuleCache -warnings-as-errors -swift-version 3 -enforce-exclusivity=checked -D DEBUG -serialize-debugging-options -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-generated-files.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-own-target-headers.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-all-non-framework-target-headers.hmap -Xcc -ivfsoverlay -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/all-product-headers.yaml -Xcc -iquote -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-project-headers.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator/include -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/DerivedSources/x86_64 -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/DerivedSources -Xcc -DDEBUG=1 -Xcc -working-directory/Users/user/Documents/work/4spar/FourSpar -emit-module-doc-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString~partial.swiftdoc -serialize-diagnostics-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.dia -Onone -module-name FourSpar -emit-module-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString~partial.swiftmodule -emit-dependencies-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.d -emit-reference-dependencies-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.swiftdeps -emit-remap-file-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Migration/FourSpar-swift.migrate/String+AttributedString.remap -o /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.o

    :0: error: fatal error encountered while reading from module 'BonMot'; please file a bug report with your project and the crash log :0: note: compiling as Swift 3.2, with 'BonMot' built as Swift 4.0 (this is supported but may expose additional compiler issues)

    *** DESERIALIZATION FAILURE (please include this section in any bug report) *** declaration is not a nominal type Cross-reference to module 'Foundation' ... NSAttributedStringKey

    0 swift 0x000000010dd88dba PrintStackTraceSignalHandler(void*) + 42 1 swift 0x000000010dd881f6 SignalHandler(int) + 662 2 libsystem_platform.dylib 0x00007fff7f026f5a _sigtramp + 26 3 libsystem_platform.dylib 0x00007ffee556d5a8 _sigtramp + 1716807272 4 libsystem_c.dylib 0x00007fff7ee5232a abort + 127 5 swift 0x000000010b484c51 swift::ModuleFile::fatal(llvm::Error) + 1569 6 swift 0x000000010b48f4e8 swift::ModuleFile::getDeclChecked(llvm::PointerEmbeddedInt<unsigned int, 31>, llvm::Optionalswift::DeclContext*) + 41944 7 swift 0x000000010b491b87 swift::ModuleFile::getTypeChecked(llvm::PointerEmbeddedInt<unsigned int, 31>) + 551 8 swift 0x000000010b4920c5 swift::ModuleFile::getTypeChecked(llvm::PointerEmbeddedInt<unsigned int, 31>) + 1893 9 swift 0x000000010b492a93 swift::ModuleFile::getTypeChecked(llvm::PointerEmbeddedInt<unsigned int, 31>) + 4403 10 swift 0x000000010b492abb swift::ModuleFile::getTypeChecked(llvm::PointerEmbeddedInt<unsigned int, 31>) + 4443 11 swift 0x000000010b48b891 swift::ModuleFile::getDeclChecked(llvm::PointerEmbeddedInt<unsigned int, 31>, llvm::Optionalswift::DeclContext*) + 26497 12 swift 0x000000010b49dca1 swift::ModuleFile::loadAllMembers(swift::Decl*, unsigned long long) + 657 13 swift 0x000000010b8f7536 swift::IterableDeclContext::loadAllMembers() const + 134 14 swift 0x000000010b95143e swift::NominalTypeDecl::lookupDirect(swift::DeclName, bool) + 46 15 swift 0x000000010b94ff2e swift::DeclContext::lookupQualified(swift::Type, swift::DeclName, swift::NLOptions, swift::LazyResolver*, llvm::SmallVectorImplswift::ValueDecl*&) const + 3518 16 swift 0x000000010b7e4372 swift::TypeChecker::lookupMember(swift::DeclContext*, swift::Type, swift::DeclName, swift::OptionSet<swift::NameLookupFlags, unsigned int>)::$_1::operator()() const + 226 17 swift 0x000000010b7e4239 swift::TypeChecker::lookupMember(swift::DeclContext*, swift::Type, swift::DeclName, swift::OptionSet<swift::NameLookupFlags, unsigned int>) + 281 18 swift 0x000000010b731239 swift::constraints::ConstraintSystem::lookupMember(swift::Type, swift::DeclName) + 473 19 swift 0x000000010b6fde24 swift::constraints::ConstraintSystem::performMemberLookup(swift::constraints::ConstraintKind, swift::DeclName, swift::Type, swift::FunctionRefKind, swift::constraints::ConstraintLocator*, bool) + 2772 20 swift 0x000000010b7001f9 swift::constraints::ConstraintSystem::simplifyMemberConstraint(swift::constraints::ConstraintKind, swift::Type, swift::DeclName, swift::Type, swift::DeclContext*, swift::FunctionRefKind, swift::OptionSet<swift::constraints::ConstraintSystem::TypeMatchFlags, unsigned int>, swift::constraints::ConstraintLocatorBuilder) + 345 21 swift 0x000000010b701b8c swift::constraints::ConstraintSystem::simplifyConstraint(swift::constraints::Constraint const&) + 1164 22 swift 0x000000010b704429 swift::constraints::ConstraintSystem::simplify(bool) + 105 23 swift 0x000000010b705525 swift::constraints::ConstraintSystem::solveRec(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 53 24 swift 0x000000010b70c79f swift::constraints::ConstraintSystem::solveSimplified(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 16703 25 swift 0x000000010b70572e swift::constraints::ConstraintSystem::solveRec(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 574 26 swift 0x000000010b70dcd4 swift::constraints::ConstraintSystem::solveSimplified(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 22132 27 swift 0x000000010b70572e swift::constraints::ConstraintSystem::solveRec(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 574 28 swift 0x000000010b705122 swift::constraints::ConstraintSystem::solve(llvm::SmallVectorImplswift::constraints::Solution&, swift::FreeTypeVariableBinding) + 354 29 swift 0x000000010b79e127 swift::TypeChecker::solveForExpression(swift::Expr*&, swift::DeclContext*, swift::Type, swift::FreeTypeVariableBinding, swift::ExprTypeCheckListener*, swift::constraints::ConstraintSystem&, llvm::SmallVectorImplswift::constraints::Solution&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 8247 30 swift 0x000000010b79e60d swift::TypeChecker::typeCheckExpression(swift::Expr*&, swift::DeclContext*, swift::TypeLoc, swift::ContextualTypePurpose, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>, swift::ExprTypeCheckListener*, swift::constraints::ConstraintSystem*) + 733 31 swift 0x000000010b822704 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 7764 32 swift 0x000000010b823dd8 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 13608 33 swift 0x000000010b81fd62 swift::TypeChecker::typeCheckAbstractFunctionBodyUntil(swift::AbstractFunctionDecl*, swift::SourceLoc) + 1090 34 swift 0x000000010b82571b swift::TypeChecker::typeCheckAbstractFunctionBody(swift::AbstractFunctionDecl*) + 475 35 swift 0x000000010b84331a swift::performTypeChecking(swift::SourceFile&, swift::TopLevelContext&, swift::OptionSet<swift::TypeCheckingFlags, unsigned int>, unsigned int, unsigned int, unsigned int, unsigned int) + 2506 36 swift 0x000000010b373cc7 swift::CompilerInstance::performSema() + 5031 37 swift 0x000000010a6f8552 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 1378 38 swift 0x000000010a6f6784 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 7716 39 swift 0x000000010a6ab6a8 main + 12248 40 libdyld.dylib 0x00007fff7eda6145 start + 1 41 libdyld.dylib 0x000000000000004c start + 2166726408 Stack dump: 0. Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c -filelist /var/folders/tg/p8d1jmc9691501v8v1qyq11h0000gn/T/sources-5c964b -primary-file /Users/user/Documents/work/4spar/FourSpar/Sources/Extensions/String+AttributedString.swift -target x86_64-apple-ios10.0 -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.0.sdk -I /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator -F /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator -F /Users/user/Documents/work/4spar/FourSpar/../Carthage/Build/iOS -F /Users/user/Documents/work/4spar/FourSpar/Fabric -enable-testing -g -module-cache-path /Users/user/Library/Developer/Xcode/DerivedData/ModuleCache -warnings-as-errors -swift-version 3 -enforce-exclusivity=checked -D DEBUG -serialize-debugging-options -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-generated-files.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-own-target-headers.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-all-non-framework-target-headers.hmap -Xcc -ivfsoverlay -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/all-product-headers.yaml -Xcc -iquote -Xcc /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/FourSpar-project-headers.hmap -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Products/Debug-iphonesimulator/include -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/DerivedSources/x86_64 -Xcc -I/Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/DerivedSources -Xcc -DDEBUG=1 -Xcc -working-directory/Users/user/Documents/work/4spar/FourSpar -emit-module-doc-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString~partial.swiftdoc -serialize-diagnostics-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.dia -Onone -module-name FourSpar -emit-module-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString~partial.swiftmodule -emit-dependencies-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.d -emit-reference-dependencies-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.swiftdeps -emit-remap-file-path /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Migration/FourSpar-swift.migrate/String+AttributedString.remap -o /Users/user/Library/Developer/Xcode/DerivedData/FourSpar-gbgsoqzalebxhxabmcpopxkzvoeh/Build/Intermediates.noindex/SwiftMigration/FourSpar/Intermediates.noindex/FourSpar.build/Debug-iphonesimulator/FourSpar.build/Objects-normal/x86_64/String+AttributedString.o

    1. While type-checking getter for underline at /Users/user/Documents/work/4spar/FourSpar/Sources/Extensions/String+AttributedString.swift:17:9
    2. While type-checking expression at [/Users/user/Documents/work/4spar/FourSpar/Sources/Extensions/String+AttributedString.swift:18:16 - line:19:9] RangeText="self.styled(with: .underline(.styleSingle, nil) )"
    3. While loading members for 'Part' in module 'BonMot'
    4. While deserializing 'extraAttributes' (EnumElementDecl #1084) in 'BonMot'
    5. While deserializing 'StyleAttributes' (TypeAliasDecl #193) in 'BonMot'

    Can you help me, please?

    opened by mdmrachev23 10
  • swift 3.0 support for carthage

    swift 3.0 support for carthage

    I just installed latest with Carthage but I'm not able to use it.

    It gives me the error below.

    *** Skipped building BonMot due to the error:
    Dependency "BonMot" has no shared framework schemes for any of the platforms: iOS
    
    If you believe this to be an error, please file an issue with the maintainers at https://github.com/Raizlabs/BonMot/issues/new
    

    Please provide any relevant information how to use this library. This lib is not generating .framework for following command

    carthage update --platform iOS
    
    opened by rahthakor 9
  • No support for lineBreakMode

    No support for lineBreakMode

    It doesn't appear that BonMot has support for lineBreakMode so that you can set truncation to be ... among other options. Unless I am missing something. I can't find that this option is exposed.

    opened by tettoffensive 9
  • Apply color on image template

    Apply color on image template

    let chain = BONChain().textColor(UIColor.redColor())
    chain.appendLink(BONChain().image(UIImage(named: "image_name")))
    chain.appendLink(BONChain().string("TEXT").text, separator: " ")
    chain.appendLink(BONChain().image(UIImage(named: "image_name")), separator: " ")
    

    The red color only is applied on the lastimage. The first image on the chain remains black.

    P1 
    opened by aleufms 9
  • StringStyle.swift cause crash

    StringStyle.swift cause crash

    hi, our project got this crash log in firebase when we are using pod 'BonMot', '6.0.0'. seems related to the StringStyle.swift. please take a look. thanks a lot.

    Crashed: com.apple.main-thread EXC_BAD_ACCESS KERN_PROTECTION_FAILURE 0x00000001ef601f60 Crashed: com.apple.main-thread 0 ??? 0x1ef601f60 (Missing) 1 HLLDriver-SEA 0xdb6e8 StringStyle.attributes.getter + 122 (StringStyle.swift:122) 2 HLLDriver-SEA 0xe45984 LMDFontStyle.attributes.getter + 230 (Fonts.swift:230) 3 HLLDriver-SEA 0xaee304 closure #1 in static UITabBarItem.homeTabBarItem(image:title:) + 33 (UIView+Home.swift:33) 4 HLLDriver-SEA 0xa48b20 closure #1 in HLLTabBarController.setupTabs() + 31 (UIView+Home.swift:31) 5 HLLDriver-SEA 0xa47318 HLLTabBarController.setupTabs() + 2838604 (:2838604) 6 HLLDriver-SEA 0xa47be8 @objc HLLTabBarController.viewDidLoad() + 34 (HLLTabBarController.swift:34) 7 UIKitCore 0x37c7b4 -[UITabBarController initWithNibName:bundle:] + 196 8 HLLDriver-SEA 0xa48164 @objc HLLTabBarController.init(nibName:bundle:) + 2842264 (:2842264) 9 HLLDriver-SEA 0xadd650 RootFlowController.handleFlowEvent(:) + 93 (RootFlowController.swift:93) 10 HLLDriver-SEA 0xaddce8 @objc RootFlowController.handleFlowEvent(:) + 3455516 (:3455516) 11 HLLDriver-SEA 0xf23084 @objc UIResponder.handleFlowEvent(:) + 1240980 (:1240980) 12 HLLDriver-SEA 0xf23084 @objc UIResponder.handleFlowEvent(:) + 1240980 (:1240980) 13 HLLDriver-SEA 0xf23084 @objc UIResponder.handleFlowEvent(:) + 1240980 (:1240980) 14 HLLDriver-SEA 0xf23084 @objc UIResponder.handleFlowEvent(:) + 1240980 (:1240980) 15 HLLDriver-SEA 0xf23084 @objc UIResponder.handleFlowEvent(_:) + 1240980 (:1240980) 16 HLLDriver-SEA 0x9ef3a4 __35-[SplashScreenVC goLoginOrMainPage]_block_invoke.420 + 158 (SplashScreenVC.m:158) 17 UIKitCore 0x1024410 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 688 18 UIKitCore 0xff8ba8 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 244 19 UIKitCore 0xff90d8 -[UIViewAnimationState animationDidStop:finished:] + 240 20 QuartzCore 0x171550 CA::Layer::run_animation_callbacks(void*) + 276 21 libdispatch.dylib 0x61298 _dispatch_client_callout + 16 22 libdispatch.dylib 0xfce0 _dispatch_main_queue_callback_4CF$VARIANT$mp + 904 23 CoreFoundation 0x9a998 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 12 24 CoreFoundation 0x94df8 __CFRunLoopRun + 2528 25 CoreFoundation 0x93ed0 CFRunLoopRunSpecific + 572 26 GraphicsServices 0x3570 GSEventRunModal + 160 27 UIKitCore 0xb302d0 -[UIApplication _run] + 1052 28 UIKitCore 0xb3584c UIApplicationMain + 164 29 HLLDriver-SEA 0x48cc main + 36 (main.m:36) 30 libdyld.dylib 0x1140 start + 4

    opened by wenjingjie 5
  • When there are multiple clickable texts in the string, the color cannot be set differently

    When there are multiple clickable texts in the string, the color cannot be set differently

    let htmlStr = "You can parse HTML with <strong>strong</strong>, <em>em</em>, and even <strong><em>nested strong and em</em></strong> tags."
    let emStr = StringStyle(
        .link(URL("emStr://")),
        .color(UIColor.red),
        .underline(.single, nil)
    )
    
    let strongStr = StringStyle(
        .link(URL("strongStr://")),
        .color(UIColor.green),
        .underline(.single, nil)
    )
    
    let style = StringStyle(
        .font(.systemFont(ofSize: 30)),
        .color(.black),
        .xmlRules([
            .style("em", emStr),
            .style("strong", strongStr),
        ])
    )
    let attributedText = htmlStr.styled(with: style)
    
    textView.linkTextAttributes = [
        NSAttributedString.Key.foregroundColor: UIColor.yellow
    ]
    textView.attributedText = attributedText
    
    

    Two links are set. If you do not set this sentence,

      textView.linkTextAttributes = [
                NSAttributedString.Key.foregroundColor: UIColor.yellow
            ]
    
    image

    they will all show blue. After setting, they will all turn yellow. How to set two different colors, and you can click the link,please help

    opened by zuqiu123 2
  • Error about Xcode 13.0

    Error about Xcode 13.0

    Hi ,I cannot build succeed in Xcode 13.0 for Swift 5.x Error as: Enum cases with associated values cannot be marked potentially unavailable with '@available'

    opened by tarek-chen 1
  • 'StringStyle' is ambiguous for type lookup

    'StringStyle' is ambiguous for type lookup

    iOS 15 SDK introduced StringStyle struct. So it's conflicting with the one from the framework and causes this error: 'StringStyle' is ambiguous for type lookup in this context

    opened by mvpaudrey 2
  • Strange Behavior with Dynamic Type Updates

    Strange Behavior with Dynamic Type Updates

    Hey Zev/Rightpointers! Hope you're doing well. I'm seeing some weird behavior with Dynamic Type and application.enableAdaptiveContentSizeMonitor() I'm wondering if anyone has context on.

    I have two StringStyles: one just uses the system body font (UIFont.preferredFont(for: .body)). The other uses a font in the bundle (Lato), and I create a scaled font like this:

    let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
    let size = descriptor.pointSize
    // swiftlint:disable:next force_unwrapping
    return UIFont(name: customFont.name, size: size)!
    

    In the demo video attached, I have 4 labels: applying both .adapt(body) and .adapt(control) to each of these styles. I'm also calling application.enableAdaptiveContentSizeMonitor(), so I'm hoping to see the labels responding to changes to Dynamic Type when I return to the app. Weirdly, what I'm seeing is that all 4 labels do adjust their size somewhat in the right direction (larger or smaller), but I still need to leave and recreate the view to get the correct dynamic size. In the video attached, the labels are initially the correct (small) size, and when I move the Dynamic Type slider to the large end, the labels get bigger, but not as big as expected. Once I leave the view and push to a new view, they're the correct size again. I see the same behavior regardless of whichever 2 points on the Dynamic Type slider I use. Any idea what might be causing this weird behavior?

    https://user-images.githubusercontent.com/7501762/104847690-ab379600-58af-11eb-8ca0-90af7c56cee0.mov

    opened by nevillco 1
  • Localization Arabic

    Localization Arabic

    I cannot make locaization from english to arabic example let text = "List of <nameStyle>%@</nameStyle>'s devices".localizedWithParameters(name)

    In the localized string i have the following : "List of <nameStyle>%@</nameStyle>'s devices"="الأجهزة <nameStyle>%@</nameStyle>'الخاصة";

    Please help

    opened by sam961 4
Releases(6.1.1)
  • 6.1.1(Jun 15, 2022)

    What's Changed

    • Add support for SwiftLint installed via Homebrew on Apple Silicon by @ZevEisenberg in https://github.com/Rightpoint/BonMot/pull/420
    • Update gems by @ZevEisenberg in https://github.com/Rightpoint/BonMot/pull/421
    • Bump podspec version, which I forgot to do when I released 6.1.0 by @ZevEisenberg in https://github.com/Rightpoint/BonMot/pull/422

    Full Changelog: https://github.com/Rightpoint/BonMot/compare/6.1.0...6.1.1

    Source code(tar.gz)
    Source code(zip)
  • 6.1.0(Jun 12, 2022)

    New Features

    • Add lineBreakStrategy paragraph attribute: https://github.com/Rightpoint/BonMot/pull/418 (@OhKanghoon)

    Full Changelog: https://github.com/Rightpoint/BonMot/compare/6.0.0...6.1.0

    Source code(tar.gz)
    Source code(zip)
  • 6.0.0(Jun 30, 2021)

    See all commits since 5.6.0.

    Changelog

    • Xcode 13 compatibility #410 #411 #412 (see #415 for a known issue)
    • Swift Package Manager test support #412
    • Simplify StringStyle.update(part:) switch statement #396
    • Fixes UISegmentedControl extension crash and improves efficiency #398
    • New XMLStyleRule variants for dynamic styling #391
    • Fixes notifyContainedAdaptiveContentSizeContainers() #400
    • Add allowsDefaultTighteningForTruncation paragraph attribute #404
    • Fix enum for module stability #406
    • Use Xcode 12 on CircleCI and CocoaPods environment context #395
    • Test cleanup and typo fixes #405
    • Remove old email address #408
    • README updates #414 #340

    Breaking changes

    • iOS 11.0 or higher
    • Xcode 12 or higher
    • Swift 5.0 or higher
    Source code(tar.gz)
    Source code(zip)
  • 5.6.0(Sep 18, 2020)

  • 5.5.1(Mar 19, 2020)

  • 5.5.0(Feb 1, 2020)

  • 5.4.1(Jul 1, 2019)

    See all commits since 5.4.

    Bug Fixes

    • Stop exporting a compatibility alias that was only ever intended to be used in the example project. Note that this is technically a breaking change, but it is unlikely that anyone was actually depending on the alias being there.
    Source code(tar.gz)
    Source code(zip)
  • 5.4(May 31, 2019)

    See all commits since 5.3.

    New Features

    • Add support for Xcode 10.2 and Swift 5.0

    Bug Fixes

    • Consider emoji when removing kerning from last character (#360, @LorDisturbia, @lukeandrews239)
    • Add AdaptiveStyle based on UIFontMetrics (#361, @ndonald2)

    Deprecations

    • Drops official support for Xcode 9.x
    Source code(tar.gz)
    Source code(zip)
  • 5.3(Sep 21, 2018)

  • 5.2(Mar 1, 2018)

    See all commits since 5.1.

    New Features

    • Add the ability to join a sequence of Composable and produce an attributed string. The upshot of this is that if you have an array of attributed strings, you can call joined(separator:) on them, supplying a string, attributed string, or other Composable as the separator, and get an attributed string. (#328 via #329, @ZevEisenberg)
    Source code(tar.gz)
    Source code(zip)
  • 5.1(Feb 24, 2018)

    See all commits since 5.0.1.

    New Features

    • Add support for making a font bold, italic, and bold/italic via the new .emphasis attribute. The cool thing about emphasis is that it can modify existing fonts, without having to re-specify the font. This means that you can do something like this: StringStyle(.font(.systemFont(ofSize: 17)), .emphasis(.italic)), and you'll get an italic version of the system font. Or, you can take any style and get the bold and/or italic version of it without knowing what its font is: someExistingStyle.byAdding(.emphasis([.bold, .italic])). Check out the example project for a cool use case for this in XML parsing! (@ZevEisenberg, #303 via #325)
    • Make Tracking's kerning(for:) method public. (@acacuce, #324)

    Improvements

    • Support building on Xcode 9.3 beta. (@rdingman, #320 via #322)
    Source code(tar.gz)
    Source code(zip)
  • 5.0.1(Jan 31, 2018)

    See all commits since 5.0

    Performance Improvements

    • Return existing font if it would be unmodified anyway. (#313, @muukii)

    Bug Fixes

    • Stop linting code when building via Carthage. (#309 via #319)
    • Specify swift_version in podspec for compatibility with CocoaPods 1.4.0.

    Miscellaneous

    • Update build for Xcode 9.2.
    • Minor readme cleanup.
    Source code(tar.gz)
    Source code(zip)
  • 5.0(Oct 13, 2017)

    BonMot 5.0 adds support for Xcode 9 and Swift 4, and drops support for Swift 2.x and 3.x. This means it also requires Xcode 9 and Swift 4. Please don't update if you're still using Xcode 8! In Xcode 9, even if your project is all on Swift 3.2, you can build BonMot using Swift 4, as long as you're using CocoaPods 1.4.0+.

    We also snuck in a feature and two bug fixes.

    See all commits since 4.3.1.

    New Features

    • Support for Swift 4 and Xcode 9 https://github.com/Raizlabs/BonMot/pull/296
    • Add support for new accessibility attributes introduced in iOS 11, tvOS 11, and watchOS 4 https://github.com/Raizlabs/BonMot/issues/292

    Improvements

    • Better Carthage support in tests: https://github.com/Raizlabs/BonMot/pull/295

    Bug Fixes

    • Fix a bug where styles that inherit from other styles were not properly overriding their parent styles' attributes https://github.com/Raizlabs/BonMot/pull/267
    • Fix a regression from the BonMot 3.x → 4.0 Swift rewrite. We now once again intelligently remove NSAttributedStringKey.kern from the last character, unless it's being concatenated into a large string. https://github.com/Raizlabs/BonMot/pull/290 (Reason)
    • Minor readme cleanup.

    Breaking Changes

    • Drops support for Swift 2.x and 3.x.

    Thanks to @Imperiopolis and @joe-goullaud for their help with this release.

    Source code(tar.gz)
    Source code(zip)
  • 4.3.1(Mar 30, 2017)

  • 4.3(Mar 27, 2017)

    See all commits since 4.2

    New Features

    • Add support for text case transforms. You can now transform arbitrary strings or substrings to lowercase, uppercase, and capitalized, with variants for both the default locale or a custom locale. You can also pass a custom transform function to perform whatever string transformations you want. See the Transform.swift and TransformTests.swift for API and usage examples. (#26 via #275, @ZevEisenberg)

    Other changes

    • Update source code to use SwiftLint 0.17.0
    Source code(tar.gz)
    Source code(zip)
  • 4.2(Feb 19, 2017)

    See all commits since 4.1.1

    New Features

    • Add support for OpenType fractions. (#243 via #270, @EthanSchatzline)

    Bug Fixes

    • Allow example app to build with Swift 2.3. (https://github.com/Raizlabs/BonMot/commit/830c34ad658b1ffcb27328d06ebe8945bcac3826, @ZevEisenberg)

    Other changes

    • Update project tooling to build using Xcode 8.2.1 and the latest stable releases of iOS, macOS, tvOS, and watchOS.
    Source code(tar.gz)
    Source code(zip)
  • 4.1.1(Feb 7, 2017)

    See all commits since 4.1

    Minor Changes in Example App

    • Add Taiwanese to language demos. (https://github.com/Raizlabs/BonMot/pull/268, @ateliercw)
    • Scale typographic features like body text. (https://github.com/Raizlabs/BonMot/commit/a24860ce75badc644520ef43275425b3828a9e02)
    • Scale accessibility examples. (https://github.com/Raizlabs/BonMot/commit/3176691e985597ad231c4ea239c0eda47c589209)
    Source code(tar.gz)
    Source code(zip)
  • 4.1(Jan 30, 2017)

    See all commits since 4.0.2

    New Features

    • Add support for speaking punctuation, speaking pitch, and speaking language attributes. (#155 via #266, @ZevEisenberg)

    Bug Fixes

    • Preserve images’ accessibility label through a tinting operation. (#263, @aral)
    • Improve compile times by profiling and optimizing the worst offenders. (#262, @ateliercw)

    Other changes

    • Some SwiftLint style fixes, and pin SwiftLint version. (#264, @ZevEisenberg)
    • Fix warnings in Xcode 8.3 beta 1 (#265, @ZevEisenberg)
    Source code(tar.gz)
    Source code(zip)
  • 4.0.2(Jan 13, 2017)

    See all commits since 4.0.1

    Bug Fixes

    • Stop breaking the ability to connect sent actions to controls in Interface Builder. (#252, @ZevEisenberg & @KingOfBrian)
    • Reinstate missing hyphenationFactor documentation, which was lost in the BonMot 4.0 update. (https://github.com/Raizlabs/BonMot/commit/03f971fdbc8a75e0d37ee1effe0fc10e58d484ee)
    • Enable running the example app on-device. (#258, @ZevEisenberg)

    Other changes

    • Code style and formatting, including updating SwiftLint.
    • Remove vestigial references to clang-format (https://github.com/Raizlabs/BonMot/commit/89d8244c93726496c5dacf6128ae8177a8a45440)
    • Specify simulators semantically for CircleCI. (#260)
    Source code(tar.gz)
    Source code(zip)
  • 4.0.1(Nov 16, 2016)

    See all commits since 4.0.0

    New Features and Major Changes

    • Renamed non-mutating instance method NSAttributedString.adapt(to:) to NSAttributedString.adapted(to:) to conform to Swift API Design Guidelines. (#248, @ZevEisenberg)

    Deprecations

    • Deprecated instance method NSAttributedString.adapt(to:). (#248)

    Minor Changes & Bug Fixes

    • Fix typo in ReadMe. (#247, @budged)
    • Update CircleCI badge style to match other badge styles. (a4f10e8e279fb4e326336dfa648220ca4f64988f)
    Source code(tar.gz)
    Source code(zip)
  • 4.0.0(Nov 14, 2016)

    BonMot 4.0 is a complete rewrite in Swift. It was primarily written by @KingOfBrian (who swears he only set out to write a blog post on Dynamic Type) and @ZevEisenberg, with contributions from @Imperiopolis, @mergesort, @noremac, @dostrander, and others.

    The 4.0 API is completely new, and it is not backwards compatible with BonMot 3. The best way to learn about the new API is to check out the ReadMe, and if you are migrating from BonMot 3, there is a migration guide BonMot 4.0 is compatible with Swift 2.2, 2.3, and 3.0.

    New Features

    • Full Swift rewrite.
    • Support for all Apple platforms: iOS, macOS, watchOS, and tvOS.
    • Support for Dynamic Type. (Readme)
    • Storyboard and XIB integration: apply global named styles via IBInspectable properties on common UI elements. (Readme)
    • Support for advanced OpenType font features including vertical position, small caps, ligatures, and contextual alternates. If you need an OpenType feature that is not supported, please file an issue.

    Improvements

    • More full-featured XML parsing, including nested tags and special characters. (Readme)
    • More powerful style composition, allowing true inheritance of properties from one style to another. (Readme)
    • Improved debugging helpers. (Readme)
    Source code(tar.gz)
    Source code(zip)
  • 4.0.rc.1(Nov 9, 2016)

    See changes between BonMot 3.2 and 4.0.rc.1.

    See all commits since 4.0.beta.1.

    New Features

    • Add support for vertical position, small caps, ligatures, and contextual alternates font features. (#173)
    • Update example app and inline documentation. Add BonMot 3 → 4 migration guide.

    Major/Breaking Changes

    • Change the StringStyle.style(...) method to a StringStyle.init(parts:...) initializer. (#222)
    • Renamed and re-scoped StringStylePart to StringStyle.Part. (#232)
    • Move continuous integration build from Travis to Circle CI. (@dostrander, #230)
    • Add a case to StringStyle.Part that takes a StringStyle. Create a new styled(with:overrideParts:) function on Composable. (@imperiopolis, #233)
    • Re-scope XMLRuleStyler as XMLStyleRule.Styler. (https://github.com/Raizlabs/BonMot/commit/71e04df9e4325be3684b2bd1aae42b04e923ae82)

    Minor Changes

    • Add currentStyle to XMLStyler protocol's style function. (d45c042f9bd1a901c0f370936e7489b92a65ed93)
    Source code(tar.gz)
    Source code(zip)
  • 4.0.beta.1(Oct 29, 2016)

    BonMot 4.0 is a complete rewrite in Swift. It was primarily written by @KingOfBrian, who swears he only set out to write a blog post on Dynamic Type, and @ZevEisenberg, with input from @Imperiopolis, @mergesort, @noremac, @dostrander, and others.

    The 4.0 API is completely new, and it is not backwards compatible with BonMot 3.x. This release is a beta, which means the new API is subject to change a little, but the main feature set is locked down. The goal of this release is to start using the new API in some real apps, including migrating from BonMot 3.x, and see if there are any major sticking points that we want to refine.

    The best way to learn about the new API is to check out the ReadMe. If you find a problem or need a clarification, please file an issue or submit a pull request to the feature/swift-first branch (naming things is hard).

    BonMot 4.0 is intended to be compatible with Swift 2.x and 3.0. If you run into problems with Swift compatibility, please file an issue!

    We can't wait to hear what you think. Please don't hold back from sending feedback! Like the Swift 3.0 release, this is your chance to register your complaints, because once it's released, it will be harder to make breaking changes.

    New Features

    • Full Swift rewrite.
    • Support for all Apple platforms: iOS, macOS, watchOS, and tvOS.
    • Support for Dynamic Type. (Readme)
    • Storyboard and XIB integration: apply global named styles via IBInspectable properties on common UI elements. (Readme)

    Improvements

    • More full-featured XML parsing, including nested tags and special characters. (Readme)
    • More powerful style composition, allowing true inheritance of properties from one style to another. (Readme)
    • Improved debugging helpers. (Readme)
    Source code(tar.gz)
    Source code(zip)
  • 3.2(Jul 15, 2016)

    See all commits since the last release here

    New Features and Major Changes

    • Rewrote the code samples in the ReadMe in Swift. (@nevillco, #152)
    • Added support for hyphenationFactor. (@Imperiopolis, #156)
    • Added support for embedding URLs in text. (@plarson, #150)
    • More powerful and predictable concatenation with the new -appendLink:separatorTextable: method, which takes a BONChain or BONText instead of a plain string. Replaces the now-deprecated appendLink:separator: method. (@nevillco, #160)

    Deprecations

    • Deprecated -[BONChain appendLink:separator]. It is replaced by -[BONChain appendLink:separatorTextable:], which takes a BONChain or BONText instead of a plain string. This means that you now have full control over the styling of the separator, rather having it pick up the styling of the preceding string. The deprecated method will be removed in a future release. (@nevillco, #160)

    Minor Changes & Bug Fixes

    • Some build system changes, but builds are still sometimes flaky.
    • Specify Ruby version in Travis config and Gemfile.
    • Update gems specified in Gemfile.lock.
    Source code(tar.gz)
    Source code(zip)
  • 3.1.1(May 25, 2016)

    See all commits since the last release here

    Minor Changes & Bug Fixes

    • Fix bug related to BONTag not copying its range when the tag is copied. (#148, @ZevEisenberg and @Imperiopolis)
    • Use correct version of CocoaPods in Travis builds.
    • Install latest Bundler before Travis builds.
    Source code(tar.gz)
    Source code(zip)
  • 3.1(May 23, 2016)

    See all commits since the last release here

    New Features and Major Changes

    • Added the ability to parse HTML-like tags in strings by mapping tag names to BONTextable objects (BONChain or BONText). Learn more here. (#69, @Imperiopolis)

    Minor Changes & Bug Fixes

    • Updated the example project to use CocoaPods 1.0 internally. This has no effect on the version of CocoaPods you use to integrate BonMot into your own project.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.1(Apr 20, 2016)

  • 3.0.0(Apr 19, 2016)

  • 3.0.0.beta.2(Apr 15, 2016)

  • 3.0.0.beta.1(Apr 15, 2016)

    See all commits since the last release here

    New Features and Major Changes

    • Added new UIKit utilities, available via the new BonMot/UIKit subspec, which make it easy to configure labels, text fields, and text views with text from BonMot. (#68, @imperiopolis)
    • Added -[NSAttributedString bon_humanReadableString], which expands special characters out into human-readable strings. This is useful for writing unit tests where you need to compare a BonMot-generated string with an example string which may contain invisible or hard-to-read characters. For example, a string with an embedded image, a non-breaking space, and some text that contains an en dash might be expanded as {image24x36}{noBreakSpace}Monday{enDash}Friday. (#40, @eliotw1 & @ZevEisenberg)
    • Added -lineBreakMode support, so you can specify the line break mode of attributed strings. (#111, @tettoffensive)
    • Added -[BONText generatesEmptyString], so you can find out whether it’s empty in O(1) (constant) time.

    Breaking Changes

    • Moved BONTextAlignmentConstraint to the new UIKit subspec, so you’ll need to include BonMot/UIKit in your Podfile if you want to use it. Carthage users should be unaffected by this change, since the project that is exposed to Carthage includes this subspec.
    • Renamed the BONChainable protocol to BONTextable to better express how it is used.
    • Removed previously deprecated -[BONText debugDescriptionIncludeImageAddresses:]. Use -[BONText debugStringIncludingImageAddresses:] instead.

    Deprecations

    • Deprecated -textColor in favor of -color on both BONChain and BONText. -textColor will be removed in the next major version update. (#135)
    • Deprecated +[BONText joinTexts:withSeparator]. It is replaced by +[BONText joinTextables:withSeparator], which supports mixing and matching BONChains and BONTexts for both the array to be joined and the separator.

    Minor Changes & Bug Fixes

    • Fixed support for case-sensitive file systems. (#118, @ejohansson)
    • Stabilized flaky continuous integration builds on Travis CI.
    • We now run BONSpecialGenerator.swift every time the example project is built, which makes it easier to add special characters to BonMot. (#89, @eliotw1)
    • Made more use of Objective-C lightweight generics in library and example project.
    • Improved tests and added new ones.
    • Added mechanism to fail the continuous integration build if static analysis fails.
    • De-emphasized BONText in the README. It is still the workhorse class of BonMot, but unless you need to query a property, BONChain should probably be your go-to for styling strings.
    Source code(tar.gz)
    Source code(zip)
Owner
Rightpoint
Rightpoint is an independent customer experience agency with technology at our core.
Rightpoint
More powerful label, attributed string builder and text parser.

DDText More powerful label, attributed string builder and text parser. DDLabel More powerful label than UILabel, using TextKit. It supports features b

Daniel 16 Nov 8, 2022
Easy Attributed String Creator

The main idea of this project is to have an online tool to be able to visually add formatting to a text and get back a swift and/or objective-c code t

Andres Canal 283 Dec 27, 2022
Swift String Validator. Simple lib for ios to validate string and UITextFields text for some criterias

Swift String validator About Library for easy and fastest string validation based on сciterias. Instalation KKStringValidator is available through Coc

Kostya 17 Dec 21, 2019
A simple library for building attributed strings, for a more civilized age.

Veneer A simple library for building attributed strings, for a more civilized age. Veneer was created to make creating attributed strings easier to re

Wess Cope 26 Dec 27, 2022
µframework for Attributed strings.

Attributed µframework for Attributed strings. What is Attributed? Attributed aims to be a drop in replacement to the current version of the NSAttribut

Nicholas Maccharoli 754 Jan 9, 2023
Easiest way to create an attributed UITextView (with support for multiple links and from html)

AttributedTextView Easiest way to create an attributed UITextView (with support for multiple links and html). See the demo app and the playground for

Edwin Vermeer 430 Nov 24, 2022
A Swifty API for attributed strings

SwiftyAttributes A Swifty API for attributed strings. With SwiftyAttributes, you can create attributed strings like so: let fancyString = "Hello World

Eddie Kaiger 1.5k Jan 5, 2023
Texstyle allows you to format iOS attributed strings easily.

Texstyle allows you to format attributed strings easily. Features Applying attributes with strong typing and autocompletion Cache for attributes Subst

Rosberry 79 Sep 9, 2022
An easier way to compose attributed strings

TextAttributes makes it easy to compose attributed strings. let attrs = TextAttributes() .font(name: "HelveticaNeue", size: 16) .foregroundCol

Damien 2.2k Dec 31, 2022
A quick helper for setting attributed texts to UILabel.

UILabelAttributedTextHelper A quick helper for setting attributed texts to UILabel. Sample usage: label.setAttributedText( leadingText: "H

Glenn Posadas 5 Aug 24, 2022
Croc is a swift emoji string parsing library

Croc is a library for parsing emojis on iOS. It provides a simple and lightweight interface for detecting, generating, categorizing and managing emoji characters, making emoji-powered features an easy task for developers.

Joe Kalash 127 Nov 20, 2022
A Cross-Platform String and Regular Expression Library written in Swift.

Guitar ?? A Cross-Platform String and Regular Expression Library written in Swift. About This library seeks to add common string manipulation function

Arthur Ariel Sabintsev 659 Dec 27, 2022
Swift emoji string parsing library

Croc is a library for parsing emojis on iOS. It provides a simple and lightweight interface for detecting, generating, categorizing and managing emoji

Joe Kalash 125 Sep 27, 2021
Great Swift String Pluralize Extension

Pluralize.swift Great Swift String Pluralize Extension case-insensitive tons of rules for irregular nouns (plural form) supports uncountable nouns all

Joshua Arvin Lat 193 Nov 8, 2022
A comprehensive, lightweight string extension for Swift

SwiftString SwiftString is a lightweight string extension for Swift. This library was motivated by having to search StackOverflow for common string op

Andrew Mayne 1.6k Dec 30, 2022
🌍⏩📄 Convert ISO8859 1-16 Encoded Text to String in Swift. Supports iOS, tvOS, watchOS and macOS.

ISO8859 Convert ISO8859 1-16 Encoded Text to String in Swift. Usage let encoding = ISO8859.part1 let string = String([...], iso8859Encoding: encoding)

Devran Cosmo Uenal 18 Jan 2, 2023
String (and more) validation for iOS

Swift Validators ?? String validation for iOS. Contents Installation Walkthrough Usage Available validators License ReactiveSwift + SwiftValidators Wa

George Kaimakas 241 Nov 13, 2022
Easy string decoration with styles

StyleDecorator Design string simply by linking attributes. Example Create Decorator with specific Style and link it at the end of needed string or wra

Dmytro Pylypenko 15 Nov 4, 2021