这是一篇swift入门教程,顶10篇文章?虾米?怎么没有swift语法,看下去,你之前的理解都是错的。

更新记录:该Storyboard教程由Caroline Begbie更新iOS 8和Swift相关内容。原文作者为教程编纂组的成员Matthijs Hollemans

2014/12/10更新:更新至Xcode 6.1.1。

Storyboard是最先在iOS 5引入的一项振奋人心的特性,大幅缩减构建App用户界面所需的时间。

要介绍Storyboard是什么,我打算从这张图讲起。下面是您将会在本教程中构建的Storyboard:

或许你现在并不清楚这个App是用来做什么的,但其中有哪些页面,还有页面间的关联都一目了然。这就是使用Storyboard的力量。

如果App中包括很多不同的页面,使用Storyboard可以帮你减少实现页面间跳转的胶合代码。过去的开发者对应每个视图控制器分别创建界面设计文件(即“nib”或“xib”文件),现在,只要一个Storyboard就可以包揽所有视图控制器的界面设计和他们之间的关联。

Storyboard有很多优点:

  • 使用Storyboard可以更好地了解App中所有的视图以及它们之间的关联的概况。掌控全局更加容易,因为所有的设计都包含在一个文件中,而不是分散在很多单独的nib文件中。

  • Storyboard可以描述不同视图之间的过渡,这种过渡叫做“segue”(译注:意为“转场”,而“Storyboard”原意为“分镜”,均源自电影术语),你可以直接在Storyboard中通过连接不同的视图控制器来创建转场。多亏有了转场,打理界面的代码比以前要少了。

  • Storyboard通过新的原型表项(prototype cell)和静态表项(static cell)特性,让处理表视图(table view)的工作更加轻松。几乎完全可以在Storyboard编辑器里搞定表视图的设计,同样也减少了代码量。

  • Storyboard使自动布局(Auto Layout)更易用。自动布局功能可以让你通过界面元素之间的数学关系定义来确定元素的位置和尺寸,极大简化了不同尺寸屏幕的适配工作。自动布局不在本教程范围之内,若想了解更多,请参阅自动布局入门

如果你非常讨厌Interface Builder,或者推崇用代码搞定所有界面的话,Storyboard可能不适合你。个人主张是代码能少写就少写,特别是UI代码,所以Storyboard简直就是为我准备的一把利器。

如果你想继续使用nib,那就继续用吧,要知道Storyboard里是可以使用nib的,两者并非互斥关系。

本教程中,你会了解Storyboard可以做什么,我们将构建一个简单的App,功能大致是创建玩家列表和游戏列表,然后给玩家技能评分。过程中你会学到大多数可以用Storyboard完成的最常见的任务。

准备开始

打开Xcode,创建新项目。选用Single View Application模板:

如下填写模板选项:

  • Product Name:ratings

  • Organization Name: 随意填写

  • Company Identifier: 你的App使用的标识符,逆域名记法

  • Language:Swift

  • Devices:iPhone

  • Use Core Data: 不选

项目创建完成后,Xcode的主界面应该如下图所示:

这个新项目包含2个类:AppDelegate 和 ViewController, 此外还有本教程的主角: Main.storyboard 文件。

这是一个只支持竖屏显示的App,所以在继续之前,在项目综合设置上面看到的 Deployment Info - Device Orientation下面把 Landscape Left和Landscape Right 选项勾掉。

接下来我们看一下Storyboard,点击项目浏览器中的 Main.storyboard 就可以在Interface Builder中打开。

一个视图控制器在Storyboard中的官方术语是“场景(scene)”,但这两种叫法是相通的。一个视图控制器在Storyboard中可以叫做场景。

这里可以看到一个包含空视图的视图控制器。在这个视图控制器左边指向它的箭头表明它是这个Storyboard中要显示的第一个视图控制器。

在Storyboard编辑器中设计布局的方法是从右下角的Object Library(对象库)中把控件拖入视图控制器,非常容易。

