Skip to content

Commit

Permalink
Add categories on UILabel, UITextView, and UITextField to accept a ch…
Browse files Browse the repository at this point in the history
…ainable object.
  • Loading branch information
Imperiopolis committed Mar 5, 2016
1 parent 30ba7f9 commit d1864be
Show file tree
Hide file tree
Showing 12 changed files with 578 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Example/BonMot.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
CD6DF0591BF6B7AF00676E2D /* DashedHairlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6DF0581BF6B7AF00676E2D /* DashedHairlineView.m */; };
CD6DF05A1BF6B98500676E2D /* NSDictionary+BONEquality.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6DF04F1BF6B53100676E2D /* NSDictionary+BONEquality.m */; };
CDE658581C24ADD8009C7D09 /* BONUnderlineAndStrikethroughTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE658571C24ADD8009C7D09 /* BONUnderlineAndStrikethroughTestCase.m */; };
EC433DB61C88BBDE001B3ABE /* BONUtilitiesTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = EC433DB41C88BBDB001B3ABE /* BONUtilitiesTestCase.m */; };
F6E9CEC93164745FB8BEB8FC /* Pods_BonMot_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E8BB0552A748FFBA2BD0BE4 /* Pods_BonMot_Example.framework */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -160,6 +161,7 @@
CDE658571C24ADD8009C7D09 /* BONUnderlineAndStrikethroughTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BONUnderlineAndStrikethroughTestCase.m; sourceTree = "<group>"; };
CFD6D97A946F2FEF294F4BBB /* Pods_BonMot_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BonMot_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EBE7037465B0F6F184875C40 /* Pods-BonMot_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BonMot_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-BonMot_Example/Pods-BonMot_Example.release.xcconfig"; sourceTree = "<group>"; };
EC433DB41C88BBDB001B3ABE /* BONUtilitiesTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BONUtilitiesTestCase.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -278,6 +280,7 @@
CD6DEFDE1BF6ADF900676E2D /* BONTextAlignmentTestCase.m */,
CD6DEFDF1BF6ADF900676E2D /* BONTrackingTestCase.m */,
CDE658571C24ADD8009C7D09 /* BONUnderlineAndStrikethroughTestCase.m */,
EC433DB41C88BBDB001B3ABE /* BONUtilitiesTestCase.m */,
6003F5B6195388D20070C39A /* Supporting Files */,
CD6DF0481BF6B37E00676E2D /* DummyAssetClass.h */,
CD6DF0491BF6B37E00676E2D /* DummyAssetClass.m */,
Expand Down Expand Up @@ -598,6 +601,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EC433DB61C88BBDE001B3ABE /* BONUtilitiesTestCase.m in Sources */,
CD6DF05A1BF6B98500676E2D /* NSDictionary+BONEquality.m in Sources */,
CD6DF04B1BF6B37E00676E2D /* DummyAssetClass.m in Sources */,
CD6DEFE51BF6ADF900676E2D /* BONChainEmptyStringTestCase.m in Sources */,
Expand Down
254 changes: 254 additions & 0 deletions Example/Tests/BONUtilitiesTestCase.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
//
// BONUtilitiesTestCase.m
// BonMot
//
// Created by Nora Trapp on 3/3/16.
//
//

#import "BONBaseTestCase.h"

@import BonMot;

@interface BONUtilitiesTestCase : BONBaseTestCase

@end

@implementation BONUtilitiesTestCase

#pragma mark - UILabel

