专栏名称: CainLuo
iOS
目录
相关文章推荐
普象工业设计小站  ·  前Nike旗下「潮牌卫衣」火遍欧美?!竟不到百元! ·  18 小时前  
昆明信息港  ·  近期大量上市,多人吃进急诊室!紧急提醒→ ·  昨天  
创意铺子  ·  比“真空”还舒服的运动内衣!1 ... ·  2 天前  
春城晚报  ·  胡歌得肺癌住院?工作室深夜回应! ·  3 天前  
51好读  ›  专栏  ›  CainLuo

UISplitViewController简单入门

CainLuo  · 掘金  ·  · 2019-09-19 01:50

正文

阅读 179

UISplitViewController简单入门

在邮件这个 App 里, 它在 iPad 里划分了两个区域, 左边是一个邮件列表, 右边则是对应的邮件详细内容. Apple 为我们创建了一个非常方便的 ViewController , 它的名字叫做 UISplitViewController . 在这个教程中, 我们将学习如何去使用它, 还有一个事情就是, 在 iOS 8 开始, UISplitViewController 就可以在 iPad iPhone 上运行.

在本教程中, 我们将会从头开始创建一个通用的 App , 它使用 UISplitViewController 来显示水果的列表, 我们将使用 UISplitViewController 来处理 iPad iPhone 11 Navigation 和显示的问题.

在此之前, 你应该掌握 iOS 开发的一些基础知识, 比如 AutoLayout Storyboard 的使用等等.

图片

在开始之前, 我们需要下载本教程的一些 课件 , 这里的课件共有两个, 一个是已经完成了的, 一个是准备让你去完成的.

注意: 这里使用的是 Xcode 11 , iOS 13 Swift 5 , 如需转载, 请联系作者, 侵权必究.

开始

点击 File ▸ New ▸ Project , 在 Xcode 中创建一个新的项目, 选择 iOS ▸ Application ▸ Single View App 模板

图片

将项目命名为 Fruit , 将开发语言设置为 Swift , 然后将用户界面设置为 Storyboard , 如果底下的勾选框勾选了的话, 则要取消掉.

图片

虽然我们可以选择使用 Master-Detail App 这个模板, 但是为了更好的了解 UISplitViewController 的工作原理, 所以我们将使用 Single View App 模板. 这对我们在将来的项目使用 UISplitViewController 时会更有帮助.

现在我们来创建 App 的主体 UI , 打开 Main.storyboard , 这里我们需要把系统自带的 ViewController 删除, 同时也要将项目中的 ViewController.swift 文件也删掉.

然后在 Main.storyboard 中找到 Split View Controller , 然后拖出来:

图片

图片

这里会让 Storyboard 添加几个元素:

  • Split View Controller : 第一个肯定是 UISplitViewController , 它将是这个 App 的根控制器.

  • Navigation Controller : 其次是 UINavigationController , 它将是主控制器的根视图, 在 iPad 或者是比较大尺寸的 iPhone 横屏时, 它将会显示在左侧.

    仔细点查看, 在 UISplitViewController 中, 这个具有 UINavigationController 的控制器, 是它的 Master View Controller , 这将允许我们在主视图控制器中创建整个 Navigation 的层次结果, 然后又不影响到 Detail View Controller .

  • View Controller : 这里将会显示所有水果的详细信息, 如果你仔细点查看 UISplitViewController , 你会发现 ViewController 是它的 Detail View Controller .

图片

  • Table View Controller : 这是 UINavigationController 的根视图, 它将会显示水果列表.

注意: 这个时候由于我们没有 Cell 的重用标识, 所以 Xcode 会有个警告, 这个前往别忘记了.

还有一点要注意的是, 由于我们把自带的 ViewController 给删除了, 所以我们需要告诉 Storyboard , 我们希望将 UISplitViewController 设置为初始化 ViewController .

这个时候我们选择 UISplitViewController , 然后在右侧的"属性"栏, 勾选 Is Initial View Controller :

图片

勾选了之后, 我们就会在 UISplitViewController 的左侧看到一个箭头, 这就是这个 Storyboard 的初始化控制器.

这个时候, 我们选择 iPad 模拟器, 然后将模拟器横向后, 你就会看到下面这个空白的 UISplitViewController 了:

图片

