From 6f0a9a3470bbf0371996724c2d2031526290f942 Mon Sep 17 00:00:00 2001 From: Todd Krabach Date: Wed, 1 Apr 2015 16:50:53 -0700 Subject: [PATCH] Fix up the app link return to referer view/controller - if it is not explicitly closed with the 'X', it should show again if the app is re-opened from a referer - rename _attachedToNavController ivar (per TODO) - remove TODOs that are no longer relevant - don't use autoresizing mask; the view has explicit layout already anyhow - fix up CG struct usage to use the C functions - allow intrinsic sizing of the referer view for use with auto-layout --- .../iOS/BFAppLinkReturnToRefererController.m | 157 ++++++++---------- Bolts/iOS/BFAppLinkReturnToRefererView.h | 27 +-- Bolts/iOS/BFAppLinkReturnToRefererView.m | 86 +++++++--- 3 files changed, 141 insertions(+), 129 deletions(-) diff --git a/Bolts/iOS/BFAppLinkReturnToRefererController.m b/Bolts/iOS/BFAppLinkReturnToRefererController.m index 1c313d649..f58a81683 100644 --- a/Bolts/iOS/BFAppLinkReturnToRefererController.m +++ b/Bolts/iOS/BFAppLinkReturnToRefererController.m @@ -16,19 +16,12 @@ static const CFTimeInterval kBFViewAnimationDuration = 0.25f; -@interface BFAppLinkReturnToRefererController () - -@property (nonatomic, strong, readwrite) UINavigationController *attachedToNavController; // TODO rename - -@end - -@implementation BFAppLinkReturnToRefererController { - BFURL *_lastShownBFUrl; - NSURL *_lastShownUrl; +@implementation BFAppLinkReturnToRefererController +{ + UINavigationController *_navigationController; + BFAppLinkReturnToRefererView *_view; } -@synthesize view = _view; - #pragma mark - Object lifecycle - (instancetype)init { @@ -38,21 +31,22 @@ - (instancetype)init { - (instancetype)initForDisplayAboveNavController:(UINavigationController *)navController { self = [super init]; if (self) { - _attachedToNavController = navController; - - if (_attachedToNavController != nil) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(statusBarFrameWillChange:) - name:UIApplicationWillChangeStatusBarFrameNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(statusBarFrameDidChange:) - name:UIApplicationDidChangeStatusBarFrameNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(orientationDidChange:) - name:UIDeviceOrientationDidChangeNotification - object:nil]; + _navigationController = navController; + + if (_navigationController != nil) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self + selector:@selector(statusBarFrameWillChange:) + name:UIApplicationWillChangeStatusBarFrameNotification + object:nil]; + [nc addObserver:self + selector:@selector(statusBarFrameDidChange:) + name:UIApplicationDidChangeStatusBarFrameNotification + object:nil]; + [nc addObserver:self + selector:@selector(orientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; } } return self; @@ -60,10 +54,7 @@ - (instancetype)initForDisplayAboveNavController:(UINavigationController *)navCo - (void)dealloc { _view.delegate = nil; - - if (_attachedToNavController) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - } + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - Public API @@ -71,8 +62,8 @@ - (void)dealloc { - (BFAppLinkReturnToRefererView *)view { if (!_view) { self.view = [[BFAppLinkReturnToRefererView alloc] initWithFrame:CGRectZero]; - if (_attachedToNavController) { - [_attachedToNavController.view addSubview:_view]; + if (_navigationController) { + [_navigationController.view addSubview:_view]; } } return _view; @@ -85,13 +76,10 @@ - (void)setView:(BFAppLinkReturnToRefererView *)view { _view = view; _view.delegate = self; - - if (_attachedToNavController) { + + if (_navigationController) { _view.includeStatusBarInSize = BFIncludeStatusBarInSizeAlways; } - - // TODO -// _view.refererAppLink = _refererAppLink; } - (void)showViewForRefererAppLink:(BFAppLink *)refererAppLink { @@ -99,7 +87,7 @@ - (void)showViewForRefererAppLink:(BFAppLink *)refererAppLink { [_view sizeToFit]; - if (_attachedToNavController) { + if (_navigationController) { if (!_view.closed) { dispatch_async(dispatch_get_main_queue(), ^{ [self moveNavigationBar]; @@ -109,30 +97,27 @@ - (void)showViewForRefererAppLink:(BFAppLink *)refererAppLink { } - (void)showViewForRefererURL:(NSURL *)url { - if (![_lastShownUrl isEqual:url]) { - _lastShownUrl = [url copy]; - _lastShownBFUrl = [BFURL URLForRenderBackToReferrerBarURL:url]; - } - [self showViewForRefererAppLink:_lastShownBFUrl.appLinkReferer]; + BFAppLink *appLink = [BFURL URLForRenderBackToReferrerBarURL:url].appLinkReferer; + [self showViewForRefererAppLink:appLink]; } - (void)removeFromNavController { - if (_attachedToNavController) { + if (_navigationController) { [_view removeFromSuperview]; - _attachedToNavController = nil; + _navigationController = nil; } } #pragma mark - BFAppLinkReturnToRefererViewDelegate - (void)returnToRefererViewDidTapInsideCloseButton:(BFAppLinkReturnToRefererView *)view { - [self closeViewAnimated:YES]; + [self closeViewAnimated:YES explicitlyClosed:YES]; } - (void)returnToRefererViewDidTapInsideLink:(BFAppLinkReturnToRefererView *)view link:(BFAppLink *)link { [self openRefererAppLink:link]; - [self closeViewAnimated:NO]; + [self closeViewAnimated:NO explicitlyClosed:NO]; } #pragma mark - Private @@ -142,11 +127,11 @@ - (void)statusBarFrameWillChange:(NSNotification *)notification { CGRect newFrame; [rectValue getValue:&newFrame]; - if (_attachedToNavController && !_view.closed) { - if (newFrame.size.height == 40) { + if (_navigationController && !_view.closed) { + if (CGRectGetHeight(newFrame) == 40) { UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; [UIView animateWithDuration:kBFViewAnimationDuration delay:0.0 options:options animations:^{ - _view.frame = CGRectMake(0.0, 0.0, _view.frame.size.width, 0.0); + _view.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(_view.bounds), 0.0); } completion:nil]; } } @@ -157,8 +142,8 @@ - (void)statusBarFrameDidChange:(NSNotification *)notification { CGRect newFrame; [rectValue getValue:&newFrame]; - if (_attachedToNavController && !_view.closed) { - if (newFrame.size.height == 40) { + if (_navigationController && !_view.closed) { + if (CGRectGetHeight(newFrame) == 40) { UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; [UIView animateWithDuration:kBFViewAnimationDuration delay:0.0 options:options animations:^{ [_view sizeToFit]; @@ -169,7 +154,7 @@ - (void)statusBarFrameDidChange:(NSNotification *)notification { } - (void)orientationDidChange:(NSNotificationCenter *)notification { - if (_attachedToNavController && !_view.closed && _view.frame.size.height > 0) { + if (_navigationController && !_view.closed && CGRectGetHeight(_view.bounds) > 0) { dispatch_async(dispatch_get_main_queue(), ^{ [self moveNavigationBar]; }); @@ -178,59 +163,53 @@ - (void)orientationDidChange:(NSNotificationCenter *)notification { - (void)moveNavigationBar { if (_view.closed || !_view.refererAppLink) { - return; + return; } - CGRect oldFrame = _attachedToNavController.navigationBar.frame; - - _attachedToNavController.navigationBar.frame = CGRectMake(0, - _view.frame.size.height, - _attachedToNavController.navigationBar.frame.size.width, - _attachedToNavController.navigationBar.frame.size.height); + [self updateNavigationBarY:CGRectGetHeight(_view.bounds)]; +} - CGFloat dy = CGRectGetMaxY(_attachedToNavController.navigationBar.frame) - CGRectGetMaxY(oldFrame); - UIView *navigationView = _attachedToNavController.visibleViewController.view.superview; - navigationView.frame = CGRectMake(navigationView.frame.origin.x, - navigationView.frame.origin.y + dy, - navigationView.frame.size.width, - navigationView.frame.size.height - dy); +- (void)updateNavigationBarY:(CGFloat)y +{ + UINavigationBar *navigationBar = _navigationController.navigationBar; + CGRect navigationBarFrame = navigationBar.frame; + CGFloat oldContainerViewY = CGRectGetMaxY(navigationBarFrame); + navigationBarFrame.origin.y = y; + navigationBar.frame = navigationBarFrame; + CGFloat dy = CGRectGetMaxY(navigationBarFrame) - oldContainerViewY; + UIView *containerView = _navigationController.visibleViewController.view.superview; + containerView.frame = UIEdgeInsetsInsetRect(containerView.frame, UIEdgeInsetsMake(dy, 0.0, 0.0, 0.0)); } - (void)closeViewAnimated:(BOOL)animated { + [self closeViewAnimated:animated explicitlyClosed:YES]; +} + +- (void)closeViewAnimated:(BOOL)animated explicitlyClosed:(BOOL)explicitlyClosed { void (^closer)(void) = ^{ - if (_attachedToNavController) { - CGRect oldFrame = _attachedToNavController.navigationBar.frame; - - _attachedToNavController.navigationBar.frame = CGRectMake(0, - _view.statusBarHeight, - _attachedToNavController.navigationBar.frame.size.width, - _attachedToNavController.navigationBar.frame.size.height); - - CGFloat dy = CGRectGetMaxY(_attachedToNavController.navigationBar.frame) - CGRectGetMaxY(oldFrame); - UIView *navigationView = _attachedToNavController.visibleViewController.view.superview; - navigationView.frame = CGRectMake(navigationView.frame.origin.x, - navigationView.frame.origin.y + dy, - navigationView.frame.size.width, - navigationView.frame.size.height - dy); + if (_navigationController) { + [self updateNavigationBarY:_view.statusBarHeight]; } - _view.frame = CGRectMake(_view.frame.origin.x, - _view.frame.origin.y, - _view.frame.size.width, - 0); + CGRect frame = _view.frame; + frame.size.height = 0.0; + _view.frame = frame; }; if (animated) { - UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState; - [UIView animateWithDuration:kBFViewAnimationDuration delay:0.0 options:options animations:^{ + [UIView animateWithDuration:kBFViewAnimationDuration animations:^{ closer(); - } completion:^(BOOL animateOutFinished) { - _view.closed = YES; + } completion:^(BOOL finished) { + if (explicitlyClosed) { + _view.closed = YES; + } }]; } else { closer(); - _view.closed = YES; + if (explicitlyClosed) { + _view.closed = YES; + } } } diff --git a/Bolts/iOS/BFAppLinkReturnToRefererView.h b/Bolts/iOS/BFAppLinkReturnToRefererView.h index e3af94083..cf3825c6a 100644 --- a/Bolts/iOS/BFAppLinkReturnToRefererView.h +++ b/Bolts/iOS/BFAppLinkReturnToRefererView.h @@ -16,11 +16,12 @@ @class BFAppLinkReturnToRefererView; @class BFURL; -typedef enum BFIncludeStatusBarInSize { - BFIncludeStatusBarInSizeNever, - BFIncludeStatusBarInSizeIOS7AndLater, - BFIncludeStatusBarInSizeAlways, -} BFIncludeStatusBarInSize; +typedef NS_ENUM(NSUInteger, BFIncludeStatusBarInSize) +{ + BFIncludeStatusBarInSizeNever, + BFIncludeStatusBarInSizeIOS7AndLater, + BFIncludeStatusBarInSizeAlways, +}; /*! Protocol that a class can implement in order to be notified when the user has navigated back @@ -65,7 +66,7 @@ typedef enum BFIncludeStatusBarInSize { /*! Indicates whether to extend the size of the view to include the current status bar size, for use in scenarios where the view might extend under the status bar on iOS 7 and - above; this property has no effect on earlier versions of iOS. + above; this property has no effect on earlier versions of iOS. */ @property (nonatomic, assign) BFIncludeStatusBarInSize includeStatusBarInSize; @@ -74,18 +75,4 @@ typedef enum BFIncludeStatusBarInSize { */ @property (nonatomic, assign) BOOL closed; -/*! - For apps that use a navigation controller, this method allows for displaying the view as - a banner above the navigation bar of the navigation controller. It will listen for orientation - change and other events to ensure it stays properly positioned above the nevigation bar. - If this method is called from, e.g., viewDidAppear, its counterpart, detachFromMainWindow should - be called from, e.g., viewWillDisappear. - */ -//- (void)attachToMainWindowAboveNavigationController:(UINavigationController *)navigationController view:(UIView *)view; - -/*! - Indicates that the view should no longer position itself above a navigation bar. - */ -//- (void)detachFromMainWindow; - @end diff --git a/Bolts/iOS/BFAppLinkReturnToRefererView.m b/Bolts/iOS/BFAppLinkReturnToRefererView.m index 58b700efc..4240ae077 100644 --- a/Bolts/iOS/BFAppLinkReturnToRefererView.m +++ b/Bolts/iOS/BFAppLinkReturnToRefererView.m @@ -32,6 +32,9 @@ @interface BFAppLinkReturnToRefererView () @end @implementation BFAppLinkReturnToRefererView +{ + BOOL _explicitlyHidden; +} #pragma mark - Initialization @@ -53,15 +56,15 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { } - (void)commonInit { - // Initialization code - _includeStatusBarInSize = BFIncludeStatusBarInSizeIOS7AndLater; + // Initialization code + _includeStatusBarInSize = BFIncludeStatusBarInSizeIOS7AndLater; - // iOS 7 system blue color - self.backgroundColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0]; - self.textColor = [UIColor whiteColor]; - self.clipsToBounds = YES; + // iOS 7 system blue color + self.backgroundColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0]; + self.textColor = [UIColor whiteColor]; + self.clipsToBounds = YES; - [self initViews]; + [self initViews]; } - (void)initViews { @@ -86,11 +89,9 @@ - (void)initViews { _labelView.textAlignment = UITextAlignmentCenter; #endif _labelView.clipsToBounds = YES; - _labelView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; [self updateLabelText]; [self addSubview:_labelView]; - _insideTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapInside:)]; _labelView.userInteractionEnabled = YES; [_labelView addGestureRecognizer:_insideTapGestureRecognizer]; @@ -101,13 +102,24 @@ - (void)initViews { #pragma mark - Layout +- (CGSize)intrinsicContentSize { + CGSize size = self.bounds.size; + if (_closed || !self.hasRefererData) { + size.height = 0.0; + } else { + CGSize labelSize = [_labelView sizeThatFits:size]; + size = CGSizeMake(size.width, labelSize.height + 2 * BFMarginY + self.statusBarHeight); + } + return size; +} + - (void)layoutSubviews { [super layoutSubviews]; CGRect bounds = self.bounds; - CGSize labelSize = [_labelView sizeThatFits:bounds.size]; _labelView.preferredMaxLayoutWidth = _labelView.bounds.size.width; + CGSize labelSize = [_labelView sizeThatFits:bounds.size]; _labelView.frame = CGRectMake(BFMarginX, CGRectGetMaxY(bounds) - labelSize.height - 1.5 * BFMarginY, CGRectGetMaxX(bounds) - BFCloseButtonWidth - 3 * BFMarginX, @@ -121,22 +133,35 @@ - (void)layoutSubviews { - (CGSize)sizeThatFits:(CGSize)size { if (_closed || !self.hasRefererData) { - return CGSizeMake(size.width, 0.0); + size = CGSizeMake(size.width, 0.0); + } else { + CGSize labelSize = [_labelView sizeThatFits:size]; + size = CGSizeMake(size.width, labelSize.height + 2 * BFMarginY + self.statusBarHeight); } - - CGSize labelSize = [_labelView sizeThatFits:size]; - return CGSizeMake(size.width, labelSize.height + 2 * BFMarginX + self.statusBarHeight); + return size; } - (CGFloat)statusBarHeight { UIApplication *application = [UIApplication sharedApplication]; - float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue]; - BOOL include = (_includeStatusBarInSize == BFIncludeStatusBarInSizeIOS7AndLater && systemVersion >= 7.0) || - _includeStatusBarInSize == BFIncludeStatusBarInSizeAlways; + BOOL include; + switch (_includeStatusBarInSize) { + case BFIncludeStatusBarInSizeAlways: + include = NO; + break; + case BFIncludeStatusBarInSizeIOS7AndLater:{ + float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue]; + include = (systemVersion >= 7.0); + break; + } + case BFIncludeStatusBarInSizeNever: + include = NO; + break; + } if (include && !application.statusBarHidden) { BOOL landscape = UIInterfaceOrientationIsLandscape(application.statusBarOrientation); - return landscape ? application.statusBarFrame.size.width : application.statusBarFrame.size.height; + CGRect statusBarFrame = application.statusBarFrame; + return landscape ? CGRectGetWidth(statusBarFrame) : CGRectGetHeight(statusBarFrame); } return 0; @@ -147,6 +172,7 @@ - (CGFloat)statusBarHeight { - (void)setIncludeStatusBarInSize:(BFIncludeStatusBarInSize)includeStatusBarInSize { _includeStatusBarInSize = includeStatusBarInSize; [self setNeedsLayout]; + [self invalidateIntrinsicContentSize]; } - (void)setTextColor:(UIColor *)textColor { @@ -155,8 +181,24 @@ - (void)setTextColor:(UIColor *)textColor { } - (void)setRefererAppLink:(BFAppLink *)refererAppLink { - _refererAppLink = refererAppLink; - [self updateLabelText]; + _refererAppLink = refererAppLink; + [self updateLabelText]; + [self updateHidden]; + [self invalidateIntrinsicContentSize]; +} + +- (void)setClosed:(BOOL)closed +{ + if (_closed != closed) { + _closed = closed; + [self updateHidden]; + [self invalidateIntrinsicContentSize]; + } +} + +- (void)setHidden:(BOOL)hidden { + _explicitlyHidden = hidden; + [self updateHidden]; } #pragma mark - Private @@ -222,4 +264,8 @@ - (void)onTapInside:(UIGestureRecognizer*)sender { [_delegate returnToRefererViewDidTapInsideLink:self link:_refererAppLink]; } +- (void)updateHidden { + [super setHidden:_explicitlyHidden || _closed || !self.hasRefererData]; +} + @end