注:你会注意到默认场景是一个正方形。Xcode 6默认为Storyboard和nib文件开启自动布局(Auto Layout)和尺寸归类(Size Classes)。自动布局和尺寸归类这两项新技术可以构建易于调整大小的用户界面,这对支持不同尺寸的iPhone和iPad非常有用。

自动布局由iOS 6引入,尺寸归类由iOS 8引入。两者都需要一定的学习曲线,所以本教程中暂不使用,但为了支持不同的设备尺寸,以后还是要接触到的。要了解更多,请参阅我们的书籍iOS 8教程在继续探索之前,先在当前Storyboard的File inspector(文件检查器)中禁用Auto Layout和Size Classes,如图:

Xcode询问操作时,选择保留iPhone的尺寸归类数据,然后点击disable Size Classes

现在,场景变成了4英寸iPhone尺寸的样子。

从右下方的对象库里把一些控件拖到空的视图控制器上,感受一下Storyboard编辑器的工作方式:

控件拖进来之后应该会在左边的文档大纲(Document Outline)中显示:

如果没看到文档大纲,请点击Storyboard面板左下角的这个按钮:

Storyboard显示所有视图控制器的内容,当前的Storyboard中仅有一个视图控制器(场景),在本教程后面我们会添加其他场景。

在场景上面还有一个缩小的文档大纲,称作Dock:

Dock显示场景中最上层的对象,每个视图都至少有一个 视图控制器(View Controller) 对象,一个 第一响应者(First Responder) 对象,一个 出口(Exit) 项。除此之外也可以有其他的最上层对象。Dock方便连接outlet和action,当你想把某个对象连接到视图控制器时,只需把它拖到Dock的图标上。

注:你可能不常用到First Responder。这是指任意对象在任意时间具有第一响应状态的代理对象。举个例子,把一个按钮的Touch Up Inside事件拖到First Responder的cut:选择器上。如果在某时有一个文本字段具有输入焦点,此时按下该按钮,就可以让该文本字段,也就是现在的第一响应者,把其中的文本剪切到剪贴板。

运行App,它看起来应该和你在编辑器中设计的样子相同(截图可能与你的不同,仅供演示参考,教程后面不会用到):

你定义的这个视图控制器被设定为初始视图控制器,但App是如何加载的呢?答案就在应用代理(application delegate)当中,打开AppDelegate.swift,你会看到如下代码:


