Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: ios/chrome/browser/ui/util/label_observer.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/util/label_observer.h"
6
7 #import <objc/runtime.h>
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"
13
14 namespace {
15 // The key under which LabelObservers are associated with their labels.
16 const void* const kLabelObserverKey = &kLabelObserverKey;
17 // Attempts to convert |value| to a string.
18 NSString* GetStringValue(id value) {
19 if ([value isKindOfClass:[NSString class]])
20 return static_cast<NSString*>(value);
21 if ([value respondsToSelector:@selector(string)])
22 return [value performSelector:@selector(string)];
23 return nil;
24 }
25 }
26
27 @interface LabelObserver () {
28 // The label being observed.
29 base::WeakNSObject<UILabel> _label;
30 // Arrays used to store registered actions.
31 base::scoped_nsobject<NSMutableArray> _styleActions;
32 base::scoped_nsobject<NSMutableArray> _layoutActions;
33 base::scoped_nsobject<NSMutableArray> _textActions;
34 }
35
36 // Whether or not observer actions are currently being executed. This is used
37 // to prevent infinite loops caused by a LinkObserverAction updating a
38 // property on |_label|.
39 @property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO;
40
41 // Initializes a LabelObserver that observes |label|.
42 - (instancetype)initWithLabel:(UILabel*)label NS_DESIGNATED_INITIALIZER;
43
44 // Performs all LabelObserverActions in |actions|.
45 - (void)performActions:(NSArray*)actions;
46
47 // 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.
49 - (void)resetLabelAttributes;
50
51 @end
52
53 // Properties of UILabel that, when changed, will cause the label's attributed
54 // text to change.
55 static NSSet* styleKeys;
56 // Properties of UILabel that invalidate the layout of the label if they change.
57 static NSSet* layoutKeys;
58 // Properties of UILabel that may invalidate the text of the label if they
59 // change.
60 static NSSet* textKeys;
61
62 @implementation LabelObserver
63
64 @synthesize respondingToKVO = _respondingToKVO;
65
66 + (void)initialize {
67 if (self == [LabelObserver class]) {
68 styleKeys = [[NSSet alloc] initWithArray:@[
69 @"font", @"textColor", @"textAlignment", @"lineBreakMode", @"shadowColor",
70 @"shadowOffset"
71 ]];
72 layoutKeys = [[NSSet alloc]
73 initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]];
74 textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]];
75 }
76 }
77
78 - (instancetype)initWithLabel:(UILabel*)label {
79 if ((self = [super init])) {
80 DCHECK(label);
81 _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];
91 }
92 return self;
93 }
94
95 - (void)dealloc {
96 for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) {
97 for (NSString* key in keySet) {
98 [_label removeObserver:self forKeyPath:key];
99 }
100 }
101 [super dealloc];
102 }
103
104 #pragma mark - Public interface
105
106 + (instancetype)observerForLabel:(UILabel*)label {
107 if (!label)
108 return nil;
109 id observer = objc_getAssociatedObject(label, kLabelObserverKey);
110 if (!observer) {
111 observer = [[LabelObserver alloc] initWithLabel:label];
112 objc_setAssociatedObject(label, kLabelObserverKey, observer,
113 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
114 [observer release];
115 }
116 return observer;
117 }
118
119 - (void)addStyleChangedAction:(LabelObserverAction)action {
120 DCHECK(action);
121 if (!_styleActions)
122 _styleActions.reset([[NSMutableArray alloc] init]);
123 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
124 [_styleActions addObject:actionCopy];
125 }
126
127 - (void)addLayoutChangedAction:(LabelObserverAction)action {
128 DCHECK(action);
129 if (!_layoutActions)
130 _layoutActions.reset([[NSMutableArray alloc] init]);
131 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
132 [_layoutActions addObject:actionCopy];
133 }
134
135 - (void)addTextChangedAction:(LabelObserverAction)action {
136 DCHECK(action);
137 if (!_textActions)
138 _textActions.reset([[NSMutableArray alloc] init]);
139 base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
140 [_textActions addObject:actionCopy];
141 }
142
143 #pragma mark -
144
145 - (void)performActions:(NSArray*)actions {
146 for (LabelObserverAction action in actions)
147 action(_label);
148 }
149
150 - (void)resetLabelAttributes {
151 if ([_label attributedText] || ![_label text])
152 return;
153 NSMutableDictionary* labelStyle =
154 [NSMutableDictionary dictionaryWithCapacity:styleKeys.count];
155 for (NSString* property in styleKeys)
156 labelStyle[property] = [_label valueForKey:property];
157 base::scoped_nsobject<NSAttributedString> attributedText(
158 [[NSAttributedString alloc] initWithString:[_label text]]);
159 [_label setAttributedText:attributedText];
160 for (NSString* property in styleKeys)
161 [_label setValue:labelStyle[property] forKey:property];
162 }
163
164 - (void)observeValueForKeyPath:(NSString*)key
165 ofObject:(id)object
166 change:(NSDictionary*)change
167 context:(void*)context {
168 if (self.respondingToKVO)
169 return;
170 self.respondingToKVO = YES;
171 DCHECK_EQ(object, _label.get());
172 if ([styleKeys containsObject:key]) {
173 [self performActions:_styleActions];
174 } else if ([layoutKeys containsObject:key]) {
175 [self performActions:_layoutActions];
176 } else if ([textKeys containsObject:key]) {
177 NSString* oldText = GetStringValue(change[NSKeyValueChangeOldKey]);
178 NSString* newText = GetStringValue(change[NSKeyValueChangeNewKey]);
179 if (![oldText isEqualToString:newText])
180 [self resetLabelAttributes];
181 [self performActions:_textActions];
182 } else {
183 NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key)
184 << "> observed";
185 }
186 self.respondingToKVO = NO;
187 }
188
189 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/util/label_observer.h ('k') | ios/chrome/browser/ui/util/label_observer_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698