之前也说了, 自从 iOS 8 之后, 我们也可以运行在 iPhone 上, 只要它的尺寸够大就可以了, 这里我们选择 iPhone 8 Plus 模拟器, 然后就会看到效果:

图片

在横向的大尺寸 iPhone 会和 iPad 显示的效果一样之外, UISplitViewController 将会和常规的操作一样, 会有 UINavigationController Push Pop , 这都是系统帮我们实现的, 不需要我们而外再去操作,

创建自定义ViewController

现在我们已经有了 Storyboard 主要的控制器结构, 现在我们需要的是在代码上添加数据源, 然后将数据显示出来.

现在我们创建一个名为 MasterViewController UITableViewController 子类, 在创建的过程里, 我们需要把 Also create XIB file 给去掉, 因为在 Storyboard 里已经有了, 然后开发语言为 Swift , 然后就一直下一步, 到最后完成创建即可.

创建完成了之后, 我们打开 MasterViewController.swift , 删除一些不需要的代码, 然后添加一些我们需要的代码:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "FruitCell", for: indexPath)
    return cell
}
复制代码

在运行之前, 我们需要在 Storyboard 中设置一些 UITableViewController UITableViewCell 相关的东西.

  • 首先设置 RootViewController Custom Class 设置为 MasterViewController :

图片

  • 其次设置 UITableViewCell Identify FruitCell , 就和我上们面的代码一致, 然后再设置 Style Basic :

图片

现在我们可以运行一下, 这个时候你会发现总共有十行, 每行的标题都是一样的, 而且每当我们点击任何的一行都不会发生任何事情.

图片

这是因为我们还没有创建 DetailViewController , 现在我们来创建对应的 DetailViewController , 和上面一样, 这个过程就忽略掉, 只是名称为 DetailViewController .

创建完成之后, 我们按照同样的方式, 在 Main.storyboard 中, 给 DetailViewController 设置 Custom Class DetailViewController .

为了让我们的 DetailViewController 能够有东西显示, 这里加一个 UILabel 然后, 写上全世界程序员第一句学的代码 Hello Wirkd! , 然后在 iPad 模拟器上运行:

图片

现在每当我们点击一下 UITableViewCell , 在 DetailViewController 中就会显示一个 Hello World! .

制作数据模型

基础知识讲完了, 接下来是来制作我们需要显示的数据模型, 由于现在我们是在演示, 所以这里的数据模型不会很复杂, 更加不会使用到数据持久化.

首先我们需要创建一个名为 Fruit 类, 这里使用的模板是:

图片

我们创建的这个类, 其中包含了水果的图片, 名称, 介绍等等:

import UIKit

struct Fruit {
    let name: String
    let description: String
    let iconName: String
    
    init(name: String, description: String, iconName: String) {
        self.name = name
        self.description = description
        self.iconName = iconName
    }
    
    var icon: UIImage? {
        return UIImage(named: iconName)
    }
}
复制代码

现在回到我们的 MasterViewController .

显示水果列表

打开 MasterViewController.swift 之后, 我们需要新增一个 let 的属性:

let fruits = [
    Fruit(name: "Apple", description: "这是苹果", iconName: "Apple"),
    Fruit(name: "Banana", description: "这是香蕉", iconName: "Banana"),
    Fruit(name: "Blackberry", description: "这是黑莓", iconName: "Blackberry"),
    Fruit(name: "Cherries", description: "这是樱桃", iconName: "Cherries"),
    Fruit(name: "Coconut", description: "这是椰子", iconName: "Coconut"),
    Fruit(name: "Grapes", description: "这是葡萄", iconName: "Grapes")
]
复制代码

这是我们用来显示的具体数据数组.

然后我们找到 tableView(_:numberOfRowsInSection:) 并将return语句替换为以下内容:

return fruits.count
复制代码

接下来, 我们需要将名称显示到 UITableViewCell 中, 找到 tableView(_:cellForRowAtIndexPath:) , 并且在 return 的语句之前添加下面的代码:

let fruti = fruits[indexPath.row]
cell.textLabel?.text = fruti.name
复制代码

这将会把水果的名称显示到 UITableViewCell 中, 现在我们运行一下项目:

图片

现在我们成功的将水果名称显示在 UITableViewCell 中了.

更改 MasterViewController 的标题