- (void)testLabelTextAfterChainable
{
UILabel *label = UILabel.new;
label.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
label.text = @"Hello, world!";

XCTAssertEqualObjects(label.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(label.attributedText, controlAttributes);
}

- (void)testLabelTextBeforeChainable
{
UILabel *label = UILabel.new;
label.text = @"Hello, world!";
label.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertEqualObjects(label.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(label.attributedText, controlAttributes);
}

- (void)testLabelAttributedTextAfterChainable
{
UILabel *label = UILabel.new;
label.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
label.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];

XCTAssertNil(label.bonChainable);
XCTAssertEqualObjects(label.attributedText.string, @"Hello, world!");

NSDictionary *controlAttributes = [self defaultAttributesForClass:[UILabel class] withString:@"Hello, world!"];

BONAssertAttributedStringHasAttributes(label.attributedText, controlAttributes);
}

- (void)testLabelAttributedTextBeforeChainable
{
UILabel *label = UILabel.new;
label.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];
label.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertNotNil(label.bonChainable);
XCTAssertEqualObjects(label.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(label.attributedText, controlAttributes);
}

#pragma mark - UITextView

- (void)testTextViewTextAfterChainable
{
UITextView *textView = UITextView.new;
textView.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
textView.text = @"Hello, world!";

XCTAssertEqualObjects(textView.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(textView.attributedText, controlAttributes);
}

- (void)testTextViewTextBeforeChainable
{
UITextView *textView = UITextView.new;
textView.text = @"Hello, world!";
textView.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertEqualObjects(textView.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(textView.attributedText, controlAttributes);
}

- (void)testTextViewAttributedTextAfterChainable
{
UITextView *textView = UITextView.new;
textView.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
textView.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];

XCTAssertEqualObjects(textView.attributedText.string, @"Hello, world!");

NSDictionary *controlAttributes = [self defaultAttributesForClass:[UITextView class] withString:@"Hello, world!"];

BONAssertAttributedStringHasAttributes(textView.attributedText, controlAttributes);
}

- (void)testTextViewAttributedTextBeforeChainable
{
UITextView *textView = UITextView.new;
textView.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];
textView.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertEqualObjects(textView.attributedText.string, @"Hello, world!");

NSParagraphStyle *defaultParagraphStyle = [[NSParagraphStyle alloc] init];

NSDictionary *controlAttributes = @{
BONValueFromRange(0, 13) : @{
NSParagraphStyleAttributeName : defaultParagraphStyle,
NSFontAttributeName : [UIFont systemFontOfSize:16],
},
};

BONAssertAttributedStringHasAttributes(textView.attributedText, controlAttributes);
}

#pragma mark - UITextField

- (void)testTextFieldTextAfterChainable
{
UITextField *textField = UITextField.new;
textField.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
textField.text = @"Hello, world!";

XCTAssertEqualObjects(textField.attributedText.string, @"Hello, world!");

NSMutableDictionary *controlAttributes = [self defaultAttributesForClass:[UITextField class] withString:@"Hello, world!"];
[controlAttributes[BONValueFromRange(0, 13)] addEntriesFromDictionary:@{
NSFontAttributeName : [UIFont systemFontOfSize:16],
}];

BONAssertAttributedStringHasAttributes(textField.attributedText, controlAttributes);
}

- (void)testTextFieldTextBeforeChainable
{
UITextField *textField = UITextField.new;
textField.text = @"Hello, world!";
textField.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertEqualObjects(textField.attributedText.string, @"Hello, world!");

NSMutableDictionary *controlAttributes = [self defaultAttributesForClass:[UITextField class] withString:@"Hello, world!"];
[controlAttributes[BONValueFromRange(0, 13)] addEntriesFromDictionary:@{
NSFontAttributeName : [UIFont systemFontOfSize:16],
}];

BONAssertAttributedStringHasAttributes(textField.attributedText, controlAttributes);
}

- (void)testTextFieldAttributedTextAfterChainable
{
UITextField *textField = UITextField.new;
textField.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);
textField.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];

XCTAssertNil(textField.bonChainable);
XCTAssertEqualObjects(textField.attributedText.string, @"Hello, world!");

NSDictionary *controlAttributes = [self defaultAttributesForClass:[UITextField class] withString:@"Hello, world!"];

BONAssertAttributedStringHasAttributes(textField.attributedText, controlAttributes);
}

- (void)testTextFieldAttributedTextBeforeChainable
{
UITextField *textField = UITextField.new;
textField.attributedText = [[NSAttributedString alloc] initWithString:@"Hello, world!"];
textField.bonChainable = BONChain.new.font([UIFont systemFontOfSize:16]);

XCTAssertNotNil(textField.bonChainable);
XCTAssertEqualObjects(textField.attributedText.string, @"Hello, world!");

NSMutableDictionary *controlAttributes = [self defaultAttributesForClass:[UITextField class] withString:@"Hello, world!"];
[controlAttributes[BONValueFromRange(0, 13)] addEntriesFromDictionary:@{
NSFontAttributeName : [UIFont systemFontOfSize:16],
}];

BONAssertAttributedStringHasAttributes(textField.attributedText, controlAttributes);
}

