Chromium Code Reviews| 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 if (self.observingCount-- == 1) { | |
|
stkhapugin
2017/04/20 15:51:34
Please rewrite the condition similar to line 120.
gambard
2017/04/21 07:20:40
Done.
| |
| 131 [self removeObserver]; | |
| 132 } | |
| 133 } | |
| 134 | |
| 119 - (void)addStyleChangedAction:(LabelObserverAction)action { | 135 - (void)addStyleChangedAction:(LabelObserverAction)action { |
| 120 DCHECK(action); | 136 DCHECK(action); |
| 121 if (!_styleActions) | 137 if (!_styleActions) |
| 122 _styleActions.reset([[NSMutableArray alloc] init]); | 138 _styleActions.reset([[NSMutableArray alloc] init]); |
| 123 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 139 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 124 [_styleActions addObject:actionCopy]; | 140 [_styleActions addObject:actionCopy]; |
| 125 } | 141 } |
| 126 | 142 |
| 127 - (void)addLayoutChangedAction:(LabelObserverAction)action { | 143 - (void)addLayoutChangedAction:(LabelObserverAction)action { |
| 128 DCHECK(action); | 144 DCHECK(action); |
| 129 if (!_layoutActions) | 145 if (!_layoutActions) |
| 130 _layoutActions.reset([[NSMutableArray alloc] init]); | 146 _layoutActions.reset([[NSMutableArray alloc] init]); |
| 131 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 147 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 132 [_layoutActions addObject:actionCopy]; | 148 [_layoutActions addObject:actionCopy]; |
| 133 } | 149 } |
| 134 | 150 |
| 135 - (void)addTextChangedAction:(LabelObserverAction)action { | 151 - (void)addTextChangedAction:(LabelObserverAction)action { |
| 136 DCHECK(action); | 152 DCHECK(action); |
| 137 if (!_textActions) | 153 if (!_textActions) |
| 138 _textActions.reset([[NSMutableArray alloc] init]); | 154 _textActions.reset([[NSMutableArray alloc] init]); |
| 139 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 155 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); |
| 140 [_textActions addObject:actionCopy]; | 156 [_textActions addObject:actionCopy]; |
| 141 } | 157 } |
| 142 | 158 |
| 143 #pragma mark - | 159 #pragma mark - |
| 144 | 160 |
| 161 - (void)registerAsObserver { | |
| 162 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { | |
| 163 for (NSString* key in keySet) { | |
| 164 [_label addObserver:self | |
| 165 forKeyPath:key | |
| 166 options:NSKeyValueObservingOptionNew | |
| 167 context:nullptr]; | |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 - (void)removeObserver { | |
| 173 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { | |
| 174 for (NSString* key in keySet) { | |
| 175 [_label removeObserver:self forKeyPath:key]; | |
| 176 } | |
| 177 }; | |
| 178 } | |
| 179 | |
| 145 - (void)performActions:(NSArray*)actions { | 180 - (void)performActions:(NSArray*)actions { |
| 146 for (LabelObserverAction action in actions) | 181 for (LabelObserverAction action in actions) |
| 147 action(_label); | 182 action(_label); |
| 148 } | 183 } |
| 149 | 184 |
| 150 - (void)resetLabelAttributes { | 185 - (void)resetLabelAttributes { |
| 151 if ([_label attributedText] || ![_label text]) | 186 if ([_label attributedText] || ![_label text]) |
| 152 return; | 187 return; |
| 153 NSMutableDictionary* labelStyle = | 188 NSMutableDictionary* labelStyle = |
| 154 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; | 189 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 180 [self resetLabelAttributes]; | 215 [self resetLabelAttributes]; |
| 181 [self performActions:_textActions]; | 216 [self performActions:_textActions]; |
| 182 } else { | 217 } else { |
| 183 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) | 218 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) |
| 184 << "> observed"; | 219 << "> observed"; |
| 185 } | 220 } |
| 186 self.respondingToKVO = NO; | 221 self.respondingToKVO = NO; |
| 187 } | 222 } |
| 188 | 223 |
| 189 @end | 224 @end |
| OLD | NEW |