为了让 MasterViewController 的标题看起来更加的贴合, 我们可以修改一下它的标题, 修改标题有两种方式, 第一种是直接在 Storyboard 中修改:

图片

第二种是在代码上修改:

override func viewDidLoad() {
    super.viewDidLoad()

    title = "Fruit List"
}
复制代码

无论哪种都可以, 但如果你所在的公司有固定的代码规范, 那就按照公司的规范来.

显示水果的详细内容

现在在 TableView 中我们已经显示了水果的名称, 现在我们是时候来完善每当点击 Cell 的时候, DetailViewController 则会显示对应的内容了.

打开 Main.storyboard , 将原来我们添加到 DetailViewController 里面的内容删掉, 然后再添加我们所需要展示的内容:

图片

下面是我们需要添加的内容:

  • 最左边是用来显示水果样子的 UIImageView , 它的尺寸是 128x128
  • 右边最上面的是用来显示水果名称的 UILabel , 它用的是系统粗体, 字号为 28
  • 右边最下面的是用来显示水果详情的 UILabel , 它用的是系统常规, 字号为 17
  • 这里使用了两个 UIStackView , 第一个是用在两个 UILabel 里, 排序方式是竖向的, 并且设置它们之间的间距为 10 , 第二个则是用在 UIImageView 和第一个 UIStackView , 它的排序方式是横向的, 并且设置它们之间的间距为 15 .

这里使用 UIStackView 可以帮助我们省下很多使用 AutoLayout 的布局问题, 现在布局完成了, 我们来将 UIKit 控件和 DetailViewController 关联.

打开 DetailViewController.swift , 然后将下面的代码添加进去:

@IBOutlet weak var imageView: UIImageView!

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!

var furti: Fruit? {
    didSet {
        refreshUI()
    }
}

private func refreshUI() {
  loadViewIfNeeded()
  nameLabel.text = furti?.name
  descriptionLabel.text = furti?.description
  imageView.image = furti?.icon
}
复制代码

接下来, 我们则需要在 Storyboard 中, 去关联这里的属性:

图片

现在, 我们准备就绪了, 只差将数据显示就可以了, 打开 SceneDelegate.swift , 然后将下面的代码替换 scene(_:willConnectTo:options :) 的内部实现:

guard
  let splitViewController = window?.rootViewController as? UISplitViewController,
  let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
  let masterViewController = leftNavController.viewControllers.first as? MasterViewController,
  let detailViewController = (splitViewController.viewControllers.last as? UINavigationController)?.topViewController as? DetailViewController
  else { fatalError() }

let firstMonster = masterViewController.fruits.first
detailViewController.furti = firstMonster
复制代码

UISplitViewController 中, 它有一个名为 ViewControllers 的属性, 其中是包含了 MasterViewController DetailViewController , 在我们这个情况下, MasterViewController 实际上就是 NavigationController , 所以我们如果要获取真正的 MasterViewController 实例, 就需要获得 NavigationController 中的一个 ViewController .

要获取 DetailViewController 也是使用同样的方式, 只不过是获取 UISplitViewController 中的 ViewControllers 最后一个 ViewController .

现在我们运行项目, 就可以看到有关于水果的详情信息:

图片

但现在我们又面临了一个问题, 无论我们点击哪个 UITableViewCell , 都只会显示苹果的信息, 接下来我们就需要解决这个问题.

使用delegate完善DetailViewController的显示内容

关于两个控制器之间的通信方式有很多种, 在 Master-Detail App 的模板中, MasterViewController 有着对 DetailViewController 的引用, 这就意味着 MasterViewController 可以在 DetailViewController 上设置属性了.

这如果只是在一个简单的 ViewController 中则可以直接使用, 但在我们这种情况, 还是遵循 UISplitViewController 类引用中的建议方法来处理, 那就是添加 delegate .

打开 MasterViewController.swift , 并且在这个类的上面添加下面的代码:

protocol FruitSelectionDelegate: class {
    func furitSelected(_ newFurit: Fruit)
}
复制代码

这定义了一个带有名为 furitSelected 方法的协议, 我们将会在 DetailViewController 中实现这个方法, 并且在 MasterViewController 中将用户选择的水果发送过去.

接下来, 我们需要定义一个 delegate 属性:

weak var delegate: FruitSelectionDelegate






请到「今天看啥」查看全文