1
2
3
4
5
6
7
8
9
10
importUIKit
@UIApplicationMain
classAppDelegate:UIResponder,UIApplicationDelegate{
var window:UIWindow?
funcapplication(application:UIApplication!,didFinishLaunchingWithOptionslaunchOptions:NSDictionary!)->Bool{
//Overridepointforcustomizationafterapplicationlaunch.
return true
}

上面的@UIApplicationMain标记指定这个AppDelegate类为该模块的入口。使用Storyboard时,应用代理必须继承UIResponder,必须含有UIWindow属性,几乎所有的方法都是空的,甚至application(_:didFinishLaunchingWithOptions:)也只是返回true而已。

秘密藏在 Info.plist 文件里,在Supporting Files Group里找到并点击Info.plist,你会看到这一条:

Storyboard用UIMainStoryboardFile(即Main storyboard file base name键) 来指明App启动时必须加载的Storyboard的名称。当设置生效,UIApplication会加载对应名称的Storyboard文件,自动将该Storyboard中的初始视图控制器实例化,并将其纳入一个新的UIWindow对象中。

在General分页的Project Settings和Deployment Info中也可以看到:

接下来真正开始创建包含多个视图控制器的评分App吧。

添加分页标签

你要构建的这个评分App中含有由分页标签控制的两个视图,使用Storyboard创建分页标签非常容易。

现在这个Storyboard需要从头做起,切回 Main.storyboard 然后把刚才做的视图控制器删掉。在文档大纲中点击View Controller并按下delete键即可。

把一个 Tab Bar Controller(分页栏控制器) 从对象库拖到面板中。你可能需要让Xcode最大化,因为分页栏控制器附带两个视图控制器,需要腾出更多空间,你可以双击面板进行缩放,或者按住control点击面板,在弹出的菜单中选择缩放比例。

一个新增的分页栏控制器默认附带两个额外的视图控制器,每个分页标签一个控制器。由于UITabBarController包含一个或多个其他的视图控制器,它被称作 容器视图控制器。此外还有两种常见的容器视图控制器,Navigation Controller(导航控制器)和Split View Controller(分割视图控制器)。

容器关系由分页栏控制器和他所包含的视图控制器之间的箭头表示,如下图这个箭头上的图标表示嵌入关系。

注:如果你想一起移动分页栏控制器和附带的视图控制器的话,先缩小画面,然后按住command点击,或直接拖选多个场景,这样可以同时移动多个场景。(选中的场景轮廓为淡蓝色。)

在第一个视图控制器(当前名称为“Item 1”)中拖入一个Label(文本标签)并将其文本设为 "First Tab",同理,在第二个视图控制器中加入文本为"Second Tab"的Label,这样你就可以看到分页标签切换后的变化。

注:编辑器缩小时无法向场景内拖入控件,此时需要先在面板上双击,回到正常缩放比例。

构建,运行,你会在Console中看到类似信息:

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

幸运的是这条报错信息讲得很清楚:未设置入口,也就是刚才删除最先使用的那个场景之后没设置初始视图控制器。为解决问题,选中这个分页栏控制器,然后在Attributes Inspector(属性检查器)中选定Is Initial View Controller

注:在Xcode 6.2中,上述选项已被控件取代。先选中当前分页栏控制器,然后从对象库里把一个Storyboard Entry Point(Storyboard入口)拖上去,可以拖到控制器上面,也可以拖入文档大纲。

现在,一开始的那个箭头已经指向当前的分页栏控制器了:

注:Xcode 6.2 beta在这里可能会崩溃,如果出现问题,请选中该分页栏控制器的某个视图控制器,把入口拖上去,然后再把入口箭头拖到分页栏控制器上。

这意味着启动App时, UIApplication 会把此分页栏控制器作为主画面。运行App试一试,现在App下面有分页标签栏了,可以用分页标签在两个视图控制器之间切换。

提示:你也可以通过拖拽视图控制器之间的箭头来改变初始视图控制器。

其实在前面你也可以选用Xcode自带的标签分页式App模板(即Tabbed Application模板)创建App,但最好还是了解一下工作原理,以后有必要的时候也能手动创建分页栏控制器。

注:如果在分页栏控制器上连接超过5个场景,App在运行时会自动将其归入一个More分页标签,干净利落。

添加表视图控制器

现在附属于分页栏控制器的两个场景都是标准UIViewController实例,接下来你会把其中第一个分页标签对应的场景替换为UITableViewController在文档大纲中点选第一个视图控制器并将其删除,然后从对象库中把一个新的Table View Controller(表视图控制器)拖到原场景所在的地方。

接下来把表视图控制器放到导航控制器(Navigation Controller)中,选中表视图控制器,在Xcode菜单中选择EditorEmbed InNavigation Controller,现在面板中又加入了另一个控制器:

你也可以从对象库中拖入导航控制器后再嵌入表视图,但这个操作一般来讲使用菜单命令会更省时。

与分页栏控制器类似,导航控制器也是容器视图控制器,所以有一个关系箭头指向表视图控制器,你也可以在文档大纲中看到这个关系:

注意,嵌入表视图控制器后,Interface Builder自动给它添加了一个导航栏,因为当前视图是在导航控制器的框架中显示的。它并不是实际存在的UINavigationBar对象,只是模拟显示情况。

打开表视图控制器的属性检查器,上面可以看到 Simulated Metrics(模拟度量)选项:

Storyboard中的默认值为“Inferred(推断)”,意思是该场景在处于导航控制器中时会显示导航栏,处于分页栏控制器中时会显示分页栏等等。你可以修改这些设置,但是请记住,这只是方便你设计界面时参考的模拟显示,并不会在运行时使用,仅仅是视觉设计的辅助工具,用来表示视图最后应该是什么样子。

接下来把这两个新场景连接到分页栏控制器,按住control从分页栏控制器拖到导航控制器,松手时会弹出一个小选单,选择Relationship Segue – view controllers选项:

这会在两个场景间新建一个关系箭头,与分页栏控制器包含控制器一样,都是嵌入关系。

分页栏控制器有两个嵌入关系,分别对应两个分页标签。导航控制器上有一个表视图控制器的嵌入关系。

创建这个连接后,分页栏控制器中会添加一个新分页标签,默认名称为“Item”。在这个App中,你希望第一个分页标签对应这个新场景,直接拖动分页标签,更改顺序:

运行App试试看,现在第一个分页标签中包含一个嵌入在导航控制器中的表视图。

在添加实际功能之前,你还需要再修整一下Storyboard,将第一个分页标签命名为"Players",第二个命名为"Gestures"。不是在分页栏控制器上修改,而是在这些分页标签对应的视图控制器上修改。

将一个视图控制器连接到分页栏控制器后,在场景下面和文档大纲中会看到它被赋予的 分页栏项(Tab Bar Item) 对象,可以用来设置分页标签的标题和在分页栏控制器中看到的图标。

选中导航控制器中的分页栏项,在属性检查器中将标题设为Players:

以同样的方法把第二个分页标签对应场景栏目改名为Gestures。

一个设计精良的App应该为分页标签附上图标。教程资源中有个Image文件夹,把这个文件夹拖入项目,选择“copy items if needed”并点击Finish:

在Players分页栏项的属性检查器中选择图片Players.png

你可能已经想到了,给Gestures选择Gestures.png

嵌入导航控制器的一个视图控制器包含用于设置导航栏的Navigation Item(导航项)。在文档大纲中选择表视图控制器的导航项,在属性检查器中把Title改成Players。

或者你也可以双击导航栏直接修改title,注意你需要双击的是表视图控制器中的模拟导航栏,而不是导航控制器中的那个导航栏对象。

运行App,欣赏一下这漂亮的分页标签栏吧!一行代码也不用写哦!

原型表项(Prototype Cell)

原型表项允许你直接在Storyboard编辑器中为表视图设计自定义布局。

表视图控制器默认会带一个空的原型表项。点击它,在属性检查器中设置Style为 Subtitle(副标题)。这会立即改变表项的外观,使其包含两个Label。

Storyboard上可以堆叠很多内容,有时可能很难点击到你想选中的东西。如果遇到困难,有几种选择:第一是在面板左侧的文档大纲中选择,第二是快捷键(按住control+option+shift,点击想选择的区域后会弹出指针所指区域的所有元素),第三种选择是Xcode 6的新功能,反复点击可以在各层之间循环。

如果你之前用过表视图,还手动创建过自己的表项,你可能会将其认作UITableViewCellStyle.Subtitle样式。有了原型表项,你可以像刚才那样选择系统内建的样式,也可以自定义设计(我们稍后就要创建了)。

设置Accessory(附件,即表项右侧的附属元素)属性为 disclosure Indicator(展开方向标,即右键头),并在 Identifier(标识符) 字段中输入 PlayerCell。所有的原型表项仍然是标准UITableViewCell对象,所以它们需要一个以供重用的标识符。

运行应用……什么都没变。这没什么值得奇怪的,接下来你还需要为这个表指定一个data source(数据源),这样它才会知道要显示什么。

在项目中添加一个新文件,选择iOS/Source下的Cocoa Touch Class模板,命名为 PlayersViewController ,并确保它是UITableViewController的子类。不要选中Also create XIB file选项,因为你已经在Storyboard中设计好了,今天不用nib!选择Swift语言,点击Next,然后点击Create。

回到Storyboard,选择表视图控制器(确保你选择的是视图控制器而不是其中包含的某个视图)。在身份检查器(Identity inspector)中设置它的 Class 为 PlayersViewController。这对于在Storyboard场景中使用自定义视图控制器的子类很重要,因为如果你不这么做,你的类就都不会被使用!

此后运行App时Storyboard中加载的那个表视图控制器就是PlayersViewController类的实例。

这个表视图要显示玩家列表,所以你需要为App创建主要的数据模型:一个包含Player对象的数组。由iOS/Source下的Swift File模板添加新文件,命名为Player。

在Player.swift中追加以下代码:

10
11
12
13
14
classplayer:NSObject{
name:String
game:String
rating:Int
init(name:String,game:String,rating:Int){
self.name=name
self.game=game
self.rating=rating
super .init()
}
没什么特别的东西,Player只是容器对象,其中包含三个属性:玩家名称,进行的游戏,还有1到5星之间的评分。

接下来要创建一组Player测试对象,并在中赋值到一个数组。请使用Swift File模板创建名为SampleData的新文件,并在SampleData.swift中追加以下代码:

5
//Setupsampledata
letplayersData=[Player(name: "BillEvans" ,game: "tic-tac-toe" ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-family:Consolas,rating:4),
Player(name: "OscarPeterson" "SpintheBottle" ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-family:Consolas,rating:5),
"DaveBrubeck" "TexasHold'emPoker" ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-family:Consolas,rating:2)]

这里定义了一个叫做playersData的常量,并把写定的Player对象数组赋值给它。

现在在PlayersViewController.swift的class PlayersTableViewController: UITableViewController下面添加一个玩家数组属性,用来保存玩家列表:

1
players:[Player]=playersData

这里,你可能会在PlayersViewController中定义players变量时顺带就把示例数据准备好了,但以后数据可能源自plist或sql文件,所以,在视图控制器之外处理数据加载问题是明智之选。

现在你有一个包含多个对象的数组,可以在中绑定数据源了。还是在PlayersViewController.swift中,用以下代码替换表视图数据源方法:

7
overridefuncnumberOfSectionsInTableView(tableView:UITableView)->Int{
return 1
}
overridefunctableView(tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{
players.count
实际工作在cellForRowAtIndexPath中。用以下代码替换方法(原来的注释掉):
10
ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; font-family:Consolas,cellForRowAtIndexPathindexPath:NSIndexPath)
->UITableViewCell{
letcell=tableView.dequeueReusableCellWithIdentifier( "PlayerCell" ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-family:Consolas,forIndexPath:indexPath)
asUITableViewCell
letplayer=players[indexPath.row]asPlayer
cell.textLabel?.text=player.name
cell.detailTextLabel?.text=player.game
cell
}

dequeueReusableCellWithIdentifier(_:forIndexPath:)方法用来检查是否存在可重用的表项。如果没有,就返回一个自动分配的原型表项。你只需要提供之前在Storyboard编辑器中给原型表项设定的重用标识符,本例中对应PlayerCell。一定要设置标识符,否则无法正常工作!

运行App,现在表视图中有玩家项了!

只要写几行代码就可以使用原型表项,赞!

注:该App中只使用了一个原型表项,但如果你的列表需要显示不同种类的表项,你可以向Storyboard中另外添加原型表项。可以复制现有的表项再进行修改,也可以增大表视图的Prototype Cells属性值。记得每个表项都要设置自己的重用标识符。

设计自己的原型表项

对很多App来说使用内建的标准表项样式已经足够了,但这个App需要在表项的右侧添加一个显示评分(1星到5星)的图片。标准表项样式不支持在这里包含图片视图,所以你只能自己创建自定义设计。

切回Main.storyboard,选择表视图中的原型表项,在属性检查器中设置Style属性为Custom(自定义),随后默认的Label不见了。

首先让表项增高一些,拖动底边上的小方块或在尺寸检查器(Size inspector)中修改Row Height(行高)值,设置表项高度为55点(points)。

从Objects Library拖两个Label到表项上,把它们放到和之前的标准样式差不多的地方,你可以在属性检查器中随意设置字体和颜色。设置上面的Label文本为“Name”,下面的为“Game”。

把一个Image View(图片视图)拖到表项中,放在右面紧挨展开方向标的地方,设宽度为81点,高度不是很重要。将其Mode设为Center(在属性检查器的View下面),保证载入视图的图片不会被拉伸。

在尺寸检查器中设Label宽度为190点。Label不应盖住Image View。原型表项的最终设计大概是这个样子:

因为这是一个自定义表项,所以再也不能用中的textLabeldetailTextLabel属性来设置文本了。这些属性只在标准表项类型中有效,它们指向的label在该表项中已经不存在了。为此,你需要用tag(标记)找到相应的label。

你也可以选择创建一个继承UITableViewCell的自定义类并包含对应表项视图中的label的属性。而tag可以用来简化工作,在简单情况下是很不错的解决方案。不过本教程后面会尝试使用自定义类的方法。

在属性检查器中设置“Name”Label的tag值为100,“Game”Label为101,Image View为102.

打开PlayersViewController.swift,在后面如下添加新方法imageForrating

14
15
16
funcimageForrating(rating:Int)->UIImage?{
switch rating{
case 1:
UIImage(named: "1StarSmall" )
2:
"2StaRSSmall" )
3:
"3StaRSSmall" )
4:
"4StaRSSmall" )
5:
"5StaRSSmall" )
default :
nil
}
很简单,该方法根据评分返回不同的星级图片。依然在PlayersViewController中,如下修改tableView(_:cellForRowAtIndexPath:)方法:
16
ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; font-family:Consolas,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{
ottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-family:Consolas,forIndexPath:indexPath)asUITableViewCell //1
letplayer=players[indexPath.row]asPlayer //2
if letnameLabel=cell.viewWithTag(100)as?UILabel{ //3
nameLabel.text=player.name
}
letgameLabel=cell.viewWithTag(101)as?UILabel{
gameLabel.text=player.game
}
letratingImageView=cell.viewWithTag(102)as?UIImageView{
ratingImageView.image=self.imageForrating(player.rating)
}
cell
}

