Implemented #769 (Safari swipe to dismiss)

This commit is contained in:
David Sinclair 2015-10-23 17:42:53 -07:00
parent da5c895eea
commit ff42d673c2
9 changed files with 216 additions and 5 deletions

View file

@ -12,7 +12,7 @@
@class NewsBlurAppDelegate;
@interface NBContainerViewController : UIViewController
<UIPopoverControllerDelegate, SFSafariViewControllerDelegate> {
<UIPopoverControllerDelegate, SFSafariViewControllerDelegate, UIViewControllerTransitioningDelegate> {
NewsBlurAppDelegate *appDelegate;
BOOL interactiveOriginalTransition;

View file

@ -15,6 +15,8 @@
#import "OriginalStoryViewController.h"
#import "ShareViewController.h"
#import "UserProfileViewController.h"
#import "NBSafariViewController.h"
#import "NBModalPushPopTransition.h"
#import "InteractionCell.h"
#import "ActivityCell.h"
#import "FeedTableCell.h"
@ -48,7 +50,8 @@
@property (nonatomic, strong) OriginalStoryViewController *originalViewController;
@property (nonatomic, strong) StoryPageControl *storyPageControl;
@property (nonatomic, strong) ShareViewController *shareViewController;
@property (nonatomic, strong) SFSafariViewController *safariViewController;
@property (nonatomic, strong) NBSafariViewController *safariViewController;
@property (nonatomic, strong) NBModalPushPopTransition *safariAnimator;
@property (nonatomic, strong) UIView *storyTitlesStub;
@property (readwrite) BOOL storyTitlesOnLeft;
@property (readwrite) int storyTitlesYCoordinate;
@ -715,11 +718,17 @@
}
- (void)transitionToSafariView:(NSURL *)url {
self.safariViewController = [[SFSafariViewController alloc] initWithURL:url
self.safariAnimator = [NBModalPushPopTransition new];
self.safariViewController = [[NBSafariViewController alloc] initWithURL:url
entersReaderIfAvailable:NO];
self.safariViewController.delegate = self;
self.safariViewController.transitioningDelegate = self;
// self.navigationController.navigationBar.translucent = YES;
[self.storyNavigationController presentViewController:self.safariViewController animated:YES completion:nil];
[self.storyNavigationController presentViewController:self.safariViewController animated:YES completion:^{
UIScreenEdgePanGestureRecognizer *recognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
recognizer.edges = UIRectEdgeLeft;
[self.safariViewController.edgeView addGestureRecognizer:recognizer];
}];
// [self.storyNavigationController pushViewController:self.safariViewController animated:YES];
}
@ -729,6 +738,41 @@
[controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)handleGesture:(UIScreenEdgePanGestureRecognizer *)recognizer {
self.safariAnimator.percentageDriven = YES;
CGFloat percentComplete = [recognizer locationInView:self.view].x / self.view.bounds.size.width / 2.0;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
[self dismissViewControllerAnimated:YES completion:nil];
break;
case UIGestureRecognizerStateChanged:
[self.safariAnimator updateInteractiveTransition:percentComplete > 0.99 ? 0.99 : percentComplete];
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
([recognizer velocityInView:self.view].x < 0.0) ? [self.safariAnimator cancelInteractiveTransition] : [self.safariAnimator finishInteractiveTransition];
self.safariAnimator.percentageDriven = NO;
break;
default:
break;
}
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
self.safariAnimator.dismissing = NO;
return self.safariAnimator;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
self.safariAnimator.dismissing = YES;
return self.safariAnimator;
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
return self.safariAnimator.percentageDriven ? self.safariAnimator : nil;
}
- (void)transitionToOriginalView {
[self transitionToOriginalView:YES];
}

View file

@ -0,0 +1,17 @@
//
// NBModalPushPopTransition.h
// NewsBlur
//
// Created by David Sinclair on 2015-10-23.
// Copyright © 2015 NewsBlur. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NBModalPushPopTransition : UIPercentDrivenInteractiveTransition <UIViewControllerAnimatedTransitioning>
@property (nonatomic) BOOL dismissing;
@property (nonatomic) BOOL percentageDriven;
@end

View file

@ -0,0 +1,75 @@
//
// NBModalPushPopTransition.m
// NewsBlur
//
// Created by David Sinclair on 2015-10-23.
// Copyright © 2015 NewsBlur. All rights reserved.
//
// Based on Swift code from https://github.com/stringcode86/SCSafariViewController
//
#import "NBModalPushPopTransition.h"
@implementation NBModalPushPopTransition
- (id)init {
if ((self = [super init])) {
self.dismissing = NO;
self.percentageDriven = NO;
}
return self;
}
- (CGFloat)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.75;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *topView = self.dismissing ? fromViewController.view : toViewController.view;
UIViewController *bottomViewController = self.dismissing ? toViewController : fromViewController;
UIView *bottomView = bottomViewController.view;
CGFloat offset = bottomView.bounds.size.width;
if ([bottomViewController isKindOfClass:[UINavigationController class]]) {
bottomView = ((UINavigationController *)bottomViewController).topViewController.view;
}
[transitionContext.containerView insertSubview:toViewController.view aboveSubview:fromViewController.view];
if (self.dismissing) {
[transitionContext.containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
}
topView.frame = fromViewController.view.frame;
topView.transform = self.dismissing ? CGAffineTransformIdentity : CGAffineTransformMakeTranslation(offset, 0.0);
UIImageView *shadowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"safari_shadow"]];
shadowView.contentMode = UIViewContentModeScaleAspectFill;
shadowView.layer.anchorPoint = CGPointMake(0.0, 0.5);
shadowView.frame = bottomView.bounds;
[bottomView addSubview:shadowView];
shadowView.transform = self.dismissing ? CGAffineTransformMakeScale(0.01, 1.0) : CGAffineTransformIdentity;
shadowView.alpha = self.dismissing ? 1.0 : 0.0;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.9 initialSpringVelocity:1.0 options:self.animationOpts animations:^{
topView.transform = self.dismissing ? CGAffineTransformMakeTranslation(offset, 0.0) : CGAffineTransformIdentity;
shadowView.transform = self.dismissing ? CGAffineTransformIdentity : CGAffineTransformMakeScale(0.01, 1.0);
shadowView.alpha = self.dismissing ? 0.0 : 1.0;
} completion:^(BOOL finished) {
topView.transform = CGAffineTransformIdentity;
[shadowView removeFromSuperview];
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
- (UIViewAnimationOptions)animationOpts {
return UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionLayoutSubviews;
}
@end

View file

@ -0,0 +1,16 @@
//
// NBSafariViewController.h
// NewsBlur
//
// Created by David Sinclair on 2015-10-23.
// Copyright © 2015 NewsBlur. All rights reserved.
//
#import <SafariServices/SafariServices.h>
@interface NBSafariViewController : SFSafariViewController
@property (nonatomic, strong, readonly) UIView *edgeView;
@end

View file

@ -0,0 +1,42 @@
//
// NBSafariViewController.m
// NewsBlur
//
// Created by David Sinclair on 2015-10-23.
// Copyright © 2015 NewsBlur. All rights reserved.
//
// Based on Swift code from https://github.com/stringcode86/SCSafariViewController
//
#import "NBSafariViewController.h"
@interface NBSafariViewController ()
@property (nonatomic, strong) UIView *edgeView;
@end
@implementation NBSafariViewController
- (UIView *)edgeView {
if (_edgeView == nil && self.isViewLoaded) {
self.edgeView = [UIView new];
_edgeView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_edgeView];
_edgeView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.005];
NSDictionary *bindings = @{@"edgeView" : _edgeView};
NSLayoutFormatOptions options = 0;
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-0-[edgeView(5)]" options:options metrics:nil views:bindings];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[edgeView]-0-|" options:options metrics:nil views:bindings];
[NSLayoutConstraint activateConstraints:hConstraints];
[NSLayoutConstraint activateConstraints:vConstraints];
}
return _edgeView;
}
@end

