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" | |
10 #import "base/mac/scoped_block.h" | |
11 #import "base/mac/scoped_nsobject.h" | |
12 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
13 | 10 |
| 11 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 12 #error "This file requires ARC support." |
| 13 #endif |
| 14 |
14 namespace { | 15 namespace { |
15 // The key under which LabelObservers are associated with their labels. | 16 // The key under which LabelObservers are associated with their labels. |
16 const void* const kLabelObserverKey = &kLabelObserverKey; | 17 const void* const kLabelObserverKey = &kLabelObserverKey; |
17 // Attempts to convert |value| to a string. | 18 // Attempts to convert |value| to a string. |
18 NSString* GetStringValue(id value) { | 19 NSString* GetStringValue(id value) { |
19 if ([value isKindOfClass:[NSString class]]) | 20 if ([value isKindOfClass:[NSString class]]) |
20 return static_cast<NSString*>(value); | 21 return static_cast<NSString*>(value); |
21 if ([value respondsToSelector:@selector(string)]) | 22 if ([value respondsToSelector:@selector(string)]) |
22 return [value performSelector:@selector(string)]; | 23 return [value performSelector:@selector(string)]; |
23 return nil; | 24 return nil; |
24 } | 25 } |
25 } | 26 } |
26 | 27 |
27 @interface LabelObserver () { | 28 @interface LabelObserver () { |
28 // The label being observed. | 29 // The label being observed. |
29 base::WeakNSObject<UILabel> _label; | 30 __weak UILabel* _label; |
30 // Arrays used to store registered actions. | 31 // Arrays used to store registered actions. |
31 base::scoped_nsobject<NSMutableArray> _styleActions; | 32 NSMutableArray* _styleActions; |
32 base::scoped_nsobject<NSMutableArray> _layoutActions; | 33 NSMutableArray* _layoutActions; |
33 base::scoped_nsobject<NSMutableArray> _textActions; | 34 NSMutableArray* _textActions; |
34 } | 35 } |
35 | 36 |
36 // Whether or not observer actions are currently being executed. This is used | 37 // Whether or not observer actions are currently being executed. This is used |
37 // to prevent infinite loops caused by a LinkObserverAction updating a | 38 // to prevent infinite loops caused by a LinkObserverAction updating a |
38 // property on |_label|. | 39 // property on |_label|. |
39 @property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO; | 40 @property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO; |
40 | 41 |
41 // The number of times this observer has been asked to observe the label. When | 42 // The number of times this observer has been asked to observe the label. When |
42 // reaching zero, the label stops being observed. | 43 // reaching zero, the label stops being observed. |
43 @property(nonatomic, assign) NSInteger observingCount; | 44 @property(nonatomic, assign) NSInteger observingCount; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 ]]; | 83 ]]; |
83 layoutKeys = [[NSSet alloc] | 84 layoutKeys = [[NSSet alloc] |
84 initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]]; | 85 initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]]; |
85 textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]]; | 86 textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]]; |
86 } | 87 } |
87 } | 88 } |
88 | 89 |
89 - (instancetype)initWithLabel:(UILabel*)label { | 90 - (instancetype)initWithLabel:(UILabel*)label { |
90 if ((self = [super init])) { | 91 if ((self = [super init])) { |
91 DCHECK(label); | 92 DCHECK(label); |
92 _label.reset(label); | 93 _label = label; |
93 [self resetLabelAttributes]; | 94 [self resetLabelAttributes]; |
94 } | 95 } |
95 return self; | 96 return self; |
96 } | 97 } |
97 | 98 |
98 - (void)dealloc { | 99 - (void)dealloc { |
99 objc_setAssociatedObject(_label, kLabelObserverKey, nil, | 100 objc_setAssociatedObject(_label, kLabelObserverKey, nil, |
100 OBJC_ASSOCIATION_ASSIGN); | 101 OBJC_ASSOCIATION_ASSIGN); |
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_ASSIGN); | 113 OBJC_ASSOCIATION_ASSIGN); |
114 [observer autorelease]; | |
115 } | 114 } |
116 return observer; | 115 return observer; |
117 } | 116 } |
118 | 117 |
119 - (void)startObserving { | 118 - (void)startObserving { |
120 if (self.observingCount == 0) { | 119 if (self.observingCount == 0) { |
121 [self registerAsObserver]; | 120 [self registerAsObserver]; |
122 } | 121 } |
123 self.observingCount++; | 122 self.observingCount++; |
124 } | 123 } |
125 | 124 |
126 - (void)stopObserving { | 125 - (void)stopObserving { |
127 if (self.observingCount == 0) | 126 if (self.observingCount == 0) |
128 return; | 127 return; |
129 | 128 |
130 self.observingCount--; | 129 self.observingCount--; |
131 if (self.observingCount == 0) { | 130 if (self.observingCount == 0) { |
132 [self removeObserver]; | 131 [self removeObserver]; |
133 } | 132 } |
134 } | 133 } |
135 | 134 |
136 - (void)addStyleChangedAction:(LabelObserverAction)action { | 135 - (void)addStyleChangedAction:(LabelObserverAction)action { |
137 DCHECK(action); | 136 DCHECK(action); |
138 if (!_styleActions) | 137 if (!_styleActions) |
139 _styleActions.reset([[NSMutableArray alloc] init]); | 138 _styleActions = [[NSMutableArray alloc] init]; |
140 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 139 LabelObserverAction actionCopy = [action copy]; |
141 [_styleActions addObject:actionCopy]; | 140 [_styleActions addObject:actionCopy]; |
142 } | 141 } |
143 | 142 |
144 - (void)addLayoutChangedAction:(LabelObserverAction)action { | 143 - (void)addLayoutChangedAction:(LabelObserverAction)action { |
145 DCHECK(action); | 144 DCHECK(action); |
146 if (!_layoutActions) | 145 if (!_layoutActions) |
147 _layoutActions.reset([[NSMutableArray alloc] init]); | 146 _layoutActions = [[NSMutableArray alloc] init]; |
148 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 147 LabelObserverAction actionCopy = [action copy]; |
149 [_layoutActions addObject:actionCopy]; | 148 [_layoutActions addObject:actionCopy]; |
150 } | 149 } |
151 | 150 |
152 - (void)addTextChangedAction:(LabelObserverAction)action { | 151 - (void)addTextChangedAction:(LabelObserverAction)action { |
153 DCHECK(action); | 152 DCHECK(action); |
154 if (!_textActions) | 153 if (!_textActions) |
155 _textActions.reset([[NSMutableArray alloc] init]); | 154 _textActions = [[NSMutableArray alloc] init]; |
156 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]); | 155 LabelObserverAction actionCopy = [action copy]; |
157 [_textActions addObject:actionCopy]; | 156 [_textActions addObject:actionCopy]; |
158 } | 157 } |
159 | 158 |
160 #pragma mark - | 159 #pragma mark - |
161 | 160 |
162 - (void)registerAsObserver { | 161 - (void)registerAsObserver { |
163 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { | 162 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) { |
164 for (NSString* key in keySet) { | 163 for (NSString* key in keySet) { |
165 [_label addObserver:self | 164 [_label addObserver:self |
166 forKeyPath:key | 165 forKeyPath:key |
(...skipping 16 matching lines...) Expand all Loading... |
183 action(_label); | 182 action(_label); |
184 } | 183 } |
185 | 184 |
186 - (void)resetLabelAttributes { | 185 - (void)resetLabelAttributes { |
187 if ([_label attributedText] || ![_label text]) | 186 if ([_label attributedText] || ![_label text]) |
188 return; | 187 return; |
189 NSMutableDictionary* labelStyle = | 188 NSMutableDictionary* labelStyle = |
190 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; | 189 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count]; |
191 for (NSString* property in styleKeys) | 190 for (NSString* property in styleKeys) |
192 labelStyle[property] = [_label valueForKey:property]; | 191 labelStyle[property] = [_label valueForKey:property]; |
193 base::scoped_nsobject<NSAttributedString> attributedText( | 192 NSAttributedString* attributedText = |
194 [[NSAttributedString alloc] initWithString:[_label text]]); | 193 [[NSAttributedString alloc] initWithString:[_label text]]; |
195 [_label setAttributedText:attributedText]; | 194 [_label setAttributedText:attributedText]; |
196 for (NSString* property in styleKeys) | 195 for (NSString* property in styleKeys) |
197 [_label setValue:labelStyle[property] forKey:property]; | 196 [_label setValue:labelStyle[property] forKey:property]; |
198 } | 197 } |
199 | 198 |
200 - (void)observeValueForKeyPath:(NSString*)key | 199 - (void)observeValueForKeyPath:(NSString*)key |
201 ofObject:(id)object | 200 ofObject:(id)object |
202 change:(NSDictionary*)change | 201 change:(NSDictionary*)change |
203 context:(void*)context { | 202 context:(void*)context { |
204 if (self.respondingToKVO) | 203 if (self.respondingToKVO) |
205 return; | 204 return; |
206 self.respondingToKVO = YES; | 205 self.respondingToKVO = YES; |
207 DCHECK_EQ(object, _label.get()); | 206 DCHECK_EQ(object, _label); |
208 if ([styleKeys containsObject:key]) { | 207 if ([styleKeys containsObject:key]) { |
209 [self performActions:_styleActions]; | 208 [self performActions:_styleActions]; |
210 } else if ([layoutKeys containsObject:key]) { | 209 } else if ([layoutKeys containsObject:key]) { |
211 [self performActions:_layoutActions]; | 210 [self performActions:_layoutActions]; |
212 } else if ([textKeys containsObject:key]) { | 211 } else if ([textKeys containsObject:key]) { |
213 NSString* oldText = GetStringValue(change[NSKeyValueChangeOldKey]); | 212 NSString* oldText = GetStringValue(change[NSKeyValueChangeOldKey]); |
214 NSString* newText = GetStringValue(change[NSKeyValueChangeNewKey]); | 213 NSString* newText = GetStringValue(change[NSKeyValueChangeNewKey]); |
215 if (![oldText isEqualToString:newText]) | 214 if (![oldText isEqualToString:newText]) |
216 [self resetLabelAttributes]; | 215 [self resetLabelAttributes]; |
217 [self performActions:_textActions]; | 216 [self performActions:_textActions]; |
218 } else { | 217 } else { |
219 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) | 218 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key) |
220 << "> observed"; | 219 << "> observed"; |
221 } | 220 } |
222 self.respondingToKVO = NO; | 221 self.respondingToKVO = NO; |
223 } | 222 } |
224 | 223 |
225 @end | 224 @end |
OLD | NEW |