[go: nahoru, domu]

blob: dd5207618de7fca7f36080884b02a87de75c879f [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/shared/ui/util/named_guide.h"
#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
namespace {
// The key path for whether NSLayoutConstraints are active.
NSString* const kActiveKeyPath = @"active";
} // namespace
@interface NamedGuide ()
// The constraints used to connect the guide to `constrainedView` or
// `constrainedFrame`.
@property(nonatomic, strong) NSArray* constraints;
// A dummy view that is used to support `constrainedFrame`.
@property(nonatomic, strong) UIView* constrainedFrameView;
// Updates `constraints` to constrain the guide to `view`.
- (void)updateConstraintsWithView:(UIView*)view;
// Updates `constrainedFrameView` according to `constrainedFrame` and
// `autoresizingMask`. This function will lazily instantiate the view if
// necessary and set up constraints so that this layout guide follows the view.
- (void)updateConstrainedFrameView;
// Checks whether the constraints have been deactivated, resetting them if
// necessary.
- (void)checkForInactiveConstraints;
@end
@implementation NamedGuide
@synthesize name = _name;
@synthesize constrainedView = _constrainedView;
@synthesize constrainedFrame = _constrainedFrame;
@synthesize autoresizingMask = _autoresizingMask;
@synthesize constraints = _constraints;
@synthesize constrainedFrameView = _constrainedFrameView;
- (instancetype)initWithName:(GuideName*)name {
if (self = [super init]) {
_name = name;
_constrainedFrame = CGRectNull;
}
return self;
}
- (void)dealloc {
[self resetConstraints];
}
- (NSString*)description {
return [NSString
stringWithFormat:@"<%@: %p - %@, layoutFrame=%@, owningView=%@>",
NSStringFromClass([self class]), self, self.name,
NSStringFromCGRect(self.layoutFrame), self.owningView];
}
#pragma mark - Accessors
- (BOOL)isConstrained {
[self checkForInactiveConstraints];
return self.constraints.count > 0;
}
- (void)setConstrainedView:(UIView*)constrainedView {
if (_constrainedView == constrainedView) {
return;
}
// Reset the constrained frame to null if specifying a new constrained view.
if (constrainedView) {
self.constrainedFrame = CGRectNull;
}
_constrainedView = constrainedView;
[self updateConstraintsWithView:_constrainedView];
}
- (void)setConstrainedFrame:(CGRect)constrainedFrame {
if (CGRectEqualToRect(_constrainedFrame, constrainedFrame)) {
return;
}
// Reset the constrained view to nil if specifying a new constrained frame.
if (!CGRectIsNull(constrainedFrame)) {
self.constrainedView = nil;
}
_constrainedFrame = constrainedFrame;
[self updateConstrainedFrameView];
}
- (void)setAutoresizingMask:(UIViewAutoresizing)autoresizingMask {
if (_autoresizingMask == autoresizingMask) {
return;
}
_autoresizingMask = autoresizingMask;
[self updateConstrainedFrameView];
}
- (void)setConstraints:(NSArray*)constraints {
if (_constraints == constraints) {
return;
}
if (_constraints.count) {
for (NSLayoutConstraint* constraint in _constraints) {
[constraint removeObserver:self forKeyPath:kActiveKeyPath];
}
[NSLayoutConstraint deactivateConstraints:_constraints];
}
_constraints = constraints;
if (_constraints.count) {
[NSLayoutConstraint activateConstraints:_constraints];
for (NSLayoutConstraint* constraint in _constraints) {
[constraint addObserver:self
forKeyPath:kActiveKeyPath
options:NSKeyValueObservingOptionNew
context:nullptr];
}
}
}
- (void)setConstrainedFrameView:(UIView*)constrainedFrameView {
if (_constrainedFrameView == constrainedFrameView) {
return;
}
if (_constrainedFrameView) {
[_constrainedFrameView removeFromSuperview];
}
_constrainedFrameView = constrainedFrameView;
// The constrained frame view is inserted at the bottom of the owning view's
// hierarchy in an effort to minimize additional rendering costs.
if (_constrainedFrameView) {
[self.owningView insertSubview:_constrainedFrameView atIndex:0];
}
}
#pragma mark - Public
+ (instancetype)guideWithName:(GuideName*)name view:(UIView*)view {
while (view) {
for (UILayoutGuide* guide in view.layoutGuides) {
NamedGuide* namedGuide = base::apple::ObjCCast<NamedGuide>(guide);
if ([namedGuide.name isEqualToString:name]) {
return namedGuide;
}
}
view = view.superview;
}
return nil;
}
- (void)resetConstraints {
_constrainedView = nil;
_constrainedFrame = CGRectNull;
self.constraints = nil;
}
#pragma mark - NSKeyValueObserving
- (void)observeValueForKeyPath:(NSString*)key
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
DCHECK([key isEqualToString:kActiveKeyPath]);
DCHECK([self.constraints containsObject:object]);
DCHECK(!base::apple::ObjCCastStrict<NSLayoutConstraint>(object).active);
[self checkForInactiveConstraints];
}
#pragma mark - Private
- (void)updateConstraintsWithView:(UIView*)view {
if (view) {
self.constraints = @[
[self.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
[self.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
[self.topAnchor constraintEqualToAnchor:view.topAnchor],
[self.bottomAnchor constraintEqualToAnchor:view.bottomAnchor]
];
} else {
self.constraints = nil;
}
}
- (void)updateConstrainedFrameView {
// Remove the dummy view if `constrainedFrame` is null.
if (CGRectIsNull(self.constrainedFrame)) {
self.constrainedFrameView = nil;
return;
}
// Lazily create the view if necessary and set it up using the specified frame
// and autoresizing mask.
// NOTE: The view's `translatesAutoresizingMaskIntoConstraints` remains set to
// the default value of `YES` in order to leverage UIKit's built in frame =>
// constraint conversion.
if (!self.constrainedFrameView) {
self.constrainedFrameView = [[UIView alloc] init];
self.constrainedFrameView.backgroundColor = [UIColor clearColor];
self.constrainedFrameView.userInteractionEnabled = NO;
}
self.constrainedFrameView.frame = self.constrainedFrame;
self.constrainedFrameView.autoresizingMask = self.autoresizingMask;
[self updateConstraintsWithView:self.constrainedFrameView];
}
- (void)checkForInactiveConstraints {
for (NSLayoutConstraint* constraint in self.constraints) {
if (constraint.active) {
return;
}
}
[self resetConstraints];
}
@end