iOS开发Swift

Swift页面跳转的几种方式

今天搞通了swift页面跳转。如果对比前端或者说web应用,其实就是“路由”,像react应用,可以通过react-router来管理路由,vue可以通过vue-router来管理路由类似,swift中可以用UINavigationController来管理“路由”,这里应该叫“导航”吧。iOS中有两种不同形式的跳转,一种是有逻辑层级关系的跳转,一种是临时页面的跳转。

1.临时的页面跳转


我觉得可以看作是一个弹出页,比如点击一个表单控件,可以弹出一个相应的编辑页。这个是属于该页面的行为,所以不需要全局使用,视图自带。

跳转到下一页,可以用self.present(anotherView, animated: true, completion: nil),这样页面会从底部弹出。收回页面,调用self.dismiss(animated: true, completion: nil)。这里灰常简单,就认识俩方法而已。

ViewController.swift

import UIKit


class ViewController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initBtn()
    }

    //初始化按钮,点击按钮跳转页面
    func initBtn() {
        let screenSize = UIScreen.main.bounds.size
        let jumpBtn = UIButton(type: .system)
        jumpBtn.setTitle("跳转", for: .normal)
        jumpBtn.frame = CGRect(x: screenSize.width / 2 - 50, y: screenSize.height - 50, width: 100, height: 30)
        jumpBtn.backgroundColor = .blue
        jumpBtn.setTitleColor(UIColor.white, for: .normal)
        //按钮绑定事件,点击时执行
        jumpBtn.addTarget(self, action: #selector(pageJump), for: .touchDown)
        self.view.addSubview(jumpBtn)

    }
    
    @objc func pageJump() {
        print("main to dest")
        //创建一个页面
        let destination = DestinationViewController()
        //取目标页面的一个变量进行赋值,以属性的方式进行传值。
        destination.message = "传递的信息"
        //跳转
        self.present(destination, animated: true, completion: nil)
        
    }
}

DestinationViewContrller.swift

import UIKit


class DestinationViewController: UIViewController {

    var message: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.white
        initBtn()
        print(message!)
    }
    //初始化返回按钮,点击按钮返回主页面。
    func initBtn() {
        let screenSize = UIScreen.main.bounds.size
        let jumpBtn = UIButton(type: .system)
        jumpBtn.setTitle("返回", for: .normal)
        jumpBtn.frame = CGRect(x: screenSize.width / 2 - 50, y: screenSize.height - 100, width: 100, height: 30)
        jumpBtn.backgroundColor = .red
        jumpBtn.setTitleColor(UIColor.white, for: .normal)
        //按钮绑定事件
        jumpBtn.addTarget(self, action: #selector(pageReturn), for: .touchDown)
        self.view.addSubview(jumpBtn)
    }
    
    @objc func pageReturn() {
        print("dest to main")
        //返回主页面
        self.dismiss(animated: true, completion: nil)
    }
    
}

2.具有逻辑层次的页面跳转


比如网上购物,下单之后要跳到订单页面,再跳到支付页面等等,这种跳转是有一定逻辑,或者说先后顺序的。这种跳转需要用NavigationViewController进行跳转。

还是上面那个例子:

import UIKit


class ViewController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initBtn()
    }

    //初始化按钮,点击按钮跳转页面
    func initBtn() {
        let screenSize = UIScreen.main.bounds.size
        let jumpBtn = UIButton(type: .system)
        jumpBtn.setTitle("跳转", for: .normal)
        jumpBtn.frame = CGRect(x: screenSize.width / 2 - 50, y: screenSize.height - 50, width: 100, height: 30)
        jumpBtn.backgroundColor = .blue
        jumpBtn.setTitleColor(UIColor.white, for: .normal)
        //按钮绑定事件,点击时执行
        jumpBtn.addTarget(self, action: #selector(pageJump), for: .touchDown)
        
        self.view.addSubview(jumpBtn)
    }
    
    @objc func pageJump() {
        print("main to dest")
        //创建一个页面
        let destination = DestinationViewController()
        //取目标页面的一个变量进行赋值,以属性的方式进行传值。
        destination.message = "传递的信息"
        //跳转
        self.navigationController?.pushViewController(destination, animated: true)
    }
}

DestinationViewController.swift

import UIKit


class DestinationViewController: UIViewController {

    var message: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.white
        initBtn()
        print(message!)
    }
    //初始化返回按钮,点击按钮返回主页面。
    func initBtn() {
        let screenSize = UIScreen.main.bounds.size
        let jumpBtn = UIButton(type: .system)
        jumpBtn.setTitle("返回", for: .normal)
        jumpBtn.frame = CGRect(x: screenSize.width / 2 - 50, y: screenSize.height - 50, width: 100, height: 30)
        jumpBtn.backgroundColor = .red
        jumpBtn.setTitleColor(UIColor.white, for: .normal)
        //按钮绑定事件
        jumpBtn.addTarget(self, action: #selector(pageReturn), for: .touchDown)
        self.view.addSubview(jumpBtn)
    }
    
    @objc func pageReturn() {
        print("dest to main")
        //返回主页面
        self.navigationController?.popViewController(animated: true)
        
    }
    
}

但是如果这时编译页面,在模拟器中点击Next按钮,发现并没有任何事情发生😅这块找了半天才找到解决办法,最后发现,是没有实例化UINavigationController,其实看到navigationController是一个可选类型就可以猜到一二。

接下来,该在什么地方实例化这个UINavigationController呢?如果你直接在页面实例化,发现一样不管用。类比一下react里的路由(react-router 4.0之前的版本),就会发现,导航其实是一个全局的东西,来管理所有的界面堆栈,而不是存在于某一个页面内。如果放入某一个页面内,每次加载都是实例化的一个新UINavigationController,它在其他页面是没法被拿到的。如这个例子中,如果在第一页实例化一个导航控制器,在第二页中是拿不到的。所以,要解决这个问题,就要实例化一个全局的UINavigationController

我们可以在AppDelegate.swift中实例化这个全局导航器。如果有SceneDelegate.swift中实例化。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }

        let vc = ViewController()
        let navRoot = UINavigationController(rootViewController: vc)
        self.window?.rootViewController = navRoot
        self.window?.backgroundColor = UIColor.white
    }
    ...
    ...
}

即通过给window的rootViewController赋值一个UINavigationController实例,就可以在所有页面使用navigationController了。这里其实是重写了应用启动后的默认行为,所以需要在实例化UINavigationController时指定一下根试图。然后给个背景色,否则是黑的😓。这样就可以了,其他页面直接使用pushViewControllerpopViewController就可以了。

还有两个有用的跳转方法

//    第三个按钮绑定的方法,根据全局序号,查找堆栈中指定序号的视图控制器
    @objc func gotoIndexPage(){
        let viewController = self.navigationController?.viewControllers[1]
        self.navigationController?.popToViewController(viewController!, animated: true)
    }
    //    创建第四个按钮绑定的方法,所有子视图出栈
    @objc func gotoRootPage(){
        self.navigationController?.popToRootViewController(animated: true)
    }

3. segue

使用segue进行跳转第一步必须在storyboard中创建segue,暂不讨论。