#pragma mark - Helpers

- (NSMutableDictionary *)defaultAttributesForClass:(Class)viewClass withString:(NSString *)string
{
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];

id view = [[viewClass alloc] init];
if ([view respondsToSelector:@selector(setAttributedText:)] &&
[view respondsToSelector:@selector(attributedText)]) {
[view setAttributedText:[[NSAttributedString alloc] initWithString:string]];
[[view attributedText] enumerateAttributesInRange:NSMakeRange(0, string.length) options:0 usingBlock:^(NSDictionary<NSString *, id> *_Nonnull attrs, NSRange range, BOOL *_Nonnull stop) {
attributes[[NSValue valueWithRange:range]] = [attrs mutableCopy];
}];
}

return attributes;
}

@end
3 changes: 3 additions & 0 deletions Pod/BonMot.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@
#import "BONSpecial.h"
#import "BONTextAlignmentConstraint.h"
#import "UIImage+BONMotUtilities.h"
#import "UILabel+BonMotUtilities.h"
#import "UITextField+BonMotUtilities.h"
#import "UITextView+BonMotUtilities.h"
20 changes: 20 additions & 0 deletions Pod/Classes/UILabel+BonMotUtilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// UILabel+BonMotUtilities.h
// Pods
//
// Created by Nora Trapp on 3/2/16.
//
//

@import UIKit;
@protocol BONChainable;

@interface UILabel (BonMotUtilities)

/**
* Assign a @p BONChainable object to apply to the label text. When a new value is assigned to @p text the chain attributes will be applied.
* If a new value is assigned directly to @p attributedText the @p bonChainable property will be set to @p nil.
*/
@property (copy, nonatomic) id<BONChainable> bonChainable;

@end
76 changes: 76 additions & 0 deletions Pod/Classes/UILabel+BonMotUtilities.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// UILabel+BonMotUtilities.m
// Pods
//
// Created by Nora Trapp on 3/2/16.
//
//

#import "BonMot.h"
@import ObjectiveC.runtime;

@implementation UILabel (BonMotUtilities)

- (void)setBonChainable:(id<BONChainable>)chainable
{
objc_setAssociatedObject(self, @selector(bonChainable), chainable, OBJC_ASSOCIATION_COPY_NONATOMIC);

if (chainable) {
chainable.text.string = self.text;
[self bon_setAttributedText:chainable.text.attributedString];

if (chainable.text.font) {
self.font = chainable.text.font;
}

if (chainable.text.textColor) {
self.textColor = chainable.text.textColor;
}
}
}

- (BONChain *)bonChainable
{
return objc_getAssociatedObject(self, @selector(bonChainable));
}

+ (void)load
{
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
Method originalSetTextMethod = class_getInstanceMethod(self, @selector(setText:));
Method newSetTextMethod = class_getInstanceMethod(self, @selector(bon_setText:));
method_exchangeImplementations(originalSetTextMethod, newSetTextMethod);

Method originalSetAttributedTextMethod = class_getInstanceMethod(self, @selector(setAttributedText:));
Method newSetAttributedTextMethod = class_getInstanceMethod(self, @selector(bon_setAttributedText:));
method_exchangeImplementations(originalSetAttributedTextMethod, newSetAttributedTextMethod);
});
}

- (void)bon_setText:(NSString *)text
{
if (self.bonChainable) {
self.bonChainable.text.string = text;
[self bon_setAttributedText:self.bonChainable.text.attributedString];
}
else {
[self bon_setText:text];
}
}

- (void)bon_setAttributedText:(NSAttributedString *)attributedText
{
if ([self.bonChainable.text.font isEqual:self.font]) {
self.font = nil;
}

if ([self.bonChainable.text.textColor isEqual:self.textColor]) {
self.textColor = nil;
}

self.bonChainable = nil;
[self bon_setAttributedText:attributedText];
}

@end

0 comments on commit d1864be

Please sign in to comment.