美文网首页iOS开发iOS迷你教程
IOS迷你教程4-Storyboards一(xcode9)

IOS迷你教程4-Storyboards一(xcode9)

作者: Lebus | 来源:发表于2018-10-16 18:58 被阅读176次

    故事板是iOS 5中首次引入的令人兴奋的功能,可以节省为应用程序构建用户界面的时间。

    完整的故事板

    您可能不知道这个app的功能,但您可以看到它的场景以及它们之间的关系。

    故事板有许多优点:

    • 您可以在“场景”中以可视方式布置所有视图控制器,并描述它们之间的连接。通过故事板,您可以更好地概念化应用中的所有场景。
    • 描述各种场景之间的跳转。这些跳转称为“segues”,您可以通过连接故事板中的视图控制器来创建它们。
    • 使用原型和静态单元格功能,可以更轻松地使用表格视图。您可以在故事板编辑器中设计完整的表视图,从而减少代码量。
    • 使用Auto Layout,这一功能可以轻松适配各种屏幕尺寸的设备。之后的教程将会单独开一章来讲。

    在本教程中,您将构建一个示例app,创建一个玩家列表,并向您显示他们玩的游戏和他们的技能等级。

    入门

    打开Xcode并创建一个新项目。使用*Single View Application *模板。

    XcodeProject

    创建后,Xcode窗口应如下:

    主窗口

    新项目包含三个文件,AppDelegate.swiftViewController.swift和本教程的焦点:Main.storyboard

    在 General > Deployment Info > Device Orientation下,将设备设置为iPhone。由于这是一个仅有纵向屏幕的app,因此取消选中Landscape LandscapeLandscape Right选项。

    打开Main.storyboard

    主要故事板

    视图控制器的官方术语是“场景”。

    现在看到的时包含空视图的一个视图控制器。左侧的箭头表示这个是app启动之后的初始视图控制器。

    在故事板中是通过将对象库中的控件(参见下图右下角)拖动到视图控制器中来完成的。

    默认场景是4.7英寸的屏幕。Xcode默认为故事板启用自动布局和大小类。自动布局和大小类允许您制作可轻松调整大小的灵活用户界面,这对于适配各种尺寸的iPhone和iPad非常有用。要将场景大小更改为其他设备,请单击故事板左下角的按钮。然后,您将能够从纵向和横向两种方式中选择支持的全系列设备,从iPad Pro(12.9英寸)到iPhone 4S(3.5英寸)。

    大小类

    对于本教程,我们将保持默认场景大小 - iPhone 7 。

    要了解故事板编辑器的工作方式,请将对象库中的一些控件拖到空白视图控制器中:

    拖动控件

    拖动控件后,它们会显示在左侧的Document Outline中:

    Document Outline

    故事板显示了所有场景的内容。目前,故事板中只有一个场景,但在本教程中,您将添加其他几个场景。

    这个Document Outline的微缩版本在场景上方,称为Dock

    Dock

    Dock展示了场景中的顶级对象。每个场景至少具有一个View Controller对象,一个First Responder对象和一个Exit对象。它也可能包含其他顶级对象。该Dock是方便制作到出口和动作连接。如果您需要将某些内容连接到场景,只需将其拖动到Dock中的图标即可。

    构建并运行应用程序,它应该与您在编辑器中设计的内容完全一样(您的内容可能与下面的屏幕截图不同):

    模拟器测试

    现在用几个视图控制器创建真正的评级应用程序。

    只需添加到tab

    您即将构建的评级应用程序具有两个场景的tab式界面。使用故事板,可以轻松创建tab。

    打开Main.storyboard并删除之前使用过的场景。这可以通过单击Document Outline中的视图控制器并按删除键。

    Tab Bar Controller对象库拖到画布中。

    标签栏控制器

    新的Tab Bar Controller预先配置了两个额外的视图控制器 - 每个选项卡一个。UITabBarController是一个所谓的容器视图控制器,因为它包含一个或多个其他视图控制器。另外两个常见的容器是导航控制器(Navigation Controller)和拆分视图控制器(Split View Controller)(稍后您将使用导航控制器)。

    容器内的关系由Tab Bar Controller和它包含的视图控制器之间的箭头表示。

    将一个label控件拖动到第一个视图控制器(现在的标题为“item1”),双击它,并为其指定文本“First Tab”。接下来,将另一个label控件拖到第二个视图控制器(“item2”)中,并为其指定文本“second Tab”。这样您可以在选项卡切换时看到他们发生的事情。

    构建并运行应用程序。您将在控制台中看到:

    Ratings[18955:1293100] Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?

    幸运的是,错误在这里非常清楚 - 您从未设置入口点,这意味着在删除上一个场景后没有设置初始视图控制器。要解决此问题,请点击Tab Bar Controller,在右侧栏的Attributes Inspector并选中Is Initial View Controller

    初始视图控制器

    在画布中,箭头现在指向Tab Bar Controller:

    是初始视图控制器

    构建并运行应用程序。现在您可以看到一个标签栏,可以在两个视图控制器之间切换:

    带标签的应用

    注意:要更改初始视图控制器,还可以在拖动视图控制器之间的箭头。

    Xcode附带了一个用于构建tab类型的app的模板(称为Tabbed Application template)。您也可以使用它,但本节为了学习使用storyboard,我们手动创建一个Tab Bar Controller。

    注意:如果超过五个选项卡,则在运行应用程序时它会自动出现一个更多按钮的选项卡。很简单!

    添加Table View Controller

    当前附加到选项卡栏控制器的两个场景都是UIViewController实例。我们将替换第一个场景为UITableViewController

    单击第一个视图控制器将其选中,然后将其删除。将Table View Controller拖到画布中:

    表视图控制器

    接下来,您要将表视图控制器放在导航控制器中。首先,选择Table View Controller。接下来,从Xcode的菜单栏中选择Editor \ Embed In \ Navigation Controller。这为画布添加了另一个控制器:

    导航控制器

    由于导航控制器也是一个容器视图控制器(就像标签栏控制器一样),它有一个指向表视图控制器的关系箭头。您还可以在文档大纲中看到这些关系:

    关系

    注意:把表视图控制器嵌入(Embed)进一个导航控制器,会自动给它一个导航栏。

    要将这两个新场景连接到标签栏控制器,请单击标签栏控制器并按住Ctrl键拖动到导航控制器。松开后,会出现一个弹出菜单。选择Relationship Segue – view controllers选项:

    嵌入VC

    这会在两个场景之间创建一个新的关系箭头。当您看到Tab Bar Controller包含的其他控制器时,这也是一个嵌入式关系。

    选项卡栏控制器有两个嵌入关系,每个tab各嵌入一个。导航控制器本身与表视图控制器具有嵌入关系。

    进行此新连接后,标签栏控制器中添加了一个新选项卡,名为“item”。您希望此场景成为第一个选项卡,因此请拖动选项卡以更改其顺序:

    拖动标签项

    构建并运行应用程序以进行尝试。第一个选项卡现在包含导航控制器内的表视图。

    SimulatorFirstTabWithTableView

    在将一些实际功能放入此应用程序之前,您需要稍微清理一下故事板。您将命名第一个选项卡“Players”和第二个“Gestures”。您不要在标签栏控制器上更改,而是具体的视图控制器中更改。

    只要将视图控制器连接到选项卡栏控制器,就会给它一个Tab Bar Item对象,您可以在文档大纲或场景底部看到它。在右侧栏可以配置选项卡的标题和图像。

    选择导航控制器内的Tab Bar Item,然后在右侧Attributes inspector中将其标题设置为“ Players”:

    接下来,将第二个选项卡的Tab Bar Item重命名为Gestures,方法与上面相同。

    精心设计的应用程序还应在这些选项卡上添加图标。点击下载一个名为Images的文件夹。将该文件夹拖到项目的Assets.xcassets中。

    将图像拖动到XCAssets

    打开Main.storyboard,在“players 的 tab bar item”的属性检查器中,选择图像。

    接下来,给Gestures标签栏项目图像Gestures

    嵌入在导航控制器内的视图控制器具有用于配置导航栏的导航项。在“ 文档大纲”中选择“表视图控制器”的“导航项”,并在“属性”检查器中将其标题更改为“ Players”。。

    navigation item

    文档大纲”中的“场景”标题现在也自动更改为“ Players”

    注意:您可以双击导航栏并在那里更改标题。

    构建并运行应用程序。现在看看你做的漂亮的tab栏,到现在没有编写任何代码!

    原型单元格

    原型单元格允许您直接在storyboard中为表格视图单元格轻松设计自定义布局。

    Table View Controller附带一个空白原型单元。单击单元格以选择它,然后在“ 属性”检查器中将“ Style”选项设置为“ Subtitle”。这会立即更改单元格的外观。

    注意:由于故事板上有如此多的可堆叠内容,有时很难点击您想要的内容。如果遇到麻烦,有几种选择。一个是您可以选择画布左侧的文档大纲中的项目。第二个是一个方便的热键:按住control + shift并单击你感兴趣的区域。将出现一个弹出窗口,允许您直接选择光标下的任何元素。

    Accessory属性设置为Disclosure Indicator,将Identifier设置PlayerCell。所有原型单元都必须具有重用标识符,因此您可以在代码中引用它们。此外,将单元格的Selection设置为nonet

    注意:单元格的Selection属性设置为None,以去掉PlayerCell的点击效果。

    构建并运行应用程序,没有任何改变。这并不奇怪:您仍然需要为表创建数据源,以便知道要显示哪些行。

    在项目中创建新file。在iOS / Source下选择Cocoa Touch Class模板。将类命名为PlayersViewController,并使其成为UITableViewController的子类。取消选中也创建XIB文件。选择Swift语言,然后单击Next,然后单击Create。

    打开Main.storyboard并选择Table View Controller(确保选择实际的视图控制器而不是其中一个视图)。在Identity检查器中,将其Class设置为PlayersViewController。这是使用自定义视图控制器子类从故事板连接场景的关键步骤。不要忘记这个!

    玩家VC类

    现在,当您运行应用程序时,故事板中的表视图控制器是PlayersViewController该类的实例。

    表视图应显示Player列表,因此现在您将为应用程序创建数据模型 - 包含Player对象的数组。使用iOS / Source下的Swift File模板将新文件添加到项目中,并将文件命名为Player

    用以下内容替换Player.swift中的代码:

    import Foundation
     
    struct Player {
    
      // MARK: - Properties
      var name: String?
      var game: String?
      var rating: Int
    }
    

    这里没什么特别的。Player它只是这三个属性的容器对象:玩家的名字,他们正在玩的游戏,以及1到5星的评级。请注意,因为您没有定义自定义构造器,struct将自动初始化

    接下来,使用名为SampleDataSwift File模板创建一个新文件。用以下内容替换SampleData.swift的内容:

    import Foundation
    
    final class SampleData {
    
      static func generatePlayersData() -> [Player] {
        return [
          Player(name: "Bill Evans", game: "Tic-Tac-Toe", rating: 4),
          Player(name: "Oscar Peterson", game: "Spin the Bottle", rating: 5),
          Player(name: "Dave Brubeck", game: "Texas Hold 'em Poker", rating: 2)
        ]
      }
    }
    

    在这里,您已经定义了一个静态方法SampleData来生成Player对象数组。

    接下来,打开PlayersViewController.swift并使用以下内容替换文件的内容:

    import UIKit
    
    class PlayersViewController: UITableViewController {
    
      // MARK: - Properties
      var players = SampleData.generatePlayersData()
    }
    

    您可以在PlayersViewController定义player变量时设置样本数据。但是这些数据在实际开发中可能是从plist或其他外部源(服务器)来的,因此现在也模拟这么做。

    现在你有一个Player对象的数组,继续连接数据源到PlayersViewController。在PlayersViewController.swift中,将以下extension添加到文件末尾:

    // MARK: - UITableViewDataSource
    extension PlayersViewController {
    
      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return players.count
      }
    
      override func tableView(_ tableView: UITableView,
                              cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath)
    
        let player = players[indexPath.row]
        cell.textLabel?.text = player.name
        cell.detailTextLabel?.text = player.game
        return cell
      }
    }
    

    该方法dequeueReusableCell(withIdentifier:for:)将重用原型单元格。您需要做的就是提供您在storyboard编辑器中的原型单元格上设置的重用标识符(identifier) - 在本例中为PlayerCell。不要忘记设置标识符,否则这个app将无效!

    构建并运行应用程序,表格视图中有玩家!

    应用程序与玩家

    使用这些原型单元格只需几行代码!

    注意:在这个应用程序中,您只使用一个原型单元格。如果您的表需要显示不同类型的单元格,则可以向故事板添加其他原型单元格。确保为每个单元格提供自己的重用标识符!

    设计自己的原型cell

    对于大多数应用程序,使用标准单元格样式都很好,但对于此应用程序,您希望在单元格的右侧添加一个显示玩家评级的图像。标准单元格样式不支持在该位置具有图像视图,因此您必须进行自定义设计。

    打开Main.storyboard,在表视图中选择原型单元格,然后在“ 属性”检查器中,将其“ 样式”属性设置为“ 自定义”。默认标签现在消失。

    首先让cell高一点。在“ 大小”检查器中更改“ 行高”值(在选中“自定义”后)或拖动单元格底部。设高为60point。

    将两个Label对象从“对象库” 拖动到单元格中,并将它们大致放置在以前标准标签的位置。只需使用属性检查器中的字体和颜色,然后选择您喜欢的内容。将顶部标签的文本设置为Name,将底部标签设置为Game

    使用Command +单击选择Document Outline中的Name和Game标签,然后选择Editor \ Embed In \ Stack View

    image view控件拖动到单元格中,并将其放在cell的右侧。在尺寸检查器中,使其宽81点,高35点。将其“ content mode”设置为“ center”(在“属性”检查器中的“view”下),这样您放入此视图的任何图像都不会被拉伸。

    按住Command并单击“ 文档大纲 ”中的“Stack View”和“image view” 以选择它们。选择编辑器\嵌入\ Stack View。Xcode将创建一个包含这两个控件的水平堆栈视图。

    堆栈视图

    选择此新的水平堆栈视图,然后在“ 属性”检查器中,将“alignment”更改为“ center”,将“Distribution”更改为“ Equal Spacing”

    现在为这个控件的一些简单的自动布局。在故事板的右下角,单击图钉图标:

    图钉图标

    将顶部约束更改为Top:0,Right:20,Bottom:0和Left:20。确保如图所示的四个红色指针。单击弹出窗口底部的“ 添加4个约束 ”。

    StackView约束

    如果堆栈视图具有橙色约束,则会放错位置。要解决此问题,请选择水平堆栈视图,然后选择编辑器\解析自动布局问题\更新框架(在菜单的选定视图部分中)。堆栈视图应正确定位,橙色约束错误消失。

    要在堆栈视图中定位图像视图,请在“ 文档大纲”中选择图像视图,然后选择“ 编辑器\解决自动布局问题\添加缺失约束”(在菜单的“选定视图”部分中)。

    您可能会在文档大纲中看到一个红色突出显示的小箭头,表示堆栈视图未解决的布局问题。单击此箭头并单击红色圆圈以查看Xcode建议的自动修复。为name或game标签的优先级选择更改优先级应该使此警告消失。

    修复拥抱优先权

    原型单元的最终设计看起来像这样:

    因为这是一个custom的cell,你不能再使用UITableViewCelltextLabeldetailTextLabel属性来放置文本标签。它们仅对标准细胞类型有效。相反,您将子类化UITableViewCell以提供功能。

    使用子类单元格

    使用Cocoa Touch Class模板向项目添加新文件。将其命名为PlayerCell并使其成为UITableViewCell的子类。不要选中创建XIB的选项,因为故事板中已有单元格。

    接下来,将以下内容添加到PlayerCell类中,就在类定义的下方:

    // MARK: - IBOutlets
    @IBOutlet weak var gameLabel: UILabel!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var ratingImageView: UIImageView!
    

    这些IBOutlets可以使用故事板连接到您的场景。

    接下来,在IBOutlets下面添加以下属性:

    // MARK: - Properties
    var player: Player? {
      didSet {
        guard let player = player else { return }
        
        gameLabel.text = player.game
        nameLabel.text = player.name
        ratingImageView.image = image(forRating: player.rating)
      }
    }
    

    每当设置player属性时,它将验证是否有值,如果是,则IBOutlets使用正确的信息更新。

    接下来,添加以下方法:

    func image(forRating rating: Int) -> UIImage? {
      let imageName = "\(rating)Stars"
      return UIImage(named: imageName)
    }
    

    这将根据提供的评级返回不同的星形图像。

    接下来,打开Main.storyboard,选择原型单元格PlayerCell并在Identity检查器中将其类更改为PlayerCell。现在,每当您向表格视图询问新单元格时dequeueReusableCell(withIdentifier:for:),它将返回PlayerCell实例而不是常规实例UITableViewCell

    注意:您为此类提供了与重用标识符相同的名称 - 它们都被调用PlayerCell- 但这只是因为我喜欢保持一致。类名和重用标识符彼此无关,因此如果您愿意,可以将它们命名为不同的名称。

    最后,将标签和图像视图连接到这些插座。到故事板中的Connections Inspector,然后从画布或文档大纲中选择Player Cell。从Connections检查器中的nameLabelOutlet 拖动到Document Outline或画布中的Name标签对象。重复gameLabelratingImageView

    名称标签

    现在您已经连接了属性,您可以稍微简化数据源代码。打开PlayersViewController.swift,并切换tableView(_:cellForRowAt:)到以下内容:

    override func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell",for: indexPath) as! PlayerCell
          
        let player = players[indexPath.row]
        cell.player = player
        return cell
    }
    

    差不多了。您现在将收到的对象转换dequeueReusableCell为一个 PlayerCell,并将正确的值传递给player单元格。使用原型单元格使表格视图变得不那么混乱。

    构建并运行应用程序。

    错误的cell高度

    嗯,这看起来不太正确 - cell似乎被压扁了。您确实更改了原型单元格的高度,但表格视图没有考虑到这一点。有两种方法可以解决它:您可以更改表视图的行高属性,或实现该tableView(_:heightForRowAt:)方法。前者在这种情况下很好,因为我们只有一种类型的单元格,我们事先知道高度。

    注意tableView(_:heightForRowAt:)如果您事先不知道细胞的高度,或者不同的行可以具有不同的高度,则可以使用。

    打开Main.storyboard,在Table View 的Size检查器中,将Row Height设置为60:

    image

    构建并运行应用程序。

    适当的行高

    ok!

    注意:如果通过拖动cell而不是键入值来更改单元格高度,则表视图的行高度属性也会自动更改。所以可能拖动更适合你。

    相关文章

      网友评论

        本文标题:IOS迷你教程4-Storyboards一(xcode9)

        本文链接:https://www.haomeiwen.com/subject/efaqzftx.html