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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/chrome/browser/ui/util/label_observer.mm
diff --git a/ios/chrome/browser/ui/util/label_observer.mm b/ios/chrome/browser/ui/util/label_observer.mm
new file mode 100644
index 0000000000000000000000000000000000000000..4fa5c8477581690d2d482fee10afa4b06c17458c
--- /dev/null
+++ b/ios/chrome/browser/ui/util/label_observer.mm
@@ -0,0 +1,189 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/util/label_observer.h"
+
+#import <objc/runtime.h>
+
+#import "base/ios/weak_nsobject.h"
+#import "base/mac/scoped_block.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace {
+// The key under which LabelObservers are associated with their labels.
+const void* const kLabelObserverKey = &kLabelObserverKey;
+// Attempts to convert |value| to a string.
+NSString* GetStringValue(id value) {
+ if ([value isKindOfClass:[NSString class]])
+ return static_cast<NSString*>(value);
+ if ([value respondsToSelector:@selector(string)])
+ return [value performSelector:@selector(string)];
+ return nil;
+}
+}
+
+@interface LabelObserver () {
+ // The label being observed.
+ base::WeakNSObject<UILabel> _label;
+ // Arrays used to store registered actions.
+ base::scoped_nsobject<NSMutableArray> _styleActions;
+ base::scoped_nsobject<NSMutableArray> _layoutActions;
+ base::scoped_nsobject<NSMutableArray> _textActions;
+}
+
+// Whether or not observer actions are currently being executed. This is used
+// to prevent infinite loops caused by a LinkObserverAction updating a
+// property on |_label|.
+@property(nonatomic, assign, getter=isRespondingToKVO) BOOL respondingToKVO;
+
+// Initializes a LabelObserver that observes |label|.
+- (instancetype)initWithLabel:(UILabel*)label NS_DESIGNATED_INITIALIZER;
+
+// Performs all LabelObserverActions in |actions|.
+- (void)performActions:(NSArray*)actions;
+
+// Takes |_label|'s values for each key from |styleKeys| and uses them to
+// construct a uniformly attributed value to use for |_label|'s attributedText.
+- (void)resetLabelAttributes;
+
+@end
+
+// Properties of UILabel that, when changed, will cause the label's attributed
+// text to change.
+static NSSet* styleKeys;
+// Properties of UILabel that invalidate the layout of the label if they change.
+static NSSet* layoutKeys;
+// Properties of UILabel that may invalidate the text of the label if they
+// change.
+static NSSet* textKeys;
+
+@implementation LabelObserver
+
+@synthesize respondingToKVO = _respondingToKVO;
+
++ (void)initialize {
+ if (self == [LabelObserver class]) {
+ styleKeys = [[NSSet alloc] initWithArray:@[
+ @"font", @"textColor", @"textAlignment", @"lineBreakMode", @"shadowColor",
+ @"shadowOffset"
+ ]];
+ layoutKeys = [[NSSet alloc]
+ initWithArray:@[ @"bounds", @"frame", @"superview", @"center" ]];
+ textKeys = [[NSSet alloc] initWithArray:@[ @"text", @"attributedText" ]];
+ }
+}
+
+- (instancetype)initWithLabel:(UILabel*)label {
+ if ((self = [super init])) {
+ DCHECK(label);
+ _label.reset(label);
+ for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) {
+ for (NSString* key in keySet) {
+ [_label addObserver:self
+ forKeyPath:key
+ options:NSKeyValueObservingOptionNew
+ context:nullptr];
+ }
+ }
+ [self resetLabelAttributes];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ for (NSSet* keySet in @[ styleKeys, layoutKeys, textKeys ]) {
+ for (NSString* key in keySet) {
+ [_label removeObserver:self forKeyPath:key];
+ }
+ }
+ [super dealloc];
+}
+
+#pragma mark - Public interface
+
++ (instancetype)observerForLabel:(UILabel*)label {
+ if (!label)
+ return nil;
+ id observer = objc_getAssociatedObject(label, kLabelObserverKey);
+ if (!observer) {
+ observer = [[LabelObserver alloc] initWithLabel:label];
+ objc_setAssociatedObject(label, kLabelObserverKey, observer,
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ [observer release];
+ }
+ return observer;
+}
+
+- (void)addStyleChangedAction:(LabelObserverAction)action {
+ DCHECK(action);
+ if (!_styleActions)
+ _styleActions.reset([[NSMutableArray alloc] init]);
+ base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
+ [_styleActions addObject:actionCopy];
+}
+
+- (void)addLayoutChangedAction:(LabelObserverAction)action {
+ DCHECK(action);
+ if (!_layoutActions)
+ _layoutActions.reset([[NSMutableArray alloc] init]);
+ base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
+ [_layoutActions addObject:actionCopy];
+}
+
+- (void)addTextChangedAction:(LabelObserverAction)action {
+ DCHECK(action);
+ if (!_textActions)
+ _textActions.reset([[NSMutableArray alloc] init]);
+ base::mac::ScopedBlock<LabelObserverAction> actionCopy([action copy]);
+ [_textActions addObject:actionCopy];
+}
+
+#pragma mark -
+
+- (void)performActions:(NSArray*)actions {
+ for (LabelObserverAction action in actions)
+ action(_label);
+}
+
+- (void)resetLabelAttributes {
+ if ([_label attributedText] || ![_label text])
+ return;
+ NSMutableDictionary* labelStyle =
+ [NSMutableDictionary dictionaryWithCapacity:styleKeys.count];
+ for (NSString* property in styleKeys)
+ labelStyle[property] = [_label valueForKey:property];
+ base::scoped_nsobject<NSAttributedString> attributedText(
+ [[NSAttributedString alloc] initWithString:[_label text]]);
+ [_label setAttributedText:attributedText];
+ for (NSString* property in styleKeys)
+ [_label setValue:labelStyle[property] forKey:property];
+}
+
+- (void)observeValueForKeyPath:(NSString*)key
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ if (self.respondingToKVO)
+ return;
+ self.respondingToKVO = YES;
+ DCHECK_EQ(object, _label.get());
+ if ([styleKeys containsObject:key]) {
+ [self performActions:_styleActions];
+ } else if ([layoutKeys containsObject:key]) {
+ [self performActions:_layoutActions];
+ } else if ([textKeys containsObject:key]) {
+ NSString* oldText = GetStringValue(change[NSKeyValueChangeOldKey]);
+ NSString* newText = GetStringValue(change[NSKeyValueChangeNewKey]);
+ if (![oldText isEqualToString:newText])
+ [self resetLabelAttributes];
+ [self performActions:_textActions];
+ } else {
+ NOTREACHED() << "Unexpected label key <" << base::SysNSStringToUTF8(key)
+ << "> observed";
+ }
+ self.respondingToKVO = NO;
+}
+
+@end
« 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