前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS 9 Storyboard 教程(一下)

iOS 9 Storyboard 教程(一下)

作者头像
hrscy
发布2018-08-30 12:15:28
3K0
发布2018-08-30 12:15:28
举报
文章被收录于专栏:hrscyhrscy

接iOS 9 Storyboard 教程(一上)

上篇链接

原型cell

你可以直接从storyboard编辑器中,使用原型cell你可以很容易的为你的tableViewCell设计一套自定义的布局.

现在的Table View Controller有一个空的原型cell.点击原型cell,你可以在Attributes inspector中设置它的样式(Style)和副标题(Subtitle).

在storyboard中有很多可堆叠的内容,但有时却很难点击你想确切操作的内容.如果你遇到麻烦,下面有几个选项可以帮你.第一个就是在左侧的Document Outline里,你可以选择这个item.第二个是一个方便的热键:按住control+shift并点击你感兴趣区域.会出现一个弹出框让你直接使用光标选择任何元素.

如果你之前使用过table view,兵器手动创建过cell,你可能会认出这是UITableViewCellStyle.带副标题(Subtitle)的样式.和原型cell一样,你也可以选择一个内置的cell样式,就好像你刚才做的一样,或者创建一个自定义设计(你很快机会这样做).

设置Accessory属性为Disclosure Indicator然后把Identifier属性设置PlayerCell.所有的原型cell都应该有一个可重用的标识符(identifier),这样你才能在代码里引用它们.

运行app,但是好像却没有任何改变…并不是很奇怪:你还必须为tableView添加数据源,这样它才会知道应该显示多少行数据.这正是你接下来要做的.

在工程中添加一个新文件.然后在 iOS/Source选项中,选择Cocoa Touch Class模板.给这个类命名为PlayersViewController并且把它设置为UITableViewController的子类.不选Also create XIB file.

选择Swift语言,然后点击下一步(Next)创建.

回到storyboard然后选中Table View Controller(确保你选的是实际的视图控制器而不是它里面的某一个视图).在Identity inspector里,设置它的Class是PlayersViewController.对于把刚才创建的类连接到storyboard里的自定义view controller,这是至关重要的一步.不要忘记这一步,否则你刚创建的类将不能使用!

从现在开始,当你运行app的时候,storyboard 中的table view controller就变成了PlayersViewController类的一个实例. 这个table view应该会显示一列玩家名单,所以现在你需要为这个app创建一个数据模型—一个包含Player对象的数组.使用Swift File模板在iOS/Source里为这个工程添加一个新文件.命名为Player. 替换Player.swift中代码:

代码语言:javascript
复制
import UIKitstruct Player { 
  var name: String? 
  var game: String? 
  var rating: Int init(name: String?, game: String?, rating: Int) { 
  self.name = name 
  self.game = game 
  self.rating = rating 
  }
}

这里没有发生什么特别的事.Player类是一个简单的容器对象,包含有三个属性:玩家的姓名(name),他们正在玩的游戏(game)以及一个额定1至5星的评级(rating).

接下来,你需要把一个Player对象数组赋值给PlayersViewController.使用Swift File模板为开始,创建一个新文件,命名为SampleData.把它添加到SampleData.swift的末尾.

代码语言:javascript
复制
//Set up sample datalet 
playersData = [ 
  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) ]

现在你已经定义了一个叫做playersData的常量,并且分配了一个硬编码的Player对象数组给它.

现在在PlayersViewController.swift文件里的class PlayersTableViewController: UITableViewController下面添加一个Player 数组:

代码语言:javascript
复制
var players:[Player] = playersData

当定义players变量时,你可以很容易在PlayersViewController里设置样本数据.但由于这些数据在后面也许会从一个plist文件或者SQL文件中取,所以在视图控制器外部加载数据是很明智的.

现在你有一个包含很多Player对象的数组.你可以继续在PlayersViewController里链接数据源.用下面的方法替换table view的数据源:

代码语言:javascript
复制
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
  return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
  return players.count
}

真正的工作发生在cellForRowAtIndexPath.使用下面的代码替换原来的方法:

代码语言:javascript
复制
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) 
  let player = players[indexPath.row] as Player 
  cell.textLabel?.text = player.name 
  cell.detailTextLabel?.text = player.game return cell
}

这个方法dequeueReusableCellWithIdentifier(_:forIndexPath:) 将会检查是否有可用于回收的cell.如果没有,它将自动分配一个原型cell并把它返回给你.你所需要做的就是提供可重用的标识符,你可以在storyboard编辑器里设置原型cell — 在这种情况下的PlayerCell.不要忘记设置标识符,否则这个小的方案将不会起作用! 运行app,看啊,table view上有玩家了!

