在 Xcode 6 和 Swift 中的视图之间进行类似 Snapchat 的滑动导航)

Posted

技术标签:

【中文标题】在 Xcode 6 和 Swift 中的视图之间进行类似 Snapchat 的滑动导航)【英文标题】:Snapchat-like swipe navigation between views in Xcode 6 and Swift) 【发布时间】:2014-07-10 22:11:04 【问题描述】:

我一直在尝试使用滑动手势识别器和嵌入式导航控制器在我的应用程序中的视图控制器之间实现滑动导航,但它看起来甚至不接近 Snapchat 的导航。

实现此类功能的最有效和最合适的方式是什么?

我真的是 Swift 和编程的新手,我会感谢每一个有用的评论。

【问题讨论】:

UIPageViewController 或带有 UIScrollView 的 UIViewController 以及视图控制器的视图作为子视图添加到 UIScrollView 将是您最好的选择。 这是一个很棒的回购:github.com/goktugyil/EZSwipeController 【参考方案1】:

简短的版本是在控制器内部使用带有滚动视图的容器视图控制器。然后,您为应用程序中所需的每个屏幕创建单独的视图控制器,并使这些视图控制器的父级容器视图控制器。

可以找到带有示例代码的 github 存储库,here。

【讨论】:

教程帮助了我 - 谢谢。但是,您的代码不再有效(Xcode 6 beta 6,运行 ios 8 beta 5) - 编译正常,但视图控制器未正确实例化。相反,我使用var AVc: AViewController = AViewController(nibName: "AViewController", bundle: nil) 直接从它们的笔尖实例化它们。希望这对某人有帮助! 太棒了,谢谢。我还没有更新 beta6,但我会在更新时添加你的更改。 ???这与 Brendans 代码有什么不同?这是同一件事。你直接从他们的笔尖是什么意思?这对我不起作用,我不知道如何解决它...原因:'-[UIViewController _loadViewFromNibNamed:bundle:] 加载了“CViewController”笔尖,但未设置视图出口。' 现在是死链接,抱歉,我不得不把这篇博文撤下。不幸的是,无法编辑/删除答案,但如果您需要代码示例,github repo 仍然存在。 @user3498976,在这里查看我的最后一条评论:github.com/lbrendanl/SwiftSwipeView/issues/7。那应该回答你的问题。【参考方案2】:

您需要一个网页浏览控制器。这最初是为了展示教程和东西,但你也可以把视图控制器放在那里。那里有大量的教程,基本上你必须应用一些逻辑来告诉程序接下来要显示什么视图控制器。

这是一个非常高级的示例,但它可能对您有所帮助:

https://github.com/cwRichardKim/RKSwipeBetweenViewControllers

【讨论】:

正是我想要的,非常感谢你,伙计。谢谢。编辑:即使他要求快速代码! 这里唯一需要注意的是UIPagesViewController 不会在不查看私有子视图的情况下让您知道转换进度,这意味着您的视图中不能有基于进度的转换。【参考方案3】:

我不喜欢 lbrendanl 给出的版本,因为它不使用约束。我们不能像我们想要的那样定制它。这是相同的版本,但有限制:

scrollView 是一个绑定到控制器的 IBOutlet,有 4 个约束,控制器视图的每一侧都有一个常量为 0。

contentView 也是一个 IBOutlet,它作为 scrollView 的子视图添加到具有 4 个约束的 scrollView,每边的常量为 0。它还具有等高约束和等宽约束。宽度相等约束在运行时被移除,仅用于平息 IB。这个view代表了scrollView的contentView。

更新 iOS 9

 func setupDetailViewControllers() 
    var previousController: UIViewController?
    for controller in self.controllers 
        addChildViewController(controller)
        addControllerInContentView(controller, previousController: previousController)
        controller.didMoveToParentViewController(self)
        previousController = controller
    


func addControllerInContentView(controller: UIViewController, previousController: UIViewController?) 
    contentView.addSubview(controller.view)
    controller.view.translatesAutoresizingMaskIntoConstraints = false

    // top
    controller.view.topAnchor.constraintEqualToAnchor(contentView.topAnchor).active = true

    // bottom
    controller.view.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor).active = true

    // trailing
    trailingContentViewConstraint?.active = false
    trailingContentViewConstraint = controller.view.trailingAnchor.constraintEqualToAnchor(contentView.trailingAnchor)
    trailingContentViewConstraint?.active = true

    // leading
    let leadingAnchor = previousController?.view.trailingAnchor ?? contentView.leadingAnchor
    controller.view.leadingAnchor.constraintEqualToAnchor(leadingAnchor).active = true

    // width
    controller.view.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true

