BonMot (pronounced Bon Mo, French for good word) is an iOS attributed string generation library. It abstracts away the advanced iOS typography tools, freeing you to focus on making your text beautiful. For Mac!
To run the example project, run pod try BonMot
, or clone the repo and open Example/BonMot.xcworkspace
.
BonMot is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'BonMot'
If you want to use the UIKit Utilities, add:
pod 'BonMot/UIKit'
BonMot is also compatible with Carthage. To install it, simply add the following line to your Cartfile:
github "Raizlabs/BonMot"
BonMot uses attributed strings to give you control over the following typographical features:
- Font
- Text color
- Tracking (in either UIKit Points or Adobe-friendly thousandths of an em)
- First line head indent
- Head indent
- Tail indent
- Line height multiple
- Maximum line height
- Minimum line height
- Line spacing
- Line break mode
- Paragraph spacing before
- Paragraph spacing after
- Baseline offset
- Text alignment
- Underlining and strikethrough
- Figure case (uppercase vs. lowercase numbers)
- Figure spacing (monospace vs. proportional numbers)
- Inline Images with optional multi-line paragraph alignment
Think something is missing? Please file an issue (or add a +1 if one already exists).
In any file where you want to use BonMot, simply #import <BonMot/BonMot.h>
or @import BonMot
.
The basic object in BonMot is BONText
. You create a text object, set some properties to configure the font, and then ask for its .attributedString
to get a string formatted according to your specification. Or ask for .attributes
if you just need the attributes dictionary:
NSString *quote = @"I used to love correcting people’s grammar until\
I realized what I loved more was having friends.\n\
—Mara Wilson";
BONText *text = [BONText new];
text.font = [UIFont fontWithName:@"AmericanTypewriter" size:17.0];
text.lineHeightMultiple = 1.8;
text.string = quote;
NSAttributedString *string = text.attributedString;
NSDictionary *attributes = text.attributes;
BONChain
is a wrapper around BONText
that allows you to chain properties together for a more concise expression of a style. You can create a chain with a normal [[BONChain alloc] init]
, but it's easier to just use [BONChain new]
or the even shorter and technically valid BONChain.new
:
NSString *quote = @"I used to love correcting people’s grammar until\
I realized what I loved more was having friends.\n\
—Mara Wilson";
// line-wrapped for readability
NSAttributedString *attributedString =
BONChain.new // [BONChain new] and [[BONChain alloc] init] also work
.fontNameAndSize(@"AmericanTypewriter", 17.0)
.lineHeightMultiple(1.8)
.string(quote)
.attributedString;
You can also create a local variable or property to save a partially-configured chain. All the chaining methods pass copies of the chain, so you don't have to worry about later changes clobbering earlier properties:
// Base Chain
BONChain *birdChain =
BONChain.new
.lineHeightMultiple(1.2)
.font([UIFont systemFontOfSize:17.0])
.string(@"bird");
// Two chains with different colors
// that inherit their parents’ properties
BONChain *redBirds = birdChain.textColor([UIColor redColor]);
BONChain *blueBirds = birdChain.textColor([UIColor blueColor]);
// two different attributed strings with all attributes shared
// except for text color
NSAttributedString *redBirdString = redBirds.attributedString;
NSAttributedString *blueBirdString = blueBirds.attributedString;
You can concatenate an array of BONText
s:
BONText *oneFish = BONChain.new.string(@"one fish").text;
BONText *twoFish = BONChain.new.string(@"two fish").text;
BONText *redFish = BONChain.new.string(@"red fish").textColor([UIColor redColor]).text;
BONText *blueFish = BONChain.new.string(@"blue fish").textColor([UIColor blueColor]).text;
BONText *separator = BONChain.new.string(@", ").text;
NSAttributedString *string = [BONText joinTexts:@[ oneFish, twoFish, redFish, blueFish ] withSeparator:separator];
Outputs:
You can also append texts directly to each other:
NSString *commaSpace = @", ";
BONChain *chain = BONChain.new;
[chain appendLink:BONChain.new.string(@"one fish")];
[chain appendLink:BONChain.new.string(@"two fish") separator:commaSpace];
[chain appendLink:BONChain.new.string(@"red fish").textColor([UIColor redColor]) separator:commaSpace];
[chain appendLink:BONChain.new.string(@"blue fish").textColor([UIColor blueColor]) separator:commaSpace];
NSAttributedString *string = chain.attributedString;
Outputs:
(Notice that the comma after red fish
is red, but in the previous example, it was not colored. This is the behavior that made the most sense to me, but please open an issue or pull request if you think it should be different.)
BonMot uses NSTextAttachment
to embed images in strings. Simply use the .image
property of a chain or text:
BONChain *chain = BONChain.new;
[chain appendLink:BONChain.new.image(someUIImage).baselineOffset(-4.0)];
[chain appendLink:BONChain.new.text(@"label with icon") separator: @" "];
NSAttributedString *string = chain.attributedString;
Outputs:
If you need to wrap multiple lines of text after an image, use the indentSpacer
property to align the whole paragraph after the image:
NSString *quote = @"This is some text that goes on and on and spans multiple lines, and it all ends up left-aligned";
BONChain *chain = BONChain.new;
[chain appendLink:BONChain.new.image(someUIImage).indentSpacer(10.0)];
[chain appendLink:BONChain.new.string(quote)];
NSAttributedString *attributedString = chain.attributedString;
Outputs:
You can easily access those hard-to-find special characters using the BONSpecial
class. These include the No-Break Space, En and Em Spaces, various kinds of dashes, and more. If it’s hard to see in your source code or debug logs, it belongs in BONSpecial
. If you want to add special characters to BonMot, add them to BONSpecialGenerator.swift
(requires Xcode 7), run swift BONSpecialGenerator.swift
, and submit a pull request! See SpecialCharactersCell.m
in the sample project for some examples of how to use BONSpecial
.
BonMot provides several utilities that enhance its interoperability with UIKit.
Note: to use these utilities, add pod 'BonMot/UIKit'
to your Podfile.
BonMot provides a bonTextable
property on UILabel
, UITextView
, and UITextField
that allows assigning a BONTextable
object to apply styling to any strings assigned via the bonString
property.
Note: to use these utilities, add pod 'BonMot/UIKit'
to your Podfile.
UILabel *label = [[UILabel alloc] init];
BONChain *chain = BONChain.new.adobeTracking(300).fontNameAndSize(@"Avenir-Book", 18.0f);
label.bonTextable = chain;
label.bonString = @"Some initial text.";
Outputs:
Some time later, you can update the text of the label with a plain string, without losing the original styling from the BONTextable
.
label.bonString = @"Some updated text.";
Outputs:
UIKit lets you align labels by top, bottom, or baseline. BonMot includes BONTextAlignmentConstraint
, 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:
BONTextAlignmentConstraint
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, BONTextAlignmentConstraint
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: BONTextAlignmentConstraint
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 BONTextAlignmentConstraint
programmatically or in Interface Builder. In code, use the convenience initializer:
[BONTextAlignmentConstraint constraintWithItem:someLabel
attribute:BONConstraintAttributeCapHeight
relatedBy:NSLayoutRelationEqual
toItem:someOtherLabel
attribute:BONConstraintAttributeCapHeight].active = YES;
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 BONTextAlignmentConstraint
:
Next, switch to the Attributes Inspector. BONTextAlignmentConstraint
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.
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.
Issues and pull requests are welcome! Please format all code using clang-format
and the included .clang-format
configuration file.
Zev Eisenberg: mailto:[email protected], @ZevEisenberg
Logo by Jon Lopkin: @jonlopkin
BonMot is available under the MIT license. See the LICENSE file for more info.