只需要几行代码就可以这些原型cell.我觉得那太棒了!


Note:

在这个app,你只用到了一个原型cell,但如果你的table需要显示不同的cell,那么你可以很简单的在storyboard中添加额外的原型cell.你也可以复制已经存在的cell,使它成为一个新的cell,或者增加Table View原型cell的值属性.确保给予了每一个cell属于它自己的标识符.


设计你自己的原型cell

对于很多app来说,使用一个标准的cell样式也是可以的,但是对于这个app来说,如果你想要在cell的右手边添加一张玩家评级(1–5星)的图片.那就需要有一个(图片视图)image view,就目前来看,标准的cell样式是不支持的,所以你必须要自定义设计一个.

切换回Main.storyboard,在 table view,里选择原型cell,并且在Attributes inspector中,设置Style属性为自定义(Custom).现在默认标签已经消失了.

先使cell在高一点.也可以在Size inspector(之后选择自定义)改变Row Height 的值.或者拖动cell的底部,设置高度60.

从Objects Library 拖拽两个Label对象到cell里,把它们粗略的放到标准标签的位置.只要在Attributes Inspector选择你喜欢字体和颜色.设置顶部标签为Name,底部标间为Game.

在Document Outline里同时选中Name和Game标签,然后按住Command+点击,选择Editor\Embed In\Stack View.


Note:

堆栈视图(Stack view)是iOS9新加入的,它可以很容易的布局视图的集合样式.


拖拽一个ImageView到cell并把它放到右侧,在Size Inspector里设置它宽为81高为35.设置它的Mode在中心(Center)(在Attributes inspector下一个),这样无论你把这张图片放置到view的任何地方,它都是不伸展的.

在Document Outline里Command + 点击Stack View和Image View.选择Editor\Embed in\Stack View.Xcode将会创建一个新的水平 stack view 包含这两个控件.

选中新的水平stack view,在Attributes Inspector里改变Alignment为Centre 并且Distribution改为Equal Spacing. 现在对于这个控制器来说,包含了一些简单地自动布局.在storyboard的右侧底部点击Pin图标:

改变约束为Top: 0, Right: 20, Bottom: 0 and Left: 20.确保这四个红色指针在图片中高亮显示.点击弹出窗口底部的 Add 4 Constraints.

如果你的stack view有橙色的约束,表明它错位了.为了解决这个问题,选择水平的stack view然后选择Editor\Resolve Auto Layout Issues\Update Frames(在选中的菜单视图部分).这个stack view应该放到正确的位置上,之后橙色的约束错误就会消失了. 为了把image view within 放到 stack view里,在Document Outline里选中image view,然后选择Editor\Resolve Auto Layout Issues\Add Missing Constraints(在选中的菜单视图部分). 最终的为原型cell设计的样子看上去向下面的一样:

因为这是一个自定义设计的cell,你不能在把UITableViewCell的 textLabel 和 detailTextLabel属性放到标签里了.这些属性所指的标签不再是这个cell里的了;它们只在标准的cell类型里才合法.取而代之的是,你需要使用tag来找到这些标签. tag被用在这里更加简单.在后面的课程里,你会创建一个自定义的类,继承自UITableViewCell,并且包含对应于你的cell视图的属性. 在Attributes inspector中,设置Name标签的tag值为100,Game标签的tag值为101,以及Image View的tag值为102. 然后打开PlayersViewController.swift,在这个类的底部,添加一个新方法叫做imageForRating.如下代码:

代码语言:javascript
复制
func imageForRating(rating:Int) -> UIImage? { 
  let imageName = "\(rating)Stars" 
  return UIImage(named: imageName)
}

相当简单—根据评级显示不同的星的图标.仍然在PlayersViewController里改变tableView(_:cellForRowAtIndexPath:),如下:

代码语言:javascript
复制
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) //1 
  let player = players[indexPath.row] as Player //2 
  if let nameLabel = cell.viewWithTag(100) as? UILabel { //3
  nameLabel.text = player.name 
  } 
  if let gameLabel = cell.viewWithTag(101) as? UILabel {
   gameLabel.text = player.game 
  } 
  if let ratingImageView = cell.viewWithTag(102) as? UIImageView { 
  ratingImageView.image = self.imageForRating(player.rating) 
  } 
  return cell
}

你所做的会出现崩溃:

1.dequeueReusableCellWithIdentifier将会使用重用标识符PlayerCell,重用已经存在的cell如果不存在就创建一个新的.

2.你查找每一行对应的Player对象,并将其分配给player.

3.可以看到标签和图片的数据都来自player对象.

应该这样做.现在再一次运行app,他看上去好像下面这样:

恩…,那看起来不太对—cell显示的好像被压扁了一些.你确实改变了原型cell的高度,但 table view 却不这么认为.有两个方法解决这个问题:你可以改变 table view的高度属性,或者实现tableView(tableView:heightForRowAtIndexPath:) 方法.前者是更适用这种情况,因为我们只拥有一种类型的cell,并且我们事先知道cell的高度.


Note:

如果你事先不知道你的cell的高度,或者不同的cell有着不同的高度,你将会使用 tableView(tableView:heightForRowAtIndexPath:).


返回Main.storyboard,在Table View的Size inspector里,设置高度为60.

如果你现在运行app,它看起来好多了!

顺便说一句,如果你通过拖拽改变了cell的高度,而没有改变它的值,那么table view的行高属性也会自动改变.所以在第一次可能是正确的.

使用cell的子类

Table view已经非常好了,但是我不是使用tag来访问标签和其他cell子视图的粉丝.如果你能通过连线(outlet)连接这些标签(label),然后使用对应的属性那么它将更干净.事实证明,你可以. 在工程中添加一个新的文件,使用Cocoa Touch Class模板.命名它为PlayerCell,并且把它作为UITableViewCell的子类.不要勾选创建XIB的选项,正如你在storyboard中已经有的cell一样. 添加PlayerCell类的属性,就好像下面的类定义一样:

代码语言:javascript
复制
@IBOutlet weak var gameLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ratingImageView: UIImageView!

所有的变量都是IBOutlet类型的,它可以连接到当前storyboard中的控制器. 使用IBOutlet就好像下面这样添加属性:

代码语言:javascript
复制
var player: Player! { 
  didSet { 
    gameLabel.text = player.game 
    nameLabel.text = player.name 
    ratingImageView.image = imageForRating(player.rating) 
  }
}

无论当什么时候设置了player的属性,它都会正确地更新IBOutlet里的信息.

把imageForRating(_:)方法从PlayersViewController移动到PlayerCell类里,这样可以在一个类里保持cell的详细信息. 返回Main.storyboard,选中PlayerCell然后在Identity inspector里改变它的类为PlayerCell.

现在无论什么时候你只要改变table view数据源的dequeueReusableCellWithIdentifier(_:forIndexPath:)方法,它都会返回一个PlayerCell实例而不是返回UITableViewCell. 现在你已经给了这个类和重用标识符相同的名字 — 它们都被叫做PlayerCell — 但那只是因为我想要让它们保持一致.类名和重用标识符彼此无关,所以你也可以给它们命名成不同的名字,如果你想那么做的话.

现在将标签和图片视图都连到这些outlet上.在storyboard中导航到Connections Inspector,然后从Document Outline或者工作空间里选择PlayerCell.在Connections inspector中拖拽nameLabel.

给Document Outline中的Name标签对象,或者是拖拽到工作空间中.重复gameLabel和ratingImageView.


Important:

你应该把控件连接到table view cell上,而不是连接到view controller!你看,只要你的数据源请求table view通过dequeueReusableCellWithIdentifier创建了一个新的cell,那么这个table view将不会调用真正的原型cell,而是一个拷贝(或者如果可能的话,之前有一个cell是可回收的).

这就意味着,在任何给定的时间都有超过一个实例.如果你是将一个标签从cell连接到控制器,那么几个标签的副本将会尝试使用相同的连线.这只是要求麻烦.(在另一方面,将原型cell的活动连接到视图控制器上的动作是非常好的.如果在你的cell上有自定义的按钮或者其他控件,你将会这么做的)


现在你已经连接了这些属性,你可以简化数据源代码.在PlayersViewController,改变tableView(_:cellForRowAtIndexPath:)如下:

代码语言:javascript
复制
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) as! PlayerCell 
  let player = players[indexPath.row] as Player cell.player = player return cell
}

更像是这样.你可以从dequeueReusableCellWithIdentifier这个方法里得到一个PlayerCell的对象,然后你就可以简单地把正确的玩家信息传递到cell上.在PlayerCell里设置玩家变量将会自动地把值传递到标签和图片视图上,并且cell会使用你在storyboard里的连线.难道使用原型cell使table view变得很整洁不好么?

运行app并且尝试做一下.它依旧会和之前一样,但是在屏幕下面,它使用的是你自己的table view cell的子类!你可以在这里下载所有的源代码.

结语

如果你有任何问题都可以在下面讨论,翻译过程中有个别地方翻译不是十分准确,希望大家批评指正后面会继续更新第二部分,敬请期待!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015.11.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 接iOS 9 Storyboard 教程(一上)
  • 原型cell
    • Note:
    • 设计你自己的原型cell
      • Note:
        • Note:
        • 使用cell的子类
          • Important:
          • 结语
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档