gpt4 book ai didi

ios - 在 iOS 中使用手势导航 UIViewControllers

转载 作者:可可西里 更新时间:2023-11-01 05:09:28 24 4
gpt4 key购买 nike

我在 UINavigationController 中嵌入了几个 View Controller (一些是模态的,一些是推送的),我使用滑动手势在它们之间导航:

// Gesture recognizers
UISwipeGestureRecognizer *downGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(dismissButton)];
downGesture.direction = UISwipeGestureRecognizerDirectionDown;
[downGesture setCancelsTouchesInView:NO];
[self.view addGestureRecognizer:downGesture];

这工作正常,但是我希望用户能够物理拖动模态呈现的 View Controller ,例如,向下和离开屏幕,而不是仅仅轻弹和动画完成其余的,或在屏幕上向右拖动并捕捉到上一个 View ,而不是点击后退按钮。

我已经尝试在 View 上使用平移手势来实现它,但当然之前的 View Controller 在它后面不可见,但它需要是可见的。这个效果是如何正确实现的?使用 View Controller 包含?如果是这样,那么在将几个 View Controller 推到堆栈上时它会如何工作?可以在 LetterPress 应用程序中找到我正在谈论的导航类型的示例。

谢谢。

最佳答案

是的,自定义容器 View 是可行的方法(iOS 5 及更高版本)。您基本上是编写自己的自定义容器,使用内置的 childViewControllers 属性来跟踪所有 subview Controller 。您可能希望自己的属性(例如 currentChildIndex)跟踪您当前所在的子 Controller :

@property (nonatomic) NSInteger currentChildIndex;

您的父 Controller 可能应该有一些用于非滑动相关导航的推送和弹出方法,例如:

- (void)pushChildViewController:(UIViewController *)newChildController
{
// remove any other children that we've popped off, but are still lingering about

for (NSInteger index = [self.childViewControllers count] - 1; index > self.currentChildIndex; index--)
{
UIViewController *childController = self.childViewControllers[index];
[childController willMoveToParentViewController:nil];
[childController.view removeFromSuperview];
[childController removeFromParentViewController];
}

// get reference to the current child controller

UIViewController *currentChildController = self.childViewControllers[self.currentChildIndex];

// set new child to be off to the right

CGRect frame = self.containerView.bounds;
frame.origin.x += frame.size.width;
newChildController.view.frame = frame;

// add the new child

[self addChildViewController:newChildController];
[self.containerView addSubview:newChildController.view];
[newChildController didMoveToParentViewController:self];

[UIView animateWithDuration:0.5
animations:^{
CGRect frame = self.containerView.bounds;
newChildController.view.frame = frame;
frame.origin.x -= frame.size.width;
currentChildController.view.frame = frame;
}];

self.currentChildIndex++;
}

- (void)popChildViewController
{
if (self.currentChildIndex == 0)
return;

UIViewController *currentChildController = self.childViewControllers[self.currentChildIndex];
self.currentChildIndex--;
UIViewController *previousChildController = self.childViewControllers[self.currentChildIndex];

CGRect onScreenFrame = self.containerView.bounds;

CGRect offToTheRightFrame = self.containerView.bounds;
offToTheRightFrame.origin.x += offToTheRightFrame.size.width;

[UIView animateWithDuration:0.5
animations:^{
currentChildController.view.frame = offToTheRightFrame;
previousChildController.view.frame = onScreenFrame;
}];
}

就我个人而言,我为这两种方法定义了一个协议(protocol),并确保我的父 Controller 配置为符合该协议(protocol):

@protocol ParentControllerDelegate <NSObject>

- (void)pushChildViewController:(UIViewController *)newChildController;
- (void)popChildViewController;

@end

@interface ParentViewController : UIViewController <ParentControllerDelegate>
...
@end

然后,当一个 child 想要插入一个新的 child 时,它可以这样做:

ChildViewController *controller = ... // instantiate and configure your next controller however you want to do that

id<ParentControllerDelegate> parent = (id)self.parentViewController;
NSAssert([parent conformsToProtocol:@protocol(ParentControllerDelegate)], @"Parent must conform to ParentControllerDelegate");

[parent pushChildViewController:controller];

当一个 child 想要弹出自己时,它可以这样做:

id<ParentControllerDelegate> parent = (id)self.parentViewController;
NSAssert([parent conformsToProtocol:@protocol(ParentControllerDelegate)], @"Parent must conform to ParentControllerDelegate");

[parent popChildViewController];

然后父 View Controller 设置了平移手势,以处理用户从一个 subview 平移到另一个 subview :

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static UIView *currentView;
static UIView *previousView;
static UIView *nextView;

if (gesture.state == UIGestureRecognizerStateBegan)
{
// identify previous view (if any)

if (self.currentChildIndex > 0)
{
UIViewController *previous = self.childViewControllers[self.currentChildIndex - 1];
previousView = previous.view;
}
else
{
previousView = nil;
}

// identify next view (if any)

if (self.currentChildIndex < ([self.childViewControllers count] - 1))
{
UIViewController *next = self.childViewControllers[self.currentChildIndex + 1];
nextView = next.view;
}
else
{
nextView = nil;
}

// identify current view

UIViewController *current = self.childViewControllers[self.currentChildIndex];
currentView = current.view;
}