讲解一下刚才做的工作:

  1. dequeueReusableCellWithIdentifier在回收表项可重用的情况下会抽出重用标识符为PlayerCell的表项,否则创建一个新表项。

  2. 按行号查看对象并将其赋值给player

  3. 按表项上的tag找到label和图片,并参照对象填充数据。

应该可以了。现在再次运行App,大概会像这样:

嗯,看起来不大对劲,表项都重叠在一起了。你只修改了原型表项的高度,但是并没有把表视图考虑进去。这里有两个解决方案,一是改变表视图的Row Height属性,二是实现tableView(tableView:heightForRowAtIndexPath:)方法。本例中前者更合适,因为只有一种表项,而且我们已经事先了解表项的高度。

注:如果无法事先判定表项的高度,或者各行的高度可能不一致,可以使用方法。

回到Main.storyboard,在表视图的尺寸检查器中设Row Height为55点:

现在运行,看起来好多了!

哦,还有一点,如果之前修改表项高度时没有手动输入数据,而是拖动表项边上的小方块的话,表视图的行高属性也会自动随之改变。所以在构建过程中你可能并没碰到上述问题。

使用表项的子类

这个表视图用起来已经相当不错了,但我不大喜欢用tag来获取原型表项的子视图。如果可能的话,把这些label于outlet连接并使用相应属性要优雅得多。事实是可行的。

在项目中以Cocoa Touch Class模板添加一个新文件,命名为PlayerCell并令其继承UITableViewCell。不要选中创建XIB的选项,因为Storyboard里已经有表项了。

在PlayerCell类的类定义下面添加以下属性

3
@IBOutletweak gameLabel:UILabel!
nameLabel:UILabel!
ratingImageView:UIImageView!

这些变量都是IBOutlet,它们可以在Storyboard中与场景建立连接。回到Main.storyboard,选中原型表项PlayerCell,并在身份检查器中把它的class改成PlayerCell。现在每当通过dequeueReusableCellWithIdentifier(_:forIndexPath:)向表视图请求一个新表项时,它会返回PlayerCell实例而不是UITableViewCell。

注意:这里我们把类名跟重用标识符设置成一样了,都是PlayerCell。这只是因为个人喜欢保持一致,类名跟重用标识符毫不相干,如果你愿意,也可以起不同的名字。

下面令label以及image view与outlet连接。在Storyboard中切到连接检查器(Connections inspector),然后在面板或文档大纲中选择Player Cell,把连接检查器中的nameLabel outlet拖到Name label对象上。对gameLabel和ratingImageView执行同样操作。

重点:控件要连接的是表项,而不是视图控制器!当你的数据源向表视图通过索求一个新表项的时候,表视图并不是把原型表项交给你,而是复制一份给你(或是之前被纳入回收空间的一个已有表项)。

这就意味着在同一时间不止有一个PlayerCell的实例,如果把表项中的label连接到了视图控制器的outlet上,不同的label拷贝会试图使用同一个outlet,这是自找麻烦。(另一方面,把原型表项连接到视图控制器的action上是可行的,当你的表项中含有自定义按钮或者是其他UIControl时可能会用到。)

除使用连接检查器之外,你也可以按住control从PlayerCell拖到控件上,然后在弹出的选单中选择outlet名称。

现在已经绑定属性,可以稍微简化数据源的代码。在PlayersViewController中如下修改tableView(_:cellForRowAtIndexPath:)

11
asPlayerCell
cell.nameLabel.text=player.name
cell.gameLabel.text=player.game
cell.ratingImageView.image=imageForrating(player.rating)
cell
}

这就更像样了。把从接收的对象转为一个,然后就可以使用连接到label和图片视图的属性。这样使用原型表项,表视图不像以前那么乱了。

运行App试试看。看起来应该和之前一样,但在幕后,现在使用的已经是你自己的表项子类了!

何去何从?

点此链接下载本项目进行到现在的所有源代码。

继续阅读本教程的第二部分,内容包含segue(转场),static table view cell(静态表项),“添加玩家”页面,“游戏选择”页面,还有本教程完整示例项目的下载!