View file

@ -37,6 +37,7 @@
#import "ARChromeActivity.h"
#import "NBCopyLinkActivity.h"
#import "MBProgressHUD.h"
#import "NBSafariViewController.h"
#import "Utilities.h"
#import "StringHelper.h"
#import "AuthorizeServicesViewController.h"
@ -1326,7 +1327,7 @@
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.masterContainerViewController transitionToSafariView:url];
} else {
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:url
NBSafariViewController *safari = [[NBSafariViewController alloc] initWithURL:url
entersReaderIfAvailable:NO];
safari.delegate = self;
[navigationController pushViewController:safari animated:YES];

View file

@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
010EDEFA1B2386B7003B79DE /* OnePasswordExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 010EDEF81B2386B7003B79DE /* OnePasswordExtension.m */; };
010EDEFC1B238722003B79DE /* 1Password.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 010EDEFB1B238722003B79DE /* 1Password.xcassets */; };
17F1566C1BDAB0930092EBFD /* NBSafariViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F1566B1BDAB0930092EBFD /* NBSafariViewController.m */; settings = {ASSET_TAGS = (); }; };
17F1566F1BDAB14F0092EBFD /* NBModalPushPopTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F1566E1BDAB14F0092EBFD /* NBModalPushPopTransition.m */; settings = {ASSET_TAGS = (); }; };
17F156711BDABBF60092EBFD /* safari_shadow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F156701BDABBF60092EBFD /* safari_shadow@2x.png */; settings = {ASSET_TAGS = (); }; };
1D3623260D0F684500981E51 /* NewsBlurAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
@ -455,6 +458,11 @@
010EDEF81B2386B7003B79DE /* OnePasswordExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OnePasswordExtension.m; path = "Other Sources/OnePasswordExtension/OnePasswordExtension.m"; sourceTree = "<group>"; };
010EDEF91B2386B7003B79DE /* OnePasswordExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OnePasswordExtension.h; path = "Other Sources/OnePasswordExtension/OnePasswordExtension.h"; sourceTree = "<group>"; };
010EDEFB1B238722003B79DE /* 1Password.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = 1Password.xcassets; path = "Other Sources/OnePasswordExtension/1Password.xcassets"; sourceTree = "<group>"; };
17F1566A1BDAB0930092EBFD /* NBSafariViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBSafariViewController.h; sourceTree = "<group>"; };
17F1566B1BDAB0930092EBFD /* NBSafariViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBSafariViewController.m; sourceTree = "<group>"; };
17F1566D1BDAB14F0092EBFD /* NBModalPushPopTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBModalPushPopTransition.h; sourceTree = "<group>"; };
17F1566E1BDAB14F0092EBFD /* NBModalPushPopTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBModalPushPopTransition.m; sourceTree = "<group>"; };
17F156701BDABBF60092EBFD /* safari_shadow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "safari_shadow@2x.png"; sourceTree = "<group>"; };
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D3623240D0F684500981E51 /* NewsBlurAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = NewsBlurAppDelegate.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = NewsBlurAppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
@ -1546,6 +1554,7 @@
FFEA5AEB19D340BC00ED87A0 /* logo_newsblur_512.png */,
43A4C42415B00A26008787B5 /* logo_background.png */,
43A4C42615B00A26008787B5 /* logo_newsblur_blur.png */,
17F156701BDABBF60092EBFD /* safari_shadow@2x.png */,
43A4C43F15B00A26008787B5 /* warning.gif */,
43A4C44015B00A26008787B5 /* warning.png */,
43A4C44115B00A26008787B5 /* world.png */,
@ -1774,6 +1783,10 @@
FFA0483D19CA5B8400618DC4 /* EventWindow.m */,
FF1C4E151A3FB1F4000995E3 /* NBActivityItemProvider.h */,
FF1C4E161A3FB1F4000995E3 /* NBActivityItemProvider.m */,
17F1566D1BDAB14F0092EBFD /* NBModalPushPopTransition.h */,
17F1566E1BDAB14F0092EBFD /* NBModalPushPopTransition.m */,
17F1566A1BDAB0930092EBFD /* NBSafariViewController.h */,
17F1566B1BDAB0930092EBFD /* NBSafariViewController.m */,
);
name = Foundation;
sourceTree = "<group>";
@ -2288,6 +2301,7 @@
FF3964BC192BED0A004BEE1A /* tag.png in Resources */,
FF29708B16DD7AA400E92F85 /* segment_active.png in Resources */,
FF29708C16DD7AA400E92F85 /* segment_inactive.png in Resources */,
17F156711BDABBF60092EBFD /* safari_shadow@2x.png in Resources */,
FF29708E16DD7C8A00E92F85 /* segment_left_selected.png in Resources */,
FF29709016DD7FD800E92F85 /* segment_unselected.png in Resources */,
FF03B00319F881380063002A /* ARChromeActivity~ipad.png in Resources */,
@ -2484,6 +2498,7 @@
FF5EA47F143B691000B7563D /* AddSiteViewController.m in Sources */,
FF1F13D818AAC97900FDA816 /* UIImage+Resize.m in Sources */,
FFD887F01445F1E800385399 /* AddSiteAutocompleteCell.m in Sources */,
17F1566F1BDAB14F0092EBFD /* NBModalPushPopTransition.m in Sources */,
FF62820A1A098EA800271FDB /* WYPopoverController.m in Sources */,
FF8D1ECD1BAA311000725D8A /* SBJson4StreamParser.m in Sources */,
FFE5322F144C8AC300ACFDE0 /* Utilities.m in Sources */,
@ -2532,6 +2547,7 @@
FF2EB7BE1AA65504002549A7 /* IASKTextField.m in Sources */,
FF8D1ECF1BAA311000725D8A /* SBJson4StreamTokeniser.m in Sources */,
FFD660641BACA46D006E4B8D /* AFNetworkReachabilityManager.m in Sources */,
17F1566C1BDAB0930092EBFD /* NBSafariViewController.m in Sources */,
FF8D1ED81BAA33BA00725D8A /* NSObject+SBJSON.m in Sources */,
FF2EB7B11AA65504002549A7 /* IASKAppSettingsWebViewController.m in Sources */,
FF1F13D418A9C2BE00FDA816 /* TMDiskCache.m in Sources */,

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB