From e4abe898d5b15d28bc1eff6ea5cb91ee87690961 Mon Sep 17 00:00:00 2001 From: Hannah Troisi Date: Fri, 24 Jun 2016 16:53:10 -0700 Subject: [PATCH] [ASEditableTextNode] Support UITextInputTraits pass-through methods (threadsafe for use before view creation) (#1809) * [ASEditableTextNode] Support UITextInputTraits * consistent property attributes * remove logging, fix tests to account for UIKit weirdness * address @appleguy's comments --- AsyncDisplayKit/ASEditableTextNode.h | 13 +- AsyncDisplayKit/ASEditableTextNode.mm | 257 ++++++++++++++++-- .../ASEditableTextNodeTests.m | 88 +++++- 3 files changed, 332 insertions(+), 26 deletions(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index 92e720f211..1ca7040b93 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN @abstract Implements a node that supports text editing. @discussion Does not support layer backing. */ -@interface ASEditableTextNode : ASDisplayNode +@interface ASEditableTextNode : ASDisplayNode /** * @abstract Initializes an editable text node using default TextKit components. @@ -93,9 +93,16 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readwrite) UIEdgeInsets textContainerInset; /** - @abstract The returnKeyType of the keyboard. This value defaults to UIReturnKeyDefault. + @abstract properties. */ -@property (nonatomic, readwrite) UIReturnKeyType returnKeyType; +@property(nonatomic, readwrite, assign) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences +@property(nonatomic, readwrite, assign) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault +@property(nonatomic, readwrite, assign) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault; +@property(nonatomic, readwrite, assign) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault +@property(nonatomic, readwrite, assign) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault +@property(nonatomic, readwrite, assign) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum) +@property(nonatomic, readwrite, assign) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents) +@property(nonatomic, readwrite, assign, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO /** @abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user. diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index fda9fd6e52..2ea00acf12 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -17,6 +17,42 @@ #import "ASTextNodeWordKerner.h" #import "ASThread.h" +/** + @abstract Object to hold UITextView's pending UITextInputTraits +**/ +@interface _ASTextInputTraitsPendingState : NSObject + +@property (nonatomic, readwrite, assign) UITextAutocapitalizationType autocapitalizationType; +@property (nonatomic, readwrite, assign) UITextAutocorrectionType autocorrectionType; +@property (nonatomic, readwrite, assign) UITextSpellCheckingType spellCheckingType; +@property (nonatomic, readwrite, assign) UIKeyboardAppearance keyboardAppearance; +@property (nonatomic, readwrite, assign) UIKeyboardType keyboardType; +@property (nonatomic, readwrite, assign) UIReturnKeyType returnKeyType; +@property (nonatomic, readwrite, assign) BOOL enablesReturnKeyAutomatically; +@property (nonatomic, readwrite, assign, getter=isSecureTextEntry) BOOL secureTextEntry; + +@end + +@implementation _ASTextInputTraitsPendingState + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + // set default values, as defined in Apple's comments in UITextInputTraits.h + _autocapitalizationType = UITextAutocapitalizationTypeSentences; + _autocorrectionType = UITextAutocorrectionTypeDefault; + _spellCheckingType = UITextSpellCheckingTypeDefault; + _keyboardAppearance = UIKeyboardAppearanceDefault; + _keyboardType = UIKeyboardTypeDefault; + _returnKeyType = UIReturnKeyDefault; + + return self; +} + +@end + /** @abstract As originally reported in rdar://14729288, when scrollEnabled = NO, UITextView does not calculate its contentSize. This makes it difficult @@ -85,6 +121,10 @@ ASTextKitComponents *_placeholderTextKitComponents; // Forwards NSLayoutManagerDelegate methods related to word kerning ASTextNodeWordKerner *_wordKerner; + + // UITextInputTraits + ASDN::RecursiveMutex _textInputTraitsLock; + _ASTextInputTraitsPendingState *_textInputTraits; // Misc. State. BOOL _displayingPlaceholder; // Defaults to YES. @@ -93,6 +133,8 @@ NSRange _previousSelectedRange; } +@property (nonatomic, strong, readonly) _ASTextInputTraitsPendingState *textInputTraits; + @end @implementation ASEditableTextNode @@ -117,13 +159,12 @@ _textKitComponents = textKitComponents; _textKitComponents.layoutManager.delegate = self; _wordKerner = [[ASTextNodeWordKerner alloc] init]; - _returnKeyType = UIReturnKeyDefault; _textContainerInset = UIEdgeInsetsZero; // Create the placeholder scaffolding. _placeholderTextKitComponents = placeholderTextKitComponents; _placeholderTextKitComponents.layoutManager.delegate = self; - + return self; } @@ -151,8 +192,6 @@ { [super didLoad]; - ASDN::MutexLocker l(_textKitLock); - void (^configureTextView)(UITextView *) = ^(UITextView *textView) { if (!_displayingPlaceholder || textView != _textKitComponents.textView) { // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder. @@ -164,28 +203,48 @@ textView.opaque = NO; } textView.textContainerInset = self.textContainerInset; + + // Configure textView with UITextInputTraits + { + ASDN::MutexLocker l(_textInputTraitsLock); + if (_textInputTraits) { + textView.autocapitalizationType = _textInputTraits.autocapitalizationType; + textView.autocorrectionType = _textInputTraits.autocorrectionType; + textView.spellCheckingType = _textInputTraits.spellCheckingType; + textView.keyboardType = _textInputTraits.keyboardType; + textView.keyboardAppearance = _textInputTraits.keyboardAppearance; + textView.returnKeyType = _textInputTraits.returnKeyType; + textView.enablesReturnKeyAutomatically = _textInputTraits.enablesReturnKeyAutomatically; + textView.secureTextEntry = _textInputTraits.isSecureTextEntry; + } + } + + [self.view addSubview:textView]; }; + ASDN::MutexLocker l(_textKitLock); + // Create and configure the placeholder text view. _placeholderTextKitComponents.textView = [[UITextView alloc] initWithFrame:CGRectZero textContainer:_placeholderTextKitComponents.textContainer]; _placeholderTextKitComponents.textView.userInteractionEnabled = NO; _placeholderTextKitComponents.textView.accessibilityElementsHidden = YES; configureTextView(_placeholderTextKitComponents.textView); - [self.view addSubview:_placeholderTextKitComponents.textView]; // Create and configure our text view. - _textKitComponents.textView = self.textView; + _textKitComponents.textView = [[ASPanningOverriddenUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; _textKitComponents.textView.scrollEnabled = _scrollEnabled; _textKitComponents.textView.delegate = self; #if TARGET_OS_IOS _textKitComponents.textView.editable = YES; #endif _textKitComponents.textView.typingAttributes = _typingAttributes; - _textKitComponents.textView.returnKeyType = _returnKeyType; _textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string; configureTextView(_textKitComponents.textView); - [self.view addSubview:_textKitComponents.textView]; + [self _updateDisplayingPlaceholder]; + + // once view is loaded, setters set directly on view + _textInputTraits = nil; } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize @@ -261,9 +320,8 @@ - (UITextView *)textView { ASDisplayNodeAssertMainThread(); - if (!_textKitComponents.textView) { - _textKitComponents.textView = [[ASPanningOverriddenUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; - } + [self view]; + ASDisplayNodeAssert(_textKitComponents.textView != nil, @"UITextView must be created in -[ASEditableTextNode didLoad]"); return _textKitComponents.textView; } @@ -425,13 +483,6 @@ return [_textKitComponents.textView textInputMode]; } -- (void)setReturnKeyType:(UIReturnKeyType)returnKeyType -{ - ASDN::MutexLocker l(_textKitLock); - _returnKeyType = returnKeyType; - [_textKitComponents.textView setReturnKeyType:_returnKeyType]; -} - - (BOOL)isFirstResponder { ASDN::MutexLocker l(_textKitLock); @@ -460,6 +511,176 @@ return [_textKitComponents.textView resignFirstResponder]; } +#pragma mark - UITextInputTraits + +- (_ASTextInputTraitsPendingState *)textInputTraits +{ + if (!_textInputTraits) { + _textInputTraits = [[_ASTextInputTraitsPendingState alloc] init]; + } + return _textInputTraits; +} + +- (void)setAutocapitalizationType:(UITextAutocapitalizationType)autocapitalizationType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setAutocapitalizationType:autocapitalizationType]; + } else { + [self.textInputTraits setAutocapitalizationType:autocapitalizationType]; + } +} + +- (UITextAutocapitalizationType)autocapitalizationType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView autocapitalizationType]; + } else { + return [self.textInputTraits autocapitalizationType]; + } +} + +- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setAutocorrectionType:autocorrectionType]; + } else { + [self.textInputTraits setAutocorrectionType:autocorrectionType]; + } +} + +- (UITextAutocorrectionType)autocorrectionType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView autocorrectionType]; + } else { + return [self.textInputTraits autocorrectionType]; + } +} + +- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setSpellCheckingType:spellCheckingType]; + } else { + [self.textInputTraits setSpellCheckingType:spellCheckingType]; + } +} + +- (UITextSpellCheckingType)spellCheckingType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView spellCheckingType]; + } else { + return [self.textInputTraits spellCheckingType]; + } +} + +- (void)setEnablesReturnKeyAutomatically:(BOOL)enablesReturnKeyAutomatically +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically]; + } else { + [self.textInputTraits setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically]; + } +} + +- (BOOL)enablesReturnKeyAutomatically +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView enablesReturnKeyAutomatically]; + } else { + return [self.textInputTraits enablesReturnKeyAutomatically]; + } +} + +- (void)setKeyboardAppearance:(UIKeyboardAppearance)setKeyboardAppearance +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setKeyboardAppearance:setKeyboardAppearance]; + } else { + [self.textInputTraits setKeyboardAppearance:setKeyboardAppearance]; + } +} + +- (UIKeyboardAppearance)keyboardAppearance +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView keyboardAppearance]; + } else { + return [self.textInputTraits keyboardAppearance]; + } +} + +- (void)setKeyboardType:(UIKeyboardType)keyboardType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setKeyboardType:keyboardType]; + } else { + [self.textInputTraits setKeyboardType:keyboardType]; + } +} + +- (UIKeyboardType)keyboardType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView keyboardType]; + } else { + return [self.textInputTraits keyboardType]; + } +} + +- (void)setReturnKeyType:(UIReturnKeyType)returnKeyType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setReturnKeyType:returnKeyType]; + } else { + [self.textInputTraits setReturnKeyType:returnKeyType]; + } +} + +- (UIReturnKeyType)returnKeyType +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView returnKeyType]; + } else { + return [self.textInputTraits returnKeyType]; + } +} + +- (void)setSecureTextEntry:(BOOL)secureTextEntry +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setSecureTextEntry:secureTextEntry]; + } else { + [self.textInputTraits setSecureTextEntry:secureTextEntry]; + } +} + +- (BOOL)isSecureTextEntry +{ + ASDN::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView isSecureTextEntry]; + } else { + return [self.textInputTraits isSecureTextEntry]; + } +} + #pragma mark - UITextView Delegate - (void)textViewDidBeginEditing:(UITextView *)textView { diff --git a/AsyncDisplayKitTests/ASEditableTextNodeTests.m b/AsyncDisplayKitTests/ASEditableTextNodeTests.m index 43d415ac51..83960f7331 100644 --- a/AsyncDisplayKitTests/ASEditableTextNodeTests.m +++ b/AsyncDisplayKitTests/ASEditableTextNodeTests.m @@ -47,7 +47,6 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta) _attributedText = mas; _editableTextNode.attributedText = _attributedText; - } #pragma mark - ASEditableTextNode @@ -55,10 +54,89 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta) - (void)testAllocASEditableTextNode { ASEditableTextNode *node = [[ASEditableTextNode alloc] init]; - XCTAssertTrue([[node class] isSubclassOfClass:[ASEditableTextNode class]], @"ASTextNode alloc should return an instance of ASTextNode, instead returned %@", [node class]); + XCTAssertTrue([[node class] isSubclassOfClass:[ASEditableTextNode class]], @"ASEditableTextNode alloc should return an instance of ASEditableTextNode, instead returned %@", [node class]); } -#pragma mark - ASEditableTextNode +#pragma mark - ASEditableTextNode Tests + +- (void)testUITextInputTraitDefaults +{ + ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init]; + + XCTAssertTrue(editableTextNode.autocapitalizationType == UITextAutocapitalizationTypeSentences, @"_ASTextInputTraitsPendingState's autocapitalizationType default should be UITextAutocapitalizationTypeSentences."); + XCTAssertTrue(editableTextNode.autocorrectionType == UITextAutocorrectionTypeDefault, @"_ASTextInputTraitsPendingState's autocorrectionType default should be UITextAutocorrectionTypeDefault."); + XCTAssertTrue(editableTextNode.spellCheckingType == UITextSpellCheckingTypeDefault, @"_ASTextInputTraitsPendingState's spellCheckingType default should be UITextSpellCheckingTypeDefault."); + XCTAssertTrue(editableTextNode.keyboardType == UIKeyboardTypeDefault, @"_ASTextInputTraitsPendingState's keyboardType default should be UIKeyboardTypeDefault."); + XCTAssertTrue(editableTextNode.keyboardAppearance == UIKeyboardAppearanceDefault, @"_ASTextInputTraitsPendingState's keyboardAppearance default should be UIKeyboardAppearanceDefault."); + XCTAssertTrue(editableTextNode.returnKeyType == UIReturnKeyDefault, @"_ASTextInputTraitsPendingState's returnKeyType default should be UIReturnKeyDefault."); + XCTAssertTrue(editableTextNode.enablesReturnKeyAutomatically == NO, @"_ASTextInputTraitsPendingState's enablesReturnKeyAutomatically default should be NO."); + XCTAssertTrue(editableTextNode.isSecureTextEntry == NO, @"_ASTextInputTraitsPendingState's isSecureTextEntry default should be NO."); + + XCTAssertTrue(editableTextNode.textView.autocapitalizationType == UITextAutocapitalizationTypeSentences, @"textView's autocapitalizationType default should be UITextAutocapitalizationTypeSentences."); + XCTAssertTrue(editableTextNode.textView.autocorrectionType == UITextAutocorrectionTypeDefault, @"textView's autocorrectionType default should be UITextAutocorrectionTypeDefault."); + XCTAssertTrue(editableTextNode.textView.spellCheckingType == UITextSpellCheckingTypeDefault, @"textView's spellCheckingType default should be UITextSpellCheckingTypeDefault."); + XCTAssertTrue(editableTextNode.textView.keyboardType == UIKeyboardTypeDefault, @"textView's keyboardType default should be UIKeyboardTypeDefault."); + XCTAssertTrue(editableTextNode.textView.keyboardAppearance == UIKeyboardAppearanceDefault, @"textView's keyboardAppearance default should be UIKeyboardAppearanceDefault."); + XCTAssertTrue(editableTextNode.textView.returnKeyType == UIReturnKeyDefault, @"textView's returnKeyType default should be UIReturnKeyDefault."); + XCTAssertTrue(editableTextNode.textView.enablesReturnKeyAutomatically == NO, @"textView's enablesReturnKeyAutomatically default should be NO."); + XCTAssertTrue(editableTextNode.textView.isSecureTextEntry == NO, @"textView's isSecureTextEntry default should be NO."); +} + +- (void)testUITextInputTraitsSetTraitsBeforeViewLoaded +{ + // UITextView ignores any values set on the first 3 properties below if secureTextEntry is enabled. + // Because of this UIKit behavior, we'll test secure entry seperately + ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init]; + + editableTextNode.autocapitalizationType = UITextAutocapitalizationTypeWords; + editableTextNode.autocorrectionType = UITextAutocorrectionTypeYes; + editableTextNode.spellCheckingType = UITextSpellCheckingTypeYes; + editableTextNode.keyboardType = UIKeyboardTypeTwitter; + editableTextNode.keyboardAppearance = UIKeyboardAppearanceDark; + editableTextNode.returnKeyType = UIReturnKeyGo; + editableTextNode.enablesReturnKeyAutomatically = YES; + + XCTAssertTrue(editableTextNode.textView.autocapitalizationType == UITextAutocapitalizationTypeWords, @"textView's autocapitalizationType should be UITextAutocapitalizationTypeAllCharacters."); + XCTAssertTrue(editableTextNode.textView.autocorrectionType == UITextAutocorrectionTypeYes, @"textView's autocorrectionType should be UITextAutocorrectionTypeYes."); + XCTAssertTrue(editableTextNode.textView.spellCheckingType == UITextSpellCheckingTypeYes, @"textView's spellCheckingType should be UITextSpellCheckingTypeYes."); + XCTAssertTrue(editableTextNode.textView.keyboardType == UIKeyboardTypeTwitter, @"textView's keyboardType should be UIKeyboardTypeTwitter."); + XCTAssertTrue(editableTextNode.textView.keyboardAppearance == UIKeyboardAppearanceDark, @"textView's keyboardAppearance should be UIKeyboardAppearanceDark."); + XCTAssertTrue(editableTextNode.textView.returnKeyType == UIReturnKeyGo, @"textView's returnKeyType should be UIReturnKeyGo."); + XCTAssertTrue(editableTextNode.textView.enablesReturnKeyAutomatically == YES, @"textView's enablesReturnKeyAutomatically should be YES."); + + ASEditableTextNode *secureEditableTextNode = [[ASEditableTextNode alloc] init]; + secureEditableTextNode.secureTextEntry = YES; + + XCTAssertTrue(secureEditableTextNode.textView.secureTextEntry == YES, @"textView's isSecureTextEntry should be YES."); +} + +- (void)testUITextInputTraitsChangeTraitAfterViewLoaded +{ + // UITextView ignores any values set on the first 3 properties below if secureTextEntry is enabled. + // Because of this UIKit behavior, we'll test secure entry seperately + ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init]; + + editableTextNode.textView.autocapitalizationType = UITextAutocapitalizationTypeWords; + editableTextNode.textView.autocorrectionType = UITextAutocorrectionTypeYes; + editableTextNode.textView.spellCheckingType = UITextSpellCheckingTypeYes; + editableTextNode.textView.keyboardType = UIKeyboardTypeTwitter; + editableTextNode.textView.keyboardAppearance = UIKeyboardAppearanceDark; + editableTextNode.textView.returnKeyType = UIReturnKeyGo; + editableTextNode.textView.enablesReturnKeyAutomatically = YES; + + XCTAssertTrue(editableTextNode.textView.autocapitalizationType == UITextAutocapitalizationTypeWords, @"textView's autocapitalizationType should be UITextAutocapitalizationTypeAllCharacters."); + XCTAssertTrue(editableTextNode.textView.autocorrectionType == UITextAutocorrectionTypeYes, @"textView's autocorrectionType should be UITextAutocorrectionTypeYes."); + XCTAssertTrue(editableTextNode.textView.spellCheckingType == UITextSpellCheckingTypeYes, @"textView's spellCheckingType should be UITextSpellCheckingTypeYes."); + XCTAssertTrue(editableTextNode.textView.keyboardType == UIKeyboardTypeTwitter, @"textView's keyboardType should be UIKeyboardTypeTwitter."); + XCTAssertTrue(editableTextNode.textView.keyboardAppearance == UIKeyboardAppearanceDark, @"textView's keyboardAppearance should be UIKeyboardAppearanceDark."); + XCTAssertTrue(editableTextNode.textView.returnKeyType == UIReturnKeyGo, @"textView's returnKeyType should be UIReturnKeyGo."); + XCTAssertTrue(editableTextNode.textView.enablesReturnKeyAutomatically == YES, @"textView's enablesReturnKeyAutomatically should be YES."); + + ASEditableTextNode *secureEditableTextNode = [[ASEditableTextNode alloc] init]; + secureEditableTextNode.textView.secureTextEntry = YES; + + XCTAssertTrue(secureEditableTextNode.textView.secureTextEntry == YES, @"textView's isSecureTextEntry should be YES."); +} - (void)testSetPreferredFrameSize { @@ -66,8 +144,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta) _editableTextNode.preferredFrameSize = preferredFrameSize; CGSize calculatedSize = [_editableTextNode measure:CGSizeZero]; - XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated width (%f) should be equal than preferred width (%f)", calculatedSize.width, preferredFrameSize.width); - XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated height (%f) should be equal than preferred height (%f)", calculatedSize.width, preferredFrameSize.width); + XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated width (%f) should be equal to preferred width (%f)", calculatedSize.width, preferredFrameSize.width); + XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated height (%f) should be equal to preferred height (%f)", calculatedSize.width, preferredFrameSize.width); _editableTextNode.preferredFrameSize = CGSizeZero; }