如果在本教程的中对某些问题不清楚,你可以阅读我们的iOS学徒系列来夯实基础,该系列中你会从头开始开始学习iOS开发者应该具备的基础知识,极为适合初学者入门,同样适用于查缺补漏。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


效率成吨提升之代码生成器-蓝湖工具神器iOS,Android,Swift,Flutter
软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘贴.待开发的功能:1.支持自动生成约束2.开发设置页面3.做一个浏览器插件,支持不需要下载整个工程,可即时操作当前蓝湖浏览页面4.支持Flutter语言模板生成5.支持更多平台,如Sketch等6.支持用户自定义语言模板
【Audio音频开发】音频基础知识及PCM技术详解
现实生活中,我们听到的声音都是时间连续的,我们称为这种信号叫模拟信号。模拟信号需要进行数字化以后才能在计算机中使用。目前我们在计算机上进行音频播放都需要依赖于音频文件。那么音频文件如何生成的呢?音频文件的生成过程是将声音信息采样、量化和编码产生的数字信号的过程,我们人耳所能听到的声音频率范围为(20Hz~20KHz),因此音频文件格式的最大带宽是20KHZ。根据奈奎斯特的理论,音频文件的采样率一般在40~50KHZ之间。奈奎斯特采样定律,又称香农采样定律。...............
见过仙女蹦迪吗?一起用python做个小仙女代码蹦迪视频
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿遍又亿遍,久久不能离开!看着小仙紫姐姐的蹦迪视频,除了一键三连还能做什么?突发奇想,能不能把舞蹈视频转成代码舞呢?说干就干,今天就手把手教大家如何把跳舞视频转成代码舞,跟着仙女姐姐一起蹦起来~视频来源:【紫颜】见过仙女蹦迪吗 【千盏】一、核心功能设计总体来说,我们需要分为以下几步完成:从B站上把小姐姐的视频下载下来对视频进行截取GIF,把截取的GIF通过ASCII Animator进行ASCII字符转换把转换的字符gif根据每
自定义ava数据集及训练与测试 完整版 时空动作/行为 视频数据集制作 yolov5, deep sort, VIA MMAction, SlowFast
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至2022年4月底。我已经将这篇博客的内容写为论文,上传至arxiv:https://arxiv.org/pdf/2204.10160.pdf欢迎大家指出我论文中的问题,特别是语法与用词问题在github上,我也上传了完整的项目:https://github.com/Whiffe/Custom-ava-dataset_Custom-Spatio-Temporally-Action-Video-Dataset关于自定义ava数据集,也是后台
【视频+源码】登录鉴权的三种方式:token、jwt、session实战分享
因为我既对接过session、cookie,也对接过JWT,今年因为工作需要也对接了gtoken的2个版本,对这方面的理解还算深入。尤其是看到官方文档评论区又小伙伴表示看不懂,所以做了这期视频内容出来:视频在这里:本期内容对应B站的开源视频因为涉及的知识点比较多,视频内容比较长。如果你觉得看视频浪费时间,可以直接阅读源码:goframe v2版本集成gtokengoframe v1版本集成gtokengoframe v2版本集成jwtgoframe v2版本session登录官方调用示例文档jwt和sess
【Android App】实战项目之仿微信的私信和群聊App附源码和演示视频 超详细必看
【Android App】实战项目之仿微信的私信和群聊App(附源码和演示视频 超详细必看)
采用MATLAB对正弦信号,语音信号进行生成、采样和恢复,利用MATLAB工具箱对混杂噪声的音频信号进行滤波
采用MATLAB对正弦信号,语音信号进行生成、采样和内插恢复,利用MATLAB工具箱对混杂噪声的音频信号进行滤波
Keras深度学习实战40——音频生成
随着移动互联网、云端存储等技术的快速发展,包含丰富信息的音频数据呈现几何级速率增长。这些海量数据在为人工分析带来困难的同时,也为音频认知、创新学习研究提供了数据基础。在本节中,我们通过构建生成模型来生成音频序列文件,从而进一步加深对序列数据处理问题的了解。
  • • 效率成吨提升之代码生成器-蓝湖工具神器…
  • • 【Audio音频开发】音频基础知识及PCM技…
  • • 见过仙女蹦迪吗?一起用python做个小仙…
  • • 【Android App】实战项目之仿抖音的短视…
  • • 自定义ava数据集及训练与测试 完整版 时…
  • • 【视频+源码】登录鉴权的三种方式:tok…
  • • 【Android App】实战项目之仿微信的私信…
  • • 零基础用Android Studio实现简单的本地…
  • • 采用MATLAB对正弦信号,语音信号进行生…
  • • Keras深度学习实战40——音频生成
  • • 视频实时行为检测——基于yolov5+deeps…
  • • 数电实验 数字电子钟设计 基于quartus …
  • • 腾讯会议使用OBS虚拟摄像头
  • • 文本生成视频Make-A-Video,根据一句话…
  • • 信号处理——MATLAB音频信号加噪、滤波
  • • 【新知实验室 - TRTC 实践】音视频互动…
  • • Keras深度学习实战39——音乐音频分类
  • • C++游戏game | 井字棋游戏坤坤版配资源…

swift+storyboard+UIImageview入门的更多相关文章

  1. HTML5 播放 RTSP 视频的实例代码

    目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是 HTML 并不标准支持 RTSP 流。本文重点给大家介绍HTML5 播放 RTSP 视频的实例代码,需要的朋友参考下吧

  2. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. 详解如何通过H5(浏览器/WebView/其他)唤起本地app

    这篇文章主要介绍了详解如何通过H5(浏览器/WebView/其他)唤起本地app的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. AmazeUI 折叠面板的实现代码

    这篇文章主要介绍了AmazeUI 折叠面板的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. 浅析HTML5中的download属性使用

    这篇文章主要介绍了浅析HTML5中的download属性使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  7. HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码

    这篇文章主要介绍了HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  8. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. html5唤起app的方法

    这篇文章主要介绍了html5唤起app的方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. HTML5拍照和摄像机功能实战详解

    这篇文章主要介绍了HTML5拍照和摄像机功能实战详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部