From b854bd8a187c60c8a2749a75b3c988167a74f2bd Mon Sep 17 00:00:00 2001 From: dourgulf Date: Thu, 10 Nov 2016 17:13:33 +0800 Subject: [PATCH 1/5] modify the KVO funtion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original KVO use runtime to hook UITextView’s dealloc method and release. This method maybe cause a problem: When this category was compile into a dynamic framework, and was used in other project which also reference this category. In that case, the runtime hook two times, and result is KVO not removed. --- Demo/Base.lproj/LaunchScreen.storyboard | 25 +++- Demo/Base.lproj/Main.storyboard | 78 ++++++++++- Demo/ViewController.m | 31 ++++- Sources/UITextView+Placeholder.m | 126 ++++++++++++------ Tests/Tests.swift | 20 +-- Tests/Utils.swift | 10 +- .../project.pbxproj | 8 ++ 7 files changed, 219 insertions(+), 79 deletions(-) diff --git a/Demo/Base.lproj/LaunchScreen.storyboard b/Demo/Base.lproj/LaunchScreen.storyboard index 2e721e1..f22440f 100644 --- a/Demo/Base.lproj/LaunchScreen.storyboard +++ b/Demo/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,12 @@ - - + + + + + - + + + @@ -13,15 +18,21 @@ - + - - + + + + - + diff --git a/Demo/Base.lproj/Main.storyboard b/Demo/Base.lproj/Main.storyboard index f56d2f3..e811923 100644 --- a/Demo/Base.lproj/Main.storyboard +++ b/Demo/Base.lproj/Main.storyboard @@ -1,25 +1,91 @@ - - + + + + + - + + + + - + - + - + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/ViewController.m b/Demo/ViewController.m index 7c6ae92..f70eddd 100644 --- a/Demo/ViewController.m +++ b/Demo/ViewController.m @@ -23,21 +23,38 @@ #import "ViewController.h" #import +@interface ViewController() + +@property (weak, nonatomic) IBOutlet UITextView *textView; + + +@end + @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; - UITextView *textView = [[UITextView alloc] init]; - textView.frame = CGRectMake(0, 20, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds)); - textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - textView.placeholder = @"Are you sure you don\'t want to reconsider? Could you tell us why you wish to leave StyleShare? Your opinion helps us improve StyleShare into a better place for fashionistas from all around the world. We are always listening to our users. Help us improve!"; + self.textView.placeholder = @"Are you sure you don\'t want to reconsider? Could you tell us why you wish to leave StyleShare? Your opinion helps us improve StyleShare into a better place for fashionistas from all around the world. We are always listening to our users. Help us improve!"; + self.textView.placeholderLabel.textAlignment = NSTextAlignmentCenter; NSDictionary *attrs = @ { NSFontAttributeName: [UIFont boldSystemFontOfSize:20], }; - textView.attributedText = [[NSAttributedString alloc] initWithString:@"Hi" attributes:attrs]; - textView.font = [UIFont systemFontOfSize:15]; - [self.view addSubview:textView]; + self.textView.attributedText = [[NSAttributedString alloc] initWithString:@"Hi" attributes:attrs]; + self.textView.font = [UIFont systemFontOfSize:15]; + [self.view addSubview:self.textView]; +} + +- (IBAction)onTextRemove:(id)sender { + [self.textView removeFromSuperview]; +} + +- (IBAction)onChangeFont:(id)sender { + self.textView.font = [UIFont systemFontOfSize:30]; +} + +- (IBAction)onChangeColor:(id)sender { + self.textView.textColor = [UIColor redColor]; } @end diff --git a/Sources/UITextView+Placeholder.m b/Sources/UITextView+Placeholder.m index 276a0bf..c9743ac 100644 --- a/Sources/UITextView+Placeholder.m +++ b/Sources/UITextView+Placeholder.m @@ -23,32 +23,89 @@ #import #import "UITextView+Placeholder.h" -@implementation UITextView (Placeholder) +#pragma mark - `observingKeys` -#pragma mark - Swizzle Dealloc +@interface DWTextViewEventObserver : NSObject + +@property (copy, nonatomic) void (^execution)(BOOL fontChanged); +// can't use weak, because weak before UITextView object dealloc, it will set all weak reference to nil +// it will make our removeObserver failed to remove. +@property (unsafe_unretained, nonatomic) UITextView *target; + +@end + +@implementation DWTextViewEventObserver + ++ (void)observe:(UITextView *)target then:(void (^)(BOOL fontChanged))exec { + DWTextViewEventObserver *monitor = [[DWTextViewEventObserver alloc] init]; + monitor.execution = exec; + monitor.target = target; + [monitor observeEvents]; + int randomKey; + // It is true that swizzle method of dealloc in NSObject Category can do the same thing, but that will cause method polluted! + objc_setAssociatedObject(target, &randomKey, monitor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - LifeCycle ++ (NSArray *)observingKeys { + return @[@"attributedText", + @"bounds", + @"font", + @"frame", + @"text", + @"textAlignment", + @"textContainerInset"]; +} -+ (void)load { - // is this the best solution? - method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")), - class_getInstanceMethod(self.class, @selector(swizzledDealloc))); +- (void)dealloc { + [self releaseObserveKeys]; +} + +- (void)observeEvents { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updatePlaceholderLabel) + name:UITextViewTextDidChangeNotification + object:self.target]; + + for (NSString *key in self.class.observingKeys) { + [self.target addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil]; + } +} + +- (void)updatePlaceholderLabel { + if (self.execution) { + self.execution(NO); + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (self.execution) { + BOOL updateFont = [keyPath isEqualToString:@"font"] && change[NSKeyValueChangeNewKey] != nil; + self.execution(updateFont); + } } -- (void)swizzledDealloc { +- (void)releaseObserveKeys { [[NSNotificationCenter defaultCenter] removeObserver:self]; - UILabel *label = objc_getAssociatedObject(self, @selector(placeholderLabel)); - if (label) { - for (NSString *key in self.class.observingKeys) { - @try { - [self removeObserver:self forKeyPath:key]; - } - @catch (NSException *exception) { - // Do nothing - } + for (NSString *key in self.class.observingKeys) { + @try { + [self.target removeObserver:self forKeyPath:key context:nil]; + } + @catch (NSException *exception) { + // Do nothing } } - [self swizzledDealloc]; } +@end + +@implementation UITextView (Placeholder) + +#pragma mark - Swizzle Dealloc #pragma mark - Class Methods #pragma mark `defaultPlaceholderColor` @@ -64,9 +121,6 @@ + (UIColor *)defaultPlaceholderColor { return color; } - -#pragma mark - `observingKeys` - + (NSArray *)observingKeys { return @[@"attributedText", @"bounds", @@ -77,7 +131,6 @@ + (NSArray *)observingKeys { @"textContainerInset"]; } - #pragma mark - Properties #pragma mark `placeholderLabel` @@ -98,14 +151,13 @@ - (UILabel *)placeholderLabel { [self updatePlaceholderLabel]; self.needsUpdateFont = NO; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(updatePlaceholderLabel) - name:UITextViewTextDidChangeNotification - object:self]; - - for (NSString *key in self.class.observingKeys) { - [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil]; - } + __weak typeof(self) weakSelf = self; + [DWTextViewEventObserver observe:self then:^(BOOL fontChanged) { + if (fontChanged) { + weakSelf.needsUpdateFont = fontChanged; + } + [weakSelf updatePlaceholderLabel]; + }]; } return label; } @@ -141,7 +193,6 @@ - (void)setPlaceholderColor:(UIColor *)placeholderColor { self.placeholderLabel.textColor = placeholderColor; } - #pragma mark `needsUpdateFont` - (BOOL)needsUpdateFont { @@ -153,21 +204,7 @@ - (void)setNeedsUpdateFont:(BOOL)needsUpdate { } -#pragma mark - KVO - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context { - if ([keyPath isEqualToString:@"font"]) { - self.needsUpdateFont = (change[NSKeyValueChangeNewKey] != nil); - } - [self updatePlaceholderLabel]; -} - - #pragma mark - Update - - (void)updatePlaceholderLabel { if (self.text.length) { [self.placeholderLabel removeFromSuperview]; @@ -180,6 +217,7 @@ - (void)updatePlaceholderLabel { self.placeholderLabel.font = self.font; self.needsUpdateFont = NO; } + self.placeholderLabel.textAlignment = self.textAlignment; // `NSTextContainer` is available since iOS 7 diff --git a/Tests/Tests.swift b/Tests/Tests.swift index 2ae6d46..e9506e4 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -45,7 +45,7 @@ class Tests: XCTestCase { } func testAttributedPlaceholder() { - let attributedPlaceholder = attributedString("Hello", .Bold(26)) + let attributedPlaceholder = attributedString("Hello", .bold(26)) self.textView.attributedPlaceholder = attributedPlaceholder XCTAssertEqual(self.textView.attributedPlaceholder, attributedPlaceholder) } @@ -54,31 +54,31 @@ class Tests: XCTestCase { // MARK: Fonts func testSetFont_beforePlaceholder() { - self.textView.font = UIFont.systemFontOfSize(34) + self.textView.font = UIFont.systemFont(ofSize: 34) self.textView.placeholder = "Hello" XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") - XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFontOfSize(34)) + XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFont(ofSize: 34)) } func testSetFont_afterPlaceholder() { self.textView.placeholder = "Hello" - self.textView.font = UIFont.systemFontOfSize(34) + self.textView.font = UIFont.systemFont(ofSize: 34) XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") - XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFontOfSize(34)) + XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFont(ofSize: 34)) } func testSetFont_beforeAttributedPlaceholder() { - let attributedPlaceholder = attributedString("Hello", .Bold(26)) - self.textView.font = UIFont.systemFontOfSize(34) + let attributedPlaceholder = attributedString("Hello", .bold(26)) + self.textView.font = UIFont.systemFont(ofSize: 34) self.textView.attributedPlaceholder = attributedPlaceholder XCTAssertEqual(self.textView.attributedPlaceholder, attributedPlaceholder) } func testSetFont_afterAttributedPlaceholderFont() { - let attributedPlaceholder = attributedString("Hello", .Bold(26)) + let attributedPlaceholder = attributedString("Hello", .bold(26)) self.textView.attributedPlaceholder = attributedPlaceholder - self.textView.font = UIFont.systemFontOfSize(34) - XCTAssertEqual(self.textView.attributedPlaceholder, attributedString("Hello", .Normal(34))) + self.textView.font = UIFont.systemFont(ofSize: 34) + XCTAssertEqual(self.textView.attributedPlaceholder, attributedString("Hello", .normal(34))) } } diff --git a/Tests/Utils.swift b/Tests/Utils.swift index c94a841..7eedd06 100644 --- a/Tests/Utils.swift +++ b/Tests/Utils.swift @@ -23,18 +23,18 @@ import UIKit enum Font { - case Normal(CGFloat) - case Bold(CGFloat) + case normal(CGFloat) + case bold(CGFloat) var font: UIFont { switch self { - case .Normal(let size): return UIFont.systemFontOfSize(size) - case .Bold(let size): return UIFont.boldSystemFontOfSize(size) + case .normal(let size): return UIFont.systemFont(ofSize: size) + case .bold(let size): return UIFont.boldSystemFont(ofSize: size) } } } -func attributedString(string: String, _ font: Font) -> NSAttributedString { +func attributedString(_ string: String, _ font: Font) -> NSAttributedString { let attributes = [NSFontAttributeName: font.font] return NSAttributedString(string: string, attributes: attributes) } diff --git a/UITextView+Placeholder.xcodeproj/project.pbxproj b/UITextView+Placeholder.xcodeproj/project.pbxproj index 776a783..aae6677 100644 --- a/UITextView+Placeholder.xcodeproj/project.pbxproj +++ b/UITextView+Placeholder.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 0309F8F81CBCCD6500062D3B /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0309F8F71CBCCD6500062D3B /* Tests.swift */; }; 0309F9001CBCCD8D00062D3B /* UITextView_Placeholder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0309F8AD1CBCC7FF00062D3B /* UITextView_Placeholder.framework */; }; 0309F9041CBCEE4700062D3B /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0309F9031CBCEE4700062D3B /* Utils.swift */; }; + D0F91DA81DD4711600DC3CFB /* UITextView+Placeholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0309F8BC1CBCC84400062D3B /* UITextView+Placeholder.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -252,12 +253,14 @@ TargetAttributes = { 0309F8AC1CBCC7FF00062D3B = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0810; }; 0309F8C51CBCC8A700062D3B = { CreatedOnToolsVersion = 7.3; }; 0309F8F41CBCCD6500062D3B = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0810; }; }; }; @@ -324,6 +327,7 @@ 0309F8D01CBCC8A700062D3B /* ViewController.m in Sources */, 0309F8CD1CBCC8A700062D3B /* AppDelegate.m in Sources */, 0309F8CA1CBCC8A700062D3B /* main.m in Sources */, + D0F91DA81DD4711600DC3CFB /* UITextView+Placeholder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -477,6 +481,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "kr.xoul.UITextView-Placeholder"; PRODUCT_NAME = UITextView_Placeholder; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -495,6 +500,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "kr.xoul.UITextView-Placeholder"; PRODUCT_NAME = UITextView_Placeholder; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -530,6 +536,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -541,6 +548,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; From e8d49c99b5c1a7843a2e9e6aedafee94de8f6883 Mon Sep 17 00:00:00 2001 From: dourgulf Date: Thu, 10 Nov 2016 17:56:26 +0800 Subject: [PATCH 2/5] restore test case code to swift 2.3 syntax, the configure the project file to use swift 2.3 --- Tests/Tests.swift | 46 +++++++++---------- Tests/Utils.swift | 13 +++--- .../project.pbxproj | 15 +++--- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Tests/Tests.swift b/Tests/Tests.swift index e9506e4..8d8adbc 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -19,66 +19,62 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. - import XCTest @testable import UITextView_Placeholder class Tests: XCTestCase { - + var textView: UITextView! - + // MARK: Setup - override func setUp() { super.setUp() self.textView = UITextView() } - - + + // MARK: Basic Tests - func testPlaceholder() { self.textView.placeholder = "Hello" XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") self.textView.placeholder = nil XCTAssertEqual(self.textView.placeholderLabel.text, nil) } - + func testAttributedPlaceholder() { - let attributedPlaceholder = attributedString("Hello", .bold(26)) + let attributedPlaceholder = attributedString("Hello", .Bold(26)) self.textView.attributedPlaceholder = attributedPlaceholder XCTAssertEqual(self.textView.attributedPlaceholder, attributedPlaceholder) } - - + + // MARK: Fonts - func testSetFont_beforePlaceholder() { - self.textView.font = UIFont.systemFont(ofSize: 34) + self.textView.font = UIFont.systemFontOfSize(34) self.textView.placeholder = "Hello" XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") - XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFont(ofSize: 34)) + XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFontOfSize(34)) } - + func testSetFont_afterPlaceholder() { self.textView.placeholder = "Hello" - self.textView.font = UIFont.systemFont(ofSize: 34) + self.textView.font = UIFont.systemFontOfSize(34) XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") - XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFont(ofSize: 34)) + XCTAssertEqual(self.textView.placeholderLabel.font, UIFont.systemFontOfSize(34)) } - + func testSetFont_beforeAttributedPlaceholder() { - let attributedPlaceholder = attributedString("Hello", .bold(26)) - self.textView.font = UIFont.systemFont(ofSize: 34) + let attributedPlaceholder = attributedString("Hello", .Bold(26)) + self.textView.font = UIFont.systemFontOfSize(34) self.textView.attributedPlaceholder = attributedPlaceholder XCTAssertEqual(self.textView.attributedPlaceholder, attributedPlaceholder) } - + func testSetFont_afterAttributedPlaceholderFont() { - let attributedPlaceholder = attributedString("Hello", .bold(26)) + let attributedPlaceholder = attributedString("Hello", .Bold(26)) self.textView.attributedPlaceholder = attributedPlaceholder - self.textView.font = UIFont.systemFont(ofSize: 34) - XCTAssertEqual(self.textView.attributedPlaceholder, attributedString("Hello", .normal(34))) + self.textView.font = UIFont.systemFontOfSize(34) + XCTAssertEqual(self.textView.attributedPlaceholder, attributedString("Hello", .Normal(34))) } - + } diff --git a/Tests/Utils.swift b/Tests/Utils.swift index 7eedd06..c9c63d1 100644 --- a/Tests/Utils.swift +++ b/Tests/Utils.swift @@ -19,22 +19,21 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. - import UIKit enum Font { - case normal(CGFloat) - case bold(CGFloat) - + case Normal(CGFloat) + case Bold(CGFloat) + var font: UIFont { switch self { - case .normal(let size): return UIFont.systemFont(ofSize: size) - case .bold(let size): return UIFont.boldSystemFont(ofSize: size) + case .Normal(let size): return UIFont.systemFontOfSize(size) + case .Bold(let size): return UIFont.boldSystemFontOfSize(size) } } } -func attributedString(_ string: String, _ font: Font) -> NSAttributedString { +func attributedString(string: String, _ font: Font) -> NSAttributedString { let attributes = [NSFontAttributeName: font.font] return NSAttributedString(string: string, attributes: attributes) } diff --git a/UITextView+Placeholder.xcodeproj/project.pbxproj b/UITextView+Placeholder.xcodeproj/project.pbxproj index aae6677..955e920 100644 --- a/UITextView+Placeholder.xcodeproj/project.pbxproj +++ b/UITextView+Placeholder.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 0309F8F81CBCCD6500062D3B /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0309F8F71CBCCD6500062D3B /* Tests.swift */; }; 0309F9001CBCCD8D00062D3B /* UITextView_Placeholder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0309F8AD1CBCC7FF00062D3B /* UITextView_Placeholder.framework */; }; 0309F9041CBCEE4700062D3B /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0309F9031CBCEE4700062D3B /* Utils.swift */; }; - D0F91DA81DD4711600DC3CFB /* UITextView+Placeholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0309F8BC1CBCC84400062D3B /* UITextView+Placeholder.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -261,6 +260,7 @@ 0309F8F41CBCCD6500062D3B = { CreatedOnToolsVersion = 7.3; LastSwiftMigration = 0810; + ProvisioningStyle = Automatic; }; }; }; @@ -327,7 +327,6 @@ 0309F8D01CBCC8A700062D3B /* ViewController.m in Sources */, 0309F8CD1CBCC8A700062D3B /* AppDelegate.m in Sources */, 0309F8CA1CBCC8A700062D3B /* main.m in Sources */, - D0F91DA81DD4711600DC3CFB /* UITextView+Placeholder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -481,7 +480,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "kr.xoul.UITextView-Placeholder"; PRODUCT_NAME = UITextView_Placeholder; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 2.3; }; name = Debug; }; @@ -500,7 +499,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "kr.xoul.UITextView-Placeholder"; PRODUCT_NAME = UITextView_Placeholder; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 2.3; }; name = Release; }; @@ -531,24 +530,28 @@ 0309F8FD1CBCCD6500062D3B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 2.3; }; name = Debug; }; 0309F8FE1CBCCD6500062D3B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = kr.xoul.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 2.3; }; name = Release; }; From bff40dde86926041b555f9bfe6e8c097b78fc767 Mon Sep 17 00:00:00 2001 From: dourgulf Date: Sat, 12 Nov 2016 10:10:05 +0800 Subject: [PATCH 3/5] remove unused observingKeys from UITextView (Placeholder) --- Sources/UITextView+Placeholder.m | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Sources/UITextView+Placeholder.m b/Sources/UITextView+Placeholder.m index c9743ac..8be400c 100644 --- a/Sources/UITextView+Placeholder.m +++ b/Sources/UITextView+Placeholder.m @@ -121,16 +121,6 @@ + (UIColor *)defaultPlaceholderColor { return color; } -+ (NSArray *)observingKeys { - return @[@"attributedText", - @"bounds", - @"font", - @"frame", - @"text", - @"textAlignment", - @"textContainerInset"]; -} - #pragma mark - Properties #pragma mark `placeholderLabel` From c40281e37caa7e1f81eadb35d5b845d6416a3706 Mon Sep 17 00:00:00 2001 From: dourgulf Date: Sat, 12 Nov 2016 11:07:35 +0800 Subject: [PATCH 4/5] update project setting to Xcode8; add a test case for release test --- Tests/Tests.swift | 1 + UITextView+Placeholder.xcodeproj/project.pbxproj | 13 ++++++++++--- .../xcshareddata/xcschemes/Demo.xcscheme | 2 +- .../xcschemes/UITextView+Placeholder.xcscheme | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Tests/Tests.swift b/Tests/Tests.swift index 8d8adbc..f17aee6 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -39,6 +39,7 @@ class Tests: XCTestCase { XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") self.textView.placeholder = nil XCTAssertEqual(self.textView.placeholderLabel.text, nil) + self.textView = nil; } func testAttributedPlaceholder() { diff --git a/UITextView+Placeholder.xcodeproj/project.pbxproj b/UITextView+Placeholder.xcodeproj/project.pbxproj index 955e920..3f27905 100644 --- a/UITextView+Placeholder.xcodeproj/project.pbxproj +++ b/UITextView+Placeholder.xcodeproj/project.pbxproj @@ -247,7 +247,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0810; ORGANIZATIONNAME = "Suyeol Jeon"; TargetAttributes = { 0309F8AC1CBCC7FF00062D3B = { @@ -388,8 +388,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -412,7 +414,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -437,8 +439,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -455,9 +459,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -468,6 +473,7 @@ 0309F8B61CBCC7FF00062D3B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -487,6 +493,7 @@ 0309F8B71CBCC7FF00062D3B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; diff --git a/UITextView+Placeholder.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme b/UITextView+Placeholder.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme index 835af73..bac4dca 100644 --- a/UITextView+Placeholder.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme +++ b/UITextView+Placeholder.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 12 Nov 2016 11:32:24 +0800 Subject: [PATCH 5/5] add some test case --- Tests/Tests.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/Tests.swift b/Tests/Tests.swift index f17aee6..f2678d0 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -42,6 +42,28 @@ class Tests: XCTestCase { self.textView = nil; } + func testRelease() { + self.textView.placeholder = "Hello" + XCTAssertEqual(self.textView.placeholderLabel.text, "Hello") + self.textView.placeholder = nil + XCTAssertEqual(self.textView.placeholderLabel.text, nil) + + self.textView = nil + } + + func testNotification() { + self.textView.text = "SomeText" + self.textView.placeholder = "Hello"; + XCTAssertNil(self.textView.placeholderLabel.superview); + self.textView.selectAll(nil); + self.textView .replaceRange(self.textView.selectedTextRange!, withText: ""); + XCTAssertNotNil(self.textView.placeholderLabel.superview); + + self.textView.selectAll(nil); + self.textView .replaceRange(self.textView.selectedTextRange!, withText: "OtherText"); + XCTAssertNil(self.textView.placeholderLabel.superview); + } + func testAttributedPlaceholder() { let attributedPlaceholder = attributedString("Hello", .Bold(26)) self.textView.attributedPlaceholder = attributedPlaceholder