// if we're in the middle of a pan, let's adjust the center of the views accordingly

CGPoint translation = [gesture translationInView:gesture.view.superview];

previousView.transform = CGAffineTransformMakeTranslation(translation.x, 0.0);
currentView.transform = CGAffineTransformMakeTranslation(translation.x, 0.0);
nextView.transform = CGAffineTransformMakeTranslation(translation.x, 0.0);

// if we're all done, let's animate the completion (or if we didn't move far enough,
// the reversal) of the pan gesture

if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed)
{

CGPoint center = currentView.center;
CGPoint currentCenter = CGPointMake(center.x + translation.x, center.y);
CGPoint offRight = CGPointMake(center.x + currentView.frame.size.width, center.y);
CGPoint offLeft = CGPointMake(center.x - currentView.frame.size.width, center.y);

CGPoint velocity = [gesture velocityInView:gesture.view.superview];

if ((translation.x + velocity.x * 0.5) < (-self.containerView.frame.size.width / 2.0) && nextView)
{
// if we finished pan to left, reset transforms

previousView.transform = CGAffineTransformIdentity;
currentView.transform = CGAffineTransformIdentity;
nextView.transform = CGAffineTransformIdentity;

// set the starting point of the animation to pick up from where
// we had previously transformed the views

CGPoint nextCenter = CGPointMake(nextView.center.x + translation.x, nextView.center.y);
currentView.center = currentCenter;
nextView.center = nextCenter;

// and animate the moving of the views to their final resting points,
// adjusting the currentChildIndex appropriately

[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
currentView.center = offLeft;
nextView.center = center;
self.currentChildIndex++;
}
completion:NULL];
}
else if ((translation.x + velocity.x * 0.5) > (self.containerView.frame.size.width / 2.0) && previousView)
{
// if we finished pan to right, reset transforms

previousView.transform = CGAffineTransformIdentity;
currentView.transform = CGAffineTransformIdentity;
nextView.transform = CGAffineTransformIdentity;

// set the starting point of the animation to pick up from where
// we had previously transformed the views

CGPoint previousCenter = CGPointMake(previousView.center.x + translation.x, previousView.center.y);
currentView.center = currentCenter;
previousView.center = previousCenter;

// and animate the moving of the views to their final resting points,
// adjusting the currentChildIndex appropriately

[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
currentView.center = offRight;
previousView.center = center;
self.currentChildIndex--;
}
completion:NULL];
}
else
{
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
previousView.transform = CGAffineTransformIdentity;
currentView.transform = CGAffineTransformIdentity;
nextView.transform = CGAffineTransformIdentity;
}
completion:NULL];
}
}
}

看起来您是在进行上下平移,而不是我上面使用的左右平移,但希望您能理解基本概念。


顺便说一句,在 iOS 6 中,您所询问的用户界面(使用手势在 View 之间滑动)可能可以使用内置容器 Controller 更有效地完成,UIPageViewController .只需使用 UIPageViewControllerTransitionStyleScroll 的过渡样式和 UIPageViewControllerNavigationOrientationHorizo​​ntal 的导航方向。不幸的是,iOS 5 只允许页面 curl 过渡,而 Apple 只在 iOS 6 中引入了你想要的滚动过渡,但如果这就是你所需要的,UIPageViewController 可以比我更有效地完成工作'已经在上面列出(您不必执行任何自定义容器调用,无需编写手势识别器等)。

例如,您可以将“页面 View Controller ”拖到 Storyboard上,创建一个UIPageViewController 子类,然后在viewDidLoad 中,您需要配置第一个页面:

UIViewController *firstPage = [self.storyboard instantiateViewControllerWithIdentifier:@"1"]; // use whatever storyboard id your left page uses
self.viewControllerStack = [NSMutableArray arrayWithObject:firstPage];

[self setViewControllers:@[firstPage]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];

self.dataSource = self;

然后你需要定义下面的UIPageViewControllerDataSource方法:

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[LeftViewController class]])
return [self.storyboard instantiateViewControllerWithIdentifier:@"2"];

return nil;
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[RightViewController class]])
return [self.storyboard instantiateViewControllerWithIdentifier:@"1"];

return nil;
}

您的实现会有所不同(至少是不同的类名和不同的 Storyboard 标识符;我还让页面 View Controller 在用户请求时实例化下一页的 Controller ,因为我没有保留任何强引用对他们来说,当我完成转换到另一个页面时,它们将被释放......你也可以在启动时实例化它们,然后实例化这些 beforeafter 例程显然不会实例化,而是在数组中查找它们),但希望你明白了。

但关键问题是我没有任何手势代码,没有自定义容器 View Controller 代码等。简单得多。

关于ios - 在 iOS 中使用手势导航 UIViewControllers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15703613/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com