UISplitViewController 从任何地方平移到主视图
UISplitViewController pan to primary view from anywhere
抱歉冗长的解释,但这个问题 - 或类似的问题 - 已被问过几次,我还没有找到满意的答案。我正在 iOS 8 中编写一个 iPad 应用程序,它实现了 UISplitViewController。最近我一直在尝试让它在 iPhone 上运行。它转移得很好,一切都自动折叠起来,我的导航左侧包含一个后退按钮。酒吧.
我的问题是我想保留后退按钮功能以从堆栈中弹出一个视图,但也能够平移回主视图,即使它上面有多个详细视图也是如此。理想情况下,我希望能够覆盖或重定向 interactivePopGestureRecognizer,以便手势平滑地平移到主视图(在某些情况下,它可以在顶部堆叠 1 到 4 个详细视图其中)。但是,我不知道该怎么做。
我目前的解决方案(下面的代码)是在细节 viewcontroller 中禁用 interactivePopGestureRecognizer 并实现我自己的 ScreenEdgePanGestureRecognizer触发后,执行 popToRootViewController。我已经将 ScreenEdgePanGestureRecognizer 子类化,因此它将屏幕边缘平移视为离散的 "swipe"(即,一旦检测到足够大的屏幕边缘滑动 - 将所有内容从堆栈中弹出,以便主视图可见)。
用于停止 interactivePopGestureRecognizer 的详细视图控制器代码:
-(void)viewWillAppear : (BOOL) animated {
[super viewWillAppear : animated];
// stops navigation controller from responding to the default back swipe gesture
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled =NO;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
// Disable the default back swipe gesture tied to automatically included back button
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isEqual:self.navigationController.interactivePopGestureRecognizer]) {
return NO;
} else {
return YES;
}
}
我认为没有必要为 screenEdgePanGestureRecognizer 包含我的子类,因为它与我要问的解决方案无关这里是一些伪代码,详细显示了我的@selector 所做的事情viewcontroller:
- (IBAction)leftEdgeSwipe:(ScreenEdgeSwipeGestureRecognizer*)sender {
if (sender.swipeIsValid) {
[(UINavigationController *)self.splitViewController.viewControllers[0]
popToRootViewControllerAnimated:YES];
}
}
我尝试使用连续平移,但找不到在后台呈现主视图的方法,因为我将当前视图拉到一边以提供干净、流畅的平移效果。我能够做到这一点,所以我可以四处移动当前视图,但它后面只有一个灰色背景,我希望我的主视图所在的位置。
总结:如果确实没有办法改变 interactivePopGestureRecognizer 总是跳转到我的主视图(理想的解决方案),那么任何关于如何使我自己的平滑的信息回到我的主要观点将不胜感激。
所以我一直在搞一个平滑的平移手势子类。目前它的功能类似于 Apple 的后退手势,除了它会一直跳回到根视图控制器,而不是从堆栈中弹出一个视图。唯一的问题是它在平移时还没有在后台显示主视图。一旦我解决了这个问题,我会更新答案。
这是子类:
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "ScreenEdgeSwipeGestureRecognizer.h"
@interface ScreenEdgeSwipeGestureRecognizer ()
@property (nonatomic) UINavigationController* navController;
@end
@implementation ScreenEdgeSwipeGestureRecognizer{
CGPoint _screenCenter;
CGPoint _cumulativePanDistance;
}
- (id)initWithNavigationController:(UINavigationController*)navController {
self = [super initWithTarget:self action:@selector(leftEdgePan:)];
_screenCenter = CGPointZero;
_cumulativePanDistance = CGPointZero;
self.edges = UIRectEdgeLeft;
self.navController = navController;
return self;
}
- (IBAction)leftEdgePan:(ScreenEdgeSwipeGestureRecognizer*)sender {
assert(sender == self);
switch (self.state) {
case UIGestureRecognizerStateBegan:
[self initializePositions];
break;
case UIGestureRecognizerStateChanged:
[self updatePositions];
break;
case UIGestureRecognizerStateEnded:
[self animateViewBasedOnCurrentLocation];
break;
case UIGestureRecognizerStateCancelled:
[self animateViewToCenter];
break;
default:
break;
}
// Reset velocity of the pan so current velocity does not compound with velocity of next cycle
[sender setTranslation:CGPointMake(0, 0) inView:sender.view];
}
- (void)initializePositions {
_screenCenter = self.view.center;
_cumulativePanDistance = CGPointZero;
}
- (void)updatePositions {
// Track position of user touch event
CGPoint deltaSinceLastCycle = [self translationInView:self.view];
// View center = view center at last cycle + distance moved by user touch since last cycle
self.view.center=CGPointMake((self.view.center.x + deltaSinceLastCycle.x), self.view.center.y+ 0);
// Update the total positive distance traveled by the user touch event.
_cumulativePanDistance.x = _cumulativePanDistance.x + deltaSinceLastCycle.x;
}
- (void)animateViewBasedOnCurrentLocation {
if (_cumulativePanDistance.x >= (_screenCenter.x - 50)){
[self reset];
[_navController popToRootViewControllerAnimated:YES];
}else{
[self animateViewToCenter];
[self reset];
}
}
- (void)animateViewToCenter {
[UIView animateWithDuration:0.25 animations:^{self.view.center = self->_screenCenter;}];
}
- (void)reset {
[super reset];
_cumulativePanDistance = CGPointZero;
self.state = UIGestureRecognizerStatePossible;
}
@end
下面是我在视图控制器中实例化识别器的方法:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Initialize the screen edge pan gesture recognizer.
_masterNavigationController = self.splitViewController.viewControllers[0];
ScreenEdgePanGestureRecognizer* edgePanRecognizer = [[ScreenEdgeSwipeGestureRecognizer alloc] initWithNavigationController:_masterNavigationController];
// Add recognizer to view this controller is bound to.
[self.view addGestureRecognizer:_edgePanRecognizer];
}
抱歉冗长的解释,但这个问题 - 或类似的问题 - 已被问过几次,我还没有找到满意的答案。我正在 iOS 8 中编写一个 iPad 应用程序,它实现了 UISplitViewController。最近我一直在尝试让它在 iPhone 上运行。它转移得很好,一切都自动折叠起来,我的导航左侧包含一个后退按钮。酒吧.
我的问题是我想保留后退按钮功能以从堆栈中弹出一个视图,但也能够平移回主视图,即使它上面有多个详细视图也是如此。理想情况下,我希望能够覆盖或重定向 interactivePopGestureRecognizer,以便手势平滑地平移到主视图(在某些情况下,它可以在顶部堆叠 1 到 4 个详细视图其中)。但是,我不知道该怎么做。
我目前的解决方案(下面的代码)是在细节 viewcontroller 中禁用 interactivePopGestureRecognizer 并实现我自己的 ScreenEdgePanGestureRecognizer触发后,执行 popToRootViewController。我已经将 ScreenEdgePanGestureRecognizer 子类化,因此它将屏幕边缘平移视为离散的 "swipe"(即,一旦检测到足够大的屏幕边缘滑动 - 将所有内容从堆栈中弹出,以便主视图可见)。
用于停止 interactivePopGestureRecognizer 的详细视图控制器代码:
-(void)viewWillAppear : (BOOL) animated {
[super viewWillAppear : animated];
// stops navigation controller from responding to the default back swipe gesture
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled =NO;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
// Disable the default back swipe gesture tied to automatically included back button
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isEqual:self.navigationController.interactivePopGestureRecognizer]) {
return NO;
} else {
return YES;
}
}
我认为没有必要为 screenEdgePanGestureRecognizer 包含我的子类,因为它与我要问的解决方案无关这里是一些伪代码,详细显示了我的@selector 所做的事情viewcontroller:
- (IBAction)leftEdgeSwipe:(ScreenEdgeSwipeGestureRecognizer*)sender {
if (sender.swipeIsValid) {
[(UINavigationController *)self.splitViewController.viewControllers[0]
popToRootViewControllerAnimated:YES];
}
}
我尝试使用连续平移,但找不到在后台呈现主视图的方法,因为我将当前视图拉到一边以提供干净、流畅的平移效果。我能够做到这一点,所以我可以四处移动当前视图,但它后面只有一个灰色背景,我希望我的主视图所在的位置。
总结:如果确实没有办法改变 interactivePopGestureRecognizer 总是跳转到我的主视图(理想的解决方案),那么任何关于如何使我自己的平滑的信息回到我的主要观点将不胜感激。
所以我一直在搞一个平滑的平移手势子类。目前它的功能类似于 Apple 的后退手势,除了它会一直跳回到根视图控制器,而不是从堆栈中弹出一个视图。唯一的问题是它在平移时还没有在后台显示主视图。一旦我解决了这个问题,我会更新答案。
这是子类:
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "ScreenEdgeSwipeGestureRecognizer.h"
@interface ScreenEdgeSwipeGestureRecognizer ()
@property (nonatomic) UINavigationController* navController;
@end
@implementation ScreenEdgeSwipeGestureRecognizer{
CGPoint _screenCenter;
CGPoint _cumulativePanDistance;
}
- (id)initWithNavigationController:(UINavigationController*)navController {
self = [super initWithTarget:self action:@selector(leftEdgePan:)];
_screenCenter = CGPointZero;
_cumulativePanDistance = CGPointZero;
self.edges = UIRectEdgeLeft;
self.navController = navController;
return self;
}
- (IBAction)leftEdgePan:(ScreenEdgeSwipeGestureRecognizer*)sender {
assert(sender == self);
switch (self.state) {
case UIGestureRecognizerStateBegan:
[self initializePositions];
break;
case UIGestureRecognizerStateChanged:
[self updatePositions];
break;
case UIGestureRecognizerStateEnded:
[self animateViewBasedOnCurrentLocation];
break;
case UIGestureRecognizerStateCancelled:
[self animateViewToCenter];
break;
default:
break;
}
// Reset velocity of the pan so current velocity does not compound with velocity of next cycle
[sender setTranslation:CGPointMake(0, 0) inView:sender.view];
}
- (void)initializePositions {
_screenCenter = self.view.center;
_cumulativePanDistance = CGPointZero;
}
- (void)updatePositions {
// Track position of user touch event
CGPoint deltaSinceLastCycle = [self translationInView:self.view];
// View center = view center at last cycle + distance moved by user touch since last cycle
self.view.center=CGPointMake((self.view.center.x + deltaSinceLastCycle.x), self.view.center.y+ 0);
// Update the total positive distance traveled by the user touch event.
_cumulativePanDistance.x = _cumulativePanDistance.x + deltaSinceLastCycle.x;
}
- (void)animateViewBasedOnCurrentLocation {
if (_cumulativePanDistance.x >= (_screenCenter.x - 50)){
[self reset];
[_navController popToRootViewControllerAnimated:YES];
}else{
[self animateViewToCenter];
[self reset];
}
}
- (void)animateViewToCenter {
[UIView animateWithDuration:0.25 animations:^{self.view.center = self->_screenCenter;}];
}
- (void)reset {
[super reset];
_cumulativePanDistance = CGPointZero;
self.state = UIGestureRecognizerStatePossible;
}
@end
下面是我在视图控制器中实例化识别器的方法:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Initialize the screen edge pan gesture recognizer.
_masterNavigationController = self.splitViewController.viewControllers[0];
ScreenEdgePanGestureRecognizer* edgePanRecognizer = [[ScreenEdgeSwipeGestureRecognizer alloc] initWithNavigationController:_masterNavigationController];
// Add recognizer to view this controller is bound to.
[self.view addGestureRecognizer:_edgePanRecognizer];
}