以前的答案

    class ContainerViewController: UIViewController 

    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var contentView: UIView!

    // A strong reference to the width contraint of the contentView
    var contentViewConstraint: NSLayoutConstraint!

    // A computed version of this reference
    var computedContentViewConstraint: NSLayoutConstraint 
        return NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: .Width, multiplier: CGFloat(controllers.count + 1), constant: 0)
    

    // The list of controllers currently present in the scrollView
    var controllers = [UIViewController]()

    override func viewDidLoad() 
        super.viewDidLoad()

        initScrollView()
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()

    

    func initScrollView()
        contentView.setTranslatesAutoresizingMaskIntoConstraints(false)

        contentViewConstraint = computedContentViewConstraint
        view.addConstraint(contentViewConstraint)

        // Adding all the controllers you want in the scrollView
        let controller1 = storyboard!.instantiateViewControllerWithIdentifier("AStoryboardID") as! AnUIControllerViewSubclass
        addToScrollViewNewController(controller)
        let controller2 = storyboard!.instantiateViewControllerWithIdentifier("AnotherStoryboardID") as! AnotherUIControllerViewSubclass
        addToScrollViewNewController(controller2)
    

    // The main method, adds the controller in the scrollView at the left of the previous controller added
    func addToScrollViewNewController(controller: UIViewController) 
        self.addChildViewController(controller)

        contentView.addSubview(controller.view)

        controller.view.setTranslatesAutoresizingMaskIntoConstraints(false)

        // Setting all the constraints 
        let bottomConstraint = NSLayoutConstraint(item: contentView, attribute: .Bottom, relatedBy: .Equal, toItem: controller.view, attribute: .Bottom, multiplier: 1.0, constant: 0)

        let topConstraint = NSLayoutConstraint(item: contentView, attribute: .Top, relatedBy: .Equal, toItem: controller.view, attribute: .Top, multiplier: 1.0, constant: 0)

        let widthConstraint = NSLayoutConstraint(item: controller.view, attribute: .Width, relatedBy: .Equal, toItem: scrollView, attribute: .Width, multiplier: 1.0, constant: 0)

        var trailingConstraint: NSLayoutConstraint!
        if controllers.isEmpty 
            // Since it's the first one, the trailing constraint is from the controller view to the contentView
            trailingConstraint = NSLayoutConstraint(item: contentView, attribute: .Trailing, relatedBy: .Equal, toItem: controller.view, attribute: .Trailing, multiplier: 1.0, constant: 0)
        
        else 
            trailingConstraint = NSLayoutConstraint(item: controllers.last!.view, attribute: .Leading, relatedBy: .Equal, toItem: controller.view, attribute: .Trailing, multiplier: 1.0, constant: 0)
        

        // Setting the new width constraint of the contentView
        view.removeConstraint(contentViewConstraint)
        contentViewConstraint = computedContentViewConstraint

        // Adding all the constraints to the view hierarchy
        view.addConstraint(contentViewConstraint)
        contentView.addConstraints([bottomConstraint, topConstraint, trailingConstraint])
        scrollView.addConstraints([widthConstraint])

        controller.didMoveToParentViewController(self)

        // Finally adding the controller in the list of controllers
        controllers.append(controller)
      

我过去使用过 lbrendanl 的版本。现在我更喜欢这个。告诉我你对它的想法。

【讨论】:

我认为这应该得到更多的关注。这有很大帮助。谢谢! 我用的是这个版本;它可以工作,但它不是即插即用的。我必须在调整约束方面做很多工作。它们似乎一直都偏离了几个像素。【参考方案4】:

我建议使用 UIPageViewController 并通过删除这些方法来隐藏点栏:

presentationCountForPageViewController
presentationIndexForPageViewController

这是一个很好的教程:

https://www.youtube.com/watch?v=8bltsDG2ENQ

这是一个很好的回购:

https://github.com/goktugyil/EZSwipeController

【讨论】:

【参考方案5】:

分页滚动视图,在 Snapchat 的情况下不是 PageViewController。

【讨论】:

【参考方案6】:

我有类似的需求,最初是用PageController 实现的,但后来我将其更改为UICollectionView,其中每个单元格都是全屏的,并且滚动设置为水平。

【讨论】:

使用 UICollectionView 有什么好处?【参考方案7】:

您可以尝试使用CATransition 来创建滑动动画。以下是如何从一个视图滑动到另一个视图的示例:

    UIView *parentView = [self.view superview];

CATransition *animation = [CATransition animation];
[animation setDuration:0.25];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

[parentView addSubview:yourSecondViewController.view];
[self.view removeFromSuperview];

[[theParentView layer] addAnimation:animation forKey:@"showSecondViewController"];

我在这里找到了一些代码:How can I implement a swiping/sliding animation between views?

【讨论】:

如果这对您有帮助,请单击复选标记。谢谢!

以上是关于在 Xcode 6 和 Swift 中的视图之间进行类似 Snapchat 的滑动导航)的主要内容,如果未能解决你的问题,请参考以下文章

Xcode(Swift):我无法在视图控制器之间传输数据

ScrollView 在子视图 xcode 6 Swift 的顶部添加空间

如何在 Swift(Xcode 6.3)中翻转标签(获取镜像视图)

Xcode Swift - 在视图控制器之间传递按钮路径数据

Swift:Xcode 6.1.1 更改由代码块执行而不是按钮触发的视图

使用 Swift 在 Xcode 6 中使用 xib 在自定义 uiview 中获取 SIGABRT