| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/browser/ui/util/label_observer.h" | 5 #import "ios/chrome/browser/ui/util/label_observer.h" |
| 6 | 6 |
| 7 #import <objc/runtime.h> | 7 #import <objc/runtime.h> |
| 8 | 8 |
| 9 #import "base/ios/weak_nsobject.h" | 9 #import "base/ios/weak_nsobject.h" |
| 10 #import "base/mac/scoped_block.h" | 10 #import "base/mac/scoped_block.h" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 base::scoped_nsobject<NSMutableArray> _styleActions; | 31 base::scoped_nsobject<NSMutableArray> _styleActions; |
| 32 base::scoped_nsobject<NSMutableArray> _layoutActions; | 32 base::scoped_nsobject<NSMutableArray> _layoutActions; |
| 33 base::scoped_nsobject<NSMutableArray> _textActions; | 33 base::scoped_nsobject<NSMutableArray> _textActions; |
| 34 } | 34 } |
| 35 | 35 |
| 36 // Whether or not observer actions are currently being executed. This is used | 36 // Whether or not observer actions are currently being executed. This is used |
| 37 // to prevent infinite loops caused by a LinkObserverAction updating a | 37 // to prevent infinite loops caused by a LinkObserverAction updating a |
| 38 // property on |_label|. | 38 // property on |_label|. |
| 39 @property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO; | 39 @property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO; |
| 40 | 40 |
| 41 // The number of times this observer has been asked to observe the label. When |
| 42 // reaching zero, the label stops being observed. |
| 43 @property(nonatomic, assign) NSInteger observingCount; |
| 44 |
| 41 // Initializes a LabelObserver that observes |label|. | 45 // Initializes a LabelObserver that observes |label|. |
| 42 - (instancetype)initWithLabel:(UILabel*)label NS_DESIGNATED_INITIALIZER; | 46 - (instancetype)initWithLabel:(UILabel*)label NS_DESIGNATED_INITIALIZER; |
| 43 | 47 |
| 48 // Adds |self| as observer for the |_label|. |
| 49 - (void)registerAsObserver; |
| 50 |
| 51 // Removes |self| as observer for the |_label|. |
| 52 - (void)removeObserver; |
| 53 |
| 44 // Performs all LabelObserverActions in |actions|. | 54 // Performs all LabelObserverActions in |actions|. |
| 45 - (void)performActions:(NSArray*)actions; | 55 - (void)performActions:(NSArray*)actions; |
| 46 | 56 |
| 47 // Takes |_label|'s values for each key from |styleKeys| and uses them to | 57 // Takes |_label|'s values for each key from |styleKeys| and uses them to |
| 48 // construct a uniformly attributed value to use for |_label|'s attributedText. | 58 // construct a uniformly attributed value to use for |_label|'s attributedText. |
| 49 - (void)resetLabelAttributes; | 59 - (void)resetLabelAttributes; |
| 50 | 60 |
| 51 @end | 61 @end |
| 52 | 62 |
| 53 // Properties of UILabel that, when changed, will cause the label's attributed | 63 // Properties of UILabel that, when changed, will cause the label's attributed |
| 54 // text to change. | 64 // text to change. |
| 55 static NSSet* styleKeys; | 65 static NSSet* styleKeys; |
| 56 // Properties of UILabel that invalidate the layout of the label if they change. | 66 // Properties of UILabel that invalidate the layout of the label if they change. |
| 57 static NSSet* layoutKeys; | 67 static NSSet* layoutKeys; |
| 58 // Properties of UILabel that may invalidate the text of the label if they | 68 // Properties of UILabel that may invalidate the text of the label if they |
| 59 // change. | 69 // change. |
| 60 static NSSet* textKeys; | 70 static NSSet* textKeys; |
| 61 | 71 |
| 62 @implementation LabelObserver | 72 @implementation LabelObserver |
| 63 | 73 |
| 64 @synthesize respondingToKVO = _respondingToKVO; | 74 @synthesize respondingToKVO = _respondingToKVO; |
| 75 @synthesize observingCount = _observingCount; |
| 65 | 76 |
| 66 + (void)initialize { | 77 + (void)initialize { |
| 67 if (self == [LabelObserver class]) { | 78 if (self == [LabelObserver class]) { |
| 68 styleKeys = [[NSSet alloc] initWithArray:@[ | 79 styleKeys = [[NSSet alloc] initWithArray:@[ |
| 69 @"font", @"textColor", @"textAlignment", @"lineBreakMode", @"shadowColor", | 80 @"font", @"textColor", @"textAlignment", @"lineBreakMode", @"shadowColor", |
| 70 @"shadowOffset" | 81 @"shadowOffset" |
| 71 ]]; | 82 ]]; |
| 72 layoutKeys = [[NSSet alloc] | 83 layoutKeys = [[NSSet alloc] |
| 73 initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]]; | 84 initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]]; |
| 74 textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]]; | 85 textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]]; |
| 75 } | 86 } |
| 76 } | 87 } |
| 77 | 88 |
| 78 - (instancetype)initWithLabel:(UILabel*)label { | 89 - (instancetype)initWithLabel:(UILabel*)label { |
| 79 if ((self = [super init])) { | 90 if ((self = [super init])) { |
| 80 DCHECK(label); | 91 DCHECK(label); |
| 81 _label.reset(label); | 92 _label.reset(label); |
| 82 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { | |
| 83 for (NSString* key in keySet) { | |
| 84 [_label addObserver:self | |
| 85 forKeyPath:key | |
| 86 options:NSKeyValueObservingOptionNew | |
| 87 context:nullptr]; | |
| 88 } | |
| 89 } | |
| 90 [self resetLabelAttributes]; | 93 [self resetLabelAttributes]; |
| 91 } | 94 } |
| 92 return self; | 95 return self; |
| 93 } | 96 } |
| 94 | 97 |
| 95 - (void)dealloc { | 98 - (void)dealloc { |
| 96 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { | 99 objc_setAssociatedObject(_label, kLabelObserverKey, nil, |
| 97 for (NSString* key in keySet) { | 100 OBJC_ASSOCIATION_ASSIGN); |
| 98 [_label removeObserver:self forKeyPath:key]; | |
| 99 } | |
| 100 } | |
| 101 [super dealloc]; | 101 [super dealloc]; |
| 102 } | 102 } |
| 103 | 103 |
| 104 #pragma mark - Public interface | 104 #pragma mark - Public interface |
| 105 | 105 |
| 106 + (instancetype)observerForLabel:(UILabel*)label { | 106 + (instancetype)observerForLabel:(UILabel*)label { |
| 107 if (!label) | 107 if (!label) |
| 108 return nil; | 108 return nil; |
| 109 id observer = objc_getAssociatedObject(label, kLabelObserverKey); | 109 id observer = objc_getAssociatedObject(label, kLabelObserverKey); |
| 110 if (!observer) { | 110 if (!observer) { |
| 111 observer = [[LabelObserver alloc] initWithLabel:label]; | 111 observer = [[LabelObserver alloc] initWithLabel:label]; |
| 112 objc_setAssociatedObject(label, kLabelObserverKey, observer, | 112 objc_setAssociatedObject(label, kLabelObserverKey, observer, |
| 113 OBJC_ASSOCIATION_RETAIN_NONATOMIC); | 113 OBJC_ASSOCIATION_ASSIGN); |
| 114 [observer release]; | 114 [observer autorelease]; |
| 115 } | 115 } |
| 116 return observer; | 116 return observer; |
| 117 } | 117 } |
| 118 | 118 |
| 119 - (void)startObserving { |
| 120 if (self.observingCount == 0) { |
| 121 [self registerAsObserver]; |
| 122 } |
| 123 self.observingCount++; |
| 124 } |
| 125 |
| 126 - (void)stopObserving { |
| 127 if (self.observingCount == 0) |
| 128 return; |
| 129 |
| 130 self.observingCount--; |
| 131 if (self.observingCount == 0) { |
| 132 [self removeObserver]; |
| 133 } |
| 134 } |
| 135 |
| 119 - (void)addStyleChangedAction:(LabelObserverAction)action { | 136 - (void)addStyleChangedAction:(LabelObserverAction)action { |
| 120 DCHECK(action); | 137 DCHECK(action); |
| 121 if (!_styleActions) | 138 if (!_styleActions) |
| 122 _styleActions.reset([[NSMutableArray alloc] init]); | 139 _styleActions.reset([[NSMutableArray alloc] init]); |
| 123 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 140 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 124 [_styleActions addObject:actionCopy]; | 141 [_styleActions addObject:actionCopy]; |
| 125 } | 142 } |
| 126 | 143 |
| 127 - (void)addLayoutChangedAction:(LabelObserverAction)action { | 144 - (void)addLayoutChangedAction:(LabelObserverAction)action { |
| 128 DCHECK(action); | 145 DCHECK(action); |
| 129 if (!_layoutActions) | 146 if (!_layoutActions) |
| 130 _layoutActions.reset([[NSMutableArray alloc] init]); | 147 _layoutActions.reset([[NSMutableArray alloc] init]); |
| 131 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 148 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 132 [_layoutActions addObject:actionCopy]; | 149 [_layoutActions addObject:actionCopy]; |
| 133 } | 150 } |
| 134 | 151 |
| 135 - (void)addTextChangedAction:(LabelObserverAction)action { | 152 - (void)addTextChangedAction:(LabelObserverAction)action { |
| 136 DCHECK(action); | 153 DCHECK(action); |
| 137 if (!_textActions) | 154 if (!_textActions) |
| 138 _textActions.reset([[NSMutableArray alloc] init]); | 155 _textActions.reset([[NSMutableArray alloc] init]); |
| 139 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 156 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 140 [_textActions addObject:actionCopy]; | 157 [_textActions addObject:actionCopy]; |
| 141 } | 158 } |
| 142 | 159 |
| 143 #pragma mark - | 160 #pragma mark - |
| 144 | 161 |
| 162 - (void)registerAsObserver { |
| 163 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { |
| 164 for (NSString* key in keySet) { |
| 165 [_label addObserver:self |
| 166 forKeyPath:key |
| 167 options:NSKeyValueObservingOptionNew |
| 168 context:nullptr]; |
| 169 } |
| 170 } |
| 171 } |
| 172 |
| 173 - (void)removeObserver { |
| 174 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { |
| 175 for (NSString* key in keySet) { |
| 176 [_label removeObserver:self forKeyPath:key]; |
| 177 } |
| 178 }; |
| 179 } |
| 180 |
| 145 - (void)performActions:(NSArray*)actions { | 181 - (void)performActions:(NSArray*)actions { |
| 146 for (LabelObserverAction action in actions) | 182 for (LabelObserverAction action in actions) |
| 147 action(_label); | 183 action(_label); |
| 148 } | 184 } |
| 149 | 185 |
| 150 - (void)resetLabelAttributes { | 186 - (void)resetLabelAttributes { |
| 151 if ([_label attributedText] || ![_label text]) | 187 if ([_label attributedText] || ![_label text]) |
| 152 return; | 188 return; |
| 153 NSMutableDictionary* labelStyle = | 189 NSMutableDictionary* labelStyle = |
| 154 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; | 190 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 180 [self resetLabelAttributes]; | 216 [self resetLabelAttributes]; |
| 181 [self performActions:_textActions]; | 217 [self performActions:_textActions]; |
| 182 } else { | 218 } else { |
| 183 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) | 219 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) |
| 184 << "> observed"; | 220 << "> observed"; |
| 185 } | 221 } |
| 186 self.respondingToKVO = NO; | 222 self.respondingToKVO = NO; |
| 187 } | 223 } |
| 188 | 224 |
| 189 @end | 225 @end |
| OLD | NEW |