本文由CocoaChina翻译小组@TurtleFromMars翻译自raywenderlich,原文:Storyboards Tutorial in Swift: Part 2

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

2014/12/5更新:更新至 Xcode 6.2 Beta。

如果你想学习Storyboard,你来对地方了!

本系列Storyboard教程的第一部分,我们已经学习了如何使用Interface Builder创建并连接不同的视图控制器,还有如何直接在Storyboard编辑器中创建自定义表项。

本教程的第二部分,也是最终部分,内容包括segue(转场),static table view cell(静态表项),添加玩家页面和游戏选择页面!

我们从上部分结束的地方开始,请先打开之前的项目,或者下载上半部分教程的示例代码

好,现在让我们一起探索Storyboard的其他酷炫特性吧!

转场(Segue)

让我们向Storyboard中继续添加视图控制器,创建一个让用户添加新玩家的页面。

打开Main.storyboard,在包含表视图的那个Players场景的导航栏右侧拖入一个Bar Button Item(栏按钮项),在属性检查器中将Identifier设为Add,使其成为标准添加(加号)按钮。

当用户点按这个按钮时,你希望App会弹出一个模态页面让用户输入新玩家的详细信息。

在Players场景的右边拖入一个新的Navigation Controller(导航控制器)。记得双击面板可以缩放画面腾出空间。新加入的导航控制器附带一个表视图控制器,很方便。

这里有个小技巧:选择刚才在Players页面里加入的加号按钮,按住control键把它拖向新建的导航控制器,松手,在弹出的小选单中选择modal(模态)。

还记得吗:当Storyboard面板处于缩小状态时,无法添加或修改内容。如果在创建转场时遇到问题,请尝试双击放大!

现在Players页面和导航控制器之间多了一个新箭头。

这 种连接的类型叫做segue(转场,读作seg-way,源自电影术语,原指两个场景间的过渡衔接),表示一个页面到另一个页面的过渡。此前我们所见的 Storyboard连接描述的都是视图控制器的包含关系,而转场是用来切换页面的。转场可以由点击按钮、表项、手势等条件触发。

使用转场的好处是,再也不用为呈现新页面写代码了,也不用把按钮连接到IBAction方法上,你只需要在Storyboard中从一个栏按钮项拖到下一个页面就可以创建过渡了。(注:如果你的控件已经绑定了IBAction连接,该连接会被转场屏蔽。)

运行App,点击加号按钮,一个新的表视图会从屏幕下方滑入。

这就是所谓的模态转场。新页面完全覆盖原页面,在关闭模态页面之前,用户只能在新页面进行交互。后面我们还会看到push(入栈)转场,这种转场会把新页面压入导航控制器的导航栈(navigation stack)。

现在新页面还没什么用,连关闭页面返回都做不到,有去无回,因为转场是单向操作。

为返回页面,Storyboard提供了unwind(回退)转场。接下来我们要实现返回功能,主要分三个步骤:

1.  创建让用户点选的控件,通常是个按钮。

2.  在你想返回的控制器创建回退方法。

3.  在Storyboard中将控件与回退方法连接。

首 先打开Main.storyboard,选择新的表视图控制器场景(叫“Root View Controller”的那个)。双击导航栏,把标题改成“Add Player”。然后在导航栏添加两个栏按钮项,在属性检查器中设置左侧按钮的Identifier为Cancel,右侧按钮为Done,并将右侧按钮的 Style改成Done。

接 下来在项目中用Cocoa Touch Class模板添加一个新文件,命名为PlayerDetailsViewController并令其继承 `UITableViewController`。要把这个类关联到Storyboard,先切回Main.storyboard,选择添加玩家的场景, 然后在身份检查器(Identity inspector)中设Class为PlayerDetailsViewController。这个步骤我经常忘掉,在此特地提醒,还请读者牢记。

现在终于可以创建回退转场了。在PlayersViewController.swift(不是detail那个)的类定义下面添加如下的回退方法:

@IBAction func cancelToPlayersViewController(segue:UIStoryboardSegue) {
  dismissViewControllerAnimated(true, completion: nil)
}
 
@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  dismissViewControllerAnimated(true, completion: nil)
}

这两个方法在调用时都会解除这个控制器。后面你会改写`savePlayerDetail`,让它名副其实地履行自己的职责。

最后回到Interface Builder,把Cancel按钮和Done按钮连接到相应的action方法上。按住control从栏按钮拖到视图控制器上面的出口(exit)对象上,然后从弹出的选单中选择正确的action名称。

记住取消方法的方法名,创建回退转场时,App中的所有回退方法(形如`@IBAction func methodname(segue:UIStoryboardSegue)`)都会在列表中显示,所以命名方法时要多加注意,避免混淆。

运行App,点击加号按钮,然后测试Cancel和Done按钮。仅仅几行代码就可以实现如此功能。

静态表项(Static Cell)

完成这部分后,添加玩家页面会像这样:

当 然这是一个分组表视图(grouped table view),但不必为该表创建数据源,也不必为此编写`cellForRowAtIndexPath`方法,你可以直接在Interface Builder中完成设计。这个特性叫做静态表项(static cell)。

选中Add Player场景的表视图,在属性检查器中设Content为Static Cells,把Style由Plain改成Grouped,并为表视图设置两个分段(section)。

修改Sections属性值时,编辑器会复制已有的分段。(你也可以在左侧的文档大纲中选择特定分段并复制。)

最终页面每个分段应该只有一行,请在面板或文档大纲中选中并删除多余的表项。

在文档大纲中选择最上面的表视图分段,在属性选择器中设Header字段值为Player Name。

向该分段内拖入一个新的Text Field(文本字段),横向拉长并移除边框,使文本字段控件融入周围环境。设字体为 _System 17.0_ ,勾掉Adjust to Fit选项。

接 下来我们要用Xcode的Assistant Editor(辅助编辑器)功能为该文本字段在`PlayerDetailsViewController`中创建一个outlet。在 Storyboard中,点击工具栏上的按钮(图标是两个套在一起的圆圈)打开辅助编辑器,应该会自动打开 PlayerDetailsViewController.swift(如果没有,在右侧的跳转栏中选择相应文件)。

选择新建的文本字段, 按住control拖到swift文件的类定义下面。在弹出框中将新outlet命名为nameTextField并点击Connect。在点击 Connect后Xcode会在PlayersDetailViewController类中添加属性并在Storyboard中建立连接:

为表项上的视图创建outlet对于原型表项来说可能会遇到问题,这在上一部分的教程中提到过,不过静态表项就不必担心了,因为每个静态表项都只会有唯一的实例,把子视图与视图控制器的outlet连接完全没问题。

把第二分段的静态表项的Style设为Right Detail,这会套用一个标准表项样式,双击左侧的label,把文本改为Game,然后为该表项设定Disclosure Indicator(展开方向标)附件。

仿 照刚才的Name文本字段,为右面的label("Detail"的那个)创建outlet并命名为detailLabel,该表项上的label都是常 规`UILabel`对象。在建立连接前选择Detail文本字段时可能需要多次点击,请确保选择的是label而不是整个表项。完成后如图:

添加玩家页面的最终设计效果如图:

目前在Storyboard中设计的页面尺寸都符合iPhone 5的4英寸屏幕,高度为568点。当然你的App应当在不同的屏幕尺寸下正常工作,你可以在Storyboard中预览所有的尺寸。

在工具栏上点开辅助编辑器,选择跳转栏中的Preview。点击辅助编辑器左下角的加号添加新的预览尺寸,如果想删除一个屏幕尺寸,选中并按delete键即可。

一个简单的评分App不需要什么花哨的东西,只是使用表视图控制器,页面自动缩放以填满屏幕空间。当你想为不同的屏幕尺寸适配布局时,你需要使用Auto Layout和Size Classes。

构建并运行App,你会注意到添加玩家页面依然是空白!

表视图控制器在使用静态表项时不需要数据源,而之前你用Xcode模板创建的`PlayerDetailsViewController`类中依然有部分数据源相关代码,静态表项因此无法正常工作,所以静态内容没有显示出来。我们这就来解决问题!

打开PlayerDetailsViewController.swift文件,删除这一条代码往下的所有内容(注意不要删掉类自己的括号):

// MARK: - Table view data source

现在,自从加入这个类以后Xcode显示的那几条警告(warning)也应该消失了。

运行App,检查使用静态表项的新页面。完全没有写代码,其实刚才还删了一段代码!

还 要了解一点:静态表项只在`UITableViewController`中有效,虽然Interface Builder允许你在常规`UIViewController`中的表视图对象里添加静态表项,运行时不会发挥作用,原因是 `UITableViewController`中额外实现了一些用来处理静态表项数据源的操作。在项目中误用的话Xcode甚至会拒绝编译,输出报错信 息:“Illegal Configuration: Static table views are only valid when embedded in UITableViewController instances”。

另一方面,原型表项在常规视图内的表视图中可以正常工作,但在nib中就没戏了。目前来讲,使用原型表项或静态表项就必须使用Storyboard。

你也有可能想在一个表视图中混合使用静态表项和常规的动态表项,很遗憾的是目前的SDK对此支持欠佳。如果你的App有这种需求,请参考苹果开发者官方论坛上的相关帖子寻求可行方案。

注:如果构建的页面上包含的静态表项多到无法在可视范围内全部展示,你可以在Interface Builder中直接利用滚动手势查看,这个功能可能不容易发现,但确实管用。

不过总的来说该写代码的地方只能靠代码,甚至静态表项的表视图也是如此。前面在把文本字段拖进第一个表项的时候,你可能发现尺寸不大合适,文本字段周围有一点白边,而且用户看不到文本字段的实际范围,如果正好点在边框上,没有弹出键盘,用户会感到困惑。

为避免这种情况,你应该让那一行任意位置接受的点击都可以唤出键盘。要这样做很容易,打开PlayerDetailsViewController.swift并如下添加

tableView(_:didSelectRowAtIndexPath:)`方法:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  if indexPath.section == 0 {
    nameTextField.becomeFirstResponder()
  }
}

代码的意思是如果用户点按第一个表项,App应该激活相应文本字段。该分段只有一个表项,你只需使用分段的索引。设文本字段为第一响应者会自动唤出键盘。这只是一小处用户体验优化,但就是这样一个小细节可以给用户省去一点烦恼。

小诀窍:添加delegate委托方法或重写视图控制器方法时,直接输入方法名开头的几个字母(前面不加func),即可在自动补全列表中选择正确的方法。

另外,还应该在Storyboard的属性检查器中把相应表项的Selection Style设为None(原本是Default),否则用户点按文本字段周围的边框时该行会高亮。

好啦,添加玩家页面设计完成。现在我们要实现功能。

为添加玩家页面实现功能

现在先不管Game这行,只输入玩家名称。

当用户点击Cancel按钮时,页面关闭,用户刚刚输入的数据随之作废。这部分功能直接用回退转场已经实现好了。

而当用户点击Done时,你应该创建一个新的Player对象,参照用户输入填充属性后更新玩家列表。

转场即将发生时,`prepareForSegue(:sender:)`会被调用。你可以重写这个方法,在退出视图之前将数据保存到一个新的Player对象中。

注:不要擅自调用`prepareForSegue`方法,这是UIKit通知你一个转场刚刚被触发的消息。

在PlayerDetailsViewController.swift中,先在类上添加一条属性:

var player:Player!

这条语句并不会将属性实例化,但其中的感叹号把该变量定义为隐式解包可选量(implicitly unwrapped optional),意思是该变量必须被实例化,而且你确定它在被使用前一定有值。

接下来在PlayerDetailsViewController.swift中添加以下方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: self.nameTextField.text, game: "Chess", rating: 1)
  }
}

prepareForSegue(_:sender:)`方法判断转场的标识符是否为`SavePlayerDetail`,当且仅 当判定结果为真时,创建一个新的Player实例,其中game和rating均取默认值。如果此时运行,App会崩溃,因为不存在标识符 `SavePlayerDetail`,player不会被实例化,结合前面的隐式解包可选量定义,引发运行时错误。

小提示:如果App出现诡异的崩溃问题,而且代码看起来似乎并无逻辑错误,那么可能是在代码中删除过对象或修改过对象名,以致Storyboard引用对象出错。

在Main.storyboard中,在文档大纲里找到Add Player场景,选择连接到`savePlayerDetail`这个action的回退转场,将其标识符改为`SavePlayerDetail`:

然后选择连接到`cancelToPlayersViewController`的回退转场,将其标识符改为`CancelPlayerDetail`。以供`prepareForSegue(_:sender:)`方法判断标识符。

转到PlayersViewController类,如下修改回退转场方法`savePlayerDetail(segue:)`:

@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  let playerDetailsViewController = segue.sourceViewController as PlayerDetailsViewController
 
  //add the new player to the players array
  players.append(playerDetailsViewController.player)
 
  //update the tableView
  let indexPath = NSIndexPath(forRow: players.count-1, inSection: 0)
  tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
 
  //hide the detail view controller
  dismissViewControllerAnimated(true, completion: nil)
}

这会通过传入方法的转场引用获取一个指向`PlayerDetailsViewController`的引用,并借此向数据源中使用的Player数组添加新的Player对象,然后通知表视图在末尾新增了一行,因为表视图和数据源应当保持同步。

你可能会直接调用`tableView.reloadData()`,但还是为新行插入的操作加入动画效果比较好。`UITableViewRowAnimation.Automatic`会以插入新行的位置自动选用合适的动画,十分方便。

试试看,现在应该可以向列表中加入新玩家了!

性能

现 在Storyboard已经有好几个视图控制器了,你或许会担心性能问题,不过一次载入整个Storyboard并不是什么苦活,Storyboard不 会立即实例化所有的视图控制器,立即载入的只有初始视图控制器。而由于这里的初始视图控制器是一个分页栏控制器,包含的两个视图控制器也会被载入(第一个 分页标签的Players场景和第二个分页标签的场景)。

其他视图控制器只有在转场过去的时候才会被实例化。而当关闭视图控制器的时候,它们会立即被释放,所以内存中只有活跃使用的视图控制器,就好像分别使用nib一样。

实践是检验真理的唯一标准,在PlayerDetailsViewController类中添加构造器(initializer)和析构器(deinitializer):

required init(coder aDecoder: NSCoder) {
  println("init PlayerDetailsViewController")
  super.init(coder: aDecoder)
}
 
deinit {
  println("deinit PlayerDetailsViewController")
}

你刚刚重写了`init(coder:)`和`deinit`方法,让它们向Xcode调试面板输出信息。现在运行App,打开添加玩家页面,你会发现视图控制器只有在被打开的时候才会分配。

关闭添加玩家页面的时候,无论是点击Cancel还是Done都会看到deinit析构器的`println()`输出。如果再次打开这个页面,你还会看到`init(coder:)`的输出,这样你应该相信这个事实了:视图控制器是按需加载的,就像手动载入nib一样。

注: 如果你以前用过nib,那么你应该会很熟悉构造器`init(coder:)`,这部分机制延续到了Storyboard中:使用的方法依然是 `init(coder:)`,`awakeFromNib()`和`viewDidLoad()`。Storyboard可以看成附带了过渡信息和关联 信息的一系列nib的集合,而Storyboard内的视图和视图控制器使用与nib相同的方式编码并解析。

游戏选择页面

在添加玩家页面中点选Game行应该打开一个新页面并让用户从列表中选择一个游戏,这意味着下一步要加入另外一个表视图控制器,不过这次的页面不是模态显示,而是压入导航栈。

向 Storyboard中拖入一个新的表视图控制器,在添加玩家页面中选择Game表项(确保选中的是整个表项,而不是其中的label),然后按住 control拖到新建的表视图控制器,在两者之间创建转场。在弹出的选单中选择转场类型为Push,然后在属性检查器中把转场的Identifier标 识符设为PickGame。

双击导航栏,将新场景命名为Choose Game。设原型表项的Style为Basic(基本),设重用标识符为GameCell,如图:

在 项目中使用Cocoa Touch Class模板新建一个Swift文件,命名为GamePickerViewController,继承UITableViewController。回 到Storyboard中将游戏选择页面的Custom Class设为`GamePickerViewController`。

现在为新页面添加数据。在GamePickerViewController.swift中,在开头添加games属性,然后重写viewDidLoad函数,像这样:

var games:[String]!
 
override func viewDidLoad() {
  super.viewDidLoad()
  games = ["Angry Birds",
           "Chess",
           "Russian Roulette",
           "Spin the Bottle",
           "Texas Hold'em Poker",
           "Tic-Tac-Toe"]
}

你刚刚新增了一个叫做`games`的字符串数组,并在`viewDidLoad()`中用写定的内容填充数组。

然后如下替换数据源方法:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  return 1
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return games.count
}
 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
  return cell
}

上述代码将`games`数组设为数据源并替换表项的textLabel中的字符串值。

只要数据源准备就绪就应该能正常工作。运行App,点选Game行,新的游戏选择页面会滑入屏幕。现在点击各项不会有什么效果,但由于该页面呈现在导航栈上,你可以直接点击返回按钮,返回原来的添加玩家页面。

不用写代码就可以唤出新页面,是不是很赞?只要按住control从静态表项拖到新场景,写的代码只有填充表视图的内容,而且一般来讲比原地设计好的列表要灵活些(因为games数组更方便修改)。

当然新页面要返回数据才有用,为此你要添加一个新的回退转场。

在GamePickerViewController类的上面添加持有选中的游戏的名称和索引的属性:

var selectedGame:String? = nil
var selectedGameIndex:Int? = nil

然后修改`cellForRowAtIndexPath:`:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
 
  if indexPath.row == selectedGameIndex {
    cell.accessoryType = .Checkmark
  } else {
    cell.accessoryType = .None
  }
  return cell
}

这会在当前所选游戏对应的表项附上选中标记(对号),这对用户体验来说不可或缺。

接着添加`tableview(tableview:didSelectRowAtIndexPath:)`方法:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
 
  //Other row is selected - need to deselect it
  if let index = selectedGameIndex {
    let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
    cell?.accessoryType = .None
  }
 
  selectedGameIndex = indexPath.row
  selectedGame = games[indexPath.row]
 
  //update the checkmark for the current row
  let cell = tableView.cellForRowAtIndexPath(indexPath)
  cell?.accessoryType = .Checkmark
}

这段代码首先会取消选择刚刚点选的行,外观会从灰色高亮变回常规的白色,然后移除对号,并在刚刚点选的行上附加选中标记。

运行App,测试是否正常。点选一个游戏名,相应行会附上选中标记,点选另一个游戏名,选中标记也随之移动。

按要求来说点选某行之后应该关闭该页面,不过现在并没有自动返回,因为尚未绑定回退转场。

在PlayerDetailsViewController.swift的类上面添加一个持有被选游戏的属性,以便之后在Player对象中保存。令其默认值为"Chess",这样一来新玩家总会有一个选定的游戏。

var game:String = "Chess"

同样在该文件中改写`viewDidLoad()`以在静态表项中游戏名称:

override func viewDidLoad() {
  super.viewDidLoad()
  detailLabel.text = game
}

添加回退转场方法:

@IBAction func selectedGame(segue:UIStoryboardSegue) {
  let gamePickerViewController = segue.sourceViewController as GamePickerViewController
  if let selectedGame = gamePickerViewController.selectedGame {
    detailLabel.text = selectedGame
    game = selectedGame
  }
  self.navigationController?.popViewControllerAnimated(true)
}

上述代码会在用户从选择游戏场景选中一个游戏后执行。该方法按照选中的游戏更新页面上的label和game属性,然后将GamePickerViewController弹出导航栈。

在Main.storyboard中按住control从表项拖到Exit出口对象,然后从弹出列表中选择`selectedGame:`。

设该回退转场标识符为SaveSelectedGame。

运行App试试看,创建新玩家,点选Game行并选择一个游戏。

不 幸的是,这个回退转场方法是在`tableView(_:didSelectRowAtIndexPath:)`方法前执行的,所以 `selectedGameIndex`并未及时更新。幸运的是你可以重写`prepareForSegue(_:sender:)`方法,在转场之前完 成更新操作。

在GamePickerViewController中添加`prepareForSegue(segue:)`方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SaveSelectedGame" {
    let cell = sender as UITableViewCell
    let indexPath = tableView.indexPathForCell(cell)
    selectedGameIndex = indexPath?.row
    if let index = selectedGameIndex {
      selectedGame = games[index]
    }
  }
}

`prepareForSegue(_:sender:)`的sender参数是引发转场的对象,在这里对应选中的游戏表项,所以你可以利用表项的indexPath来在games数组中确定选中的游戏并在转场发生之前更新`selectedGame`。

现在运行App,选择游戏后玩家的游戏信息会随之更新了。

接下来改写PlayerDetailsViewController的prepareForSegue方法来返回选中的游戏,而不是写定的"Chess"。这样一来,完成添加玩家的操作后,Players场景中会显示玩家实际选择的游戏。

在PlayerDetailsViewController.swift中如下改写`prepareForSegue(_:sender:)`方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: nameTextField.text, game:game, rating: 1)
  }
}

完成添加玩家页面并点击Done后,玩家列表会更新正确的游戏信息。

还有一点,当你选择一个游戏,返回添加玩家页面,然后尝试重新选择游戏的时候,之前选定的游戏应该显示选中标记。解决方法是在转场时把PlayerDetailsViewController中保存的选中的游戏传给GamePickerViewController。

还是在PlayerDetailsViewController.swift中,于`prepareForSegue(segue:,sender:)`方法的末尾添加以下代码:

if segue.identifier == "PickGame" {
  let gamePickerViewController = segue.destinationViewController as GamePickerViewController
  gamePickerViewController.selectedGame = game
}

注意:现在有两条检查`segue.identifier`的 `if`语句。SavePlayerDetail是返回玩家列表的回退转场,PickGame是前往游戏选择页面的入栈转场。添加的代码会在 GamePickerViewController的视图加载之前更新其中的`selectedGame`。

打开GamePickerViewController.swift并在`viewDidLoad()`末尾添加以下代码:

if let game = selectedGame {
  selectedGameIndex = find(games, game)!
}

这两行代码获取从 PlayerDetailsViewController传进的selectedGame并将其转换成正确的索引。`find()`函数会在games数 组中查找匹配selectedGame的String,然后返回匹配元素的索引,赋值给selectedGameIndex,这个索引用来在对应表项上设 置选中标记。

好。现在选择游戏页面功能实现完成!

何去何从?

这是整个教程的示例项目,包含上述所有源代码。

可喜可贺,现在你已经了解Storyboard编辑器的基本用法,能够创建包含多个视图控制器并能通过转场在场景之间切换的App!在一处集中管理多个视图控制器和互相的关联,让整体把握App的样子更加容易。

你也看到了自定义表视图和表项有多么容易。有了静态表项,不用实现所有的数据源方法也可以构建一些界面。

如果想深入了解Storyboard,请参阅我们的书籍iOS 8教程,其中涵盖了最新的通用Storyboard(Universal Storyboard)。

Swift Swift语言Storyboard教程:第二部的更多相关文章

  1. Swift语言Storyboard教程:第二部

    本文由CocoaChina翻译小组@TurtleFromMars翻译自raywenderlich,原文:Storyboards Tutorial in Swift: Part 2 更新记录:该Stor ...

  2. Swift语言Storyboard教程:第一部分

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

  3. 苹果Swift语言中文教程资源汇总

    苹果swift语言中文教程(零)搭配环境以及代码执行成功http://vjiazhi.com/kaifa/1014.html 苹果Swift语言中文教程(一)基础数据类型 http://vjiazhi ...

  4. 清风注解-Swift程序设计语言

    前言 Apple 发布了全新的 Swift 程序设计语言,用来开发 iOS 和 OS X 平台的应用程序.其目的不言而喻:就是为了给老迈的 Objective-C 一个合适接班人!因此,不难预见,未来 ...

  5. Swift游戏开发实战教程(霸内部信息大学)

    Swift游戏开发实战教程(大学霸内部资料) 试读下载地址:http://pan.baidu.com/s/1sj7DvQH 介绍:本教程是国内第一本Swift游戏开发专向资料. 本教程具体解说记忆配对 ...

  6. 清风注解-Swift程序设计语言:Point11~15

    目录索引 清风注解-Swift程序设计语言 Point 11. 数值型字面量 代码事例: let decimalInteger = // 十进制的17 let binaryInteger = 0b10 ...

  7. 清风注解-Swift程序设计语言:Point6~10

    目录索引 清风注解-Swift程序设计语言 Point 6. 输出常量和变量 代码事例: // 输出的内容会在最后换行 println("hello, world") // 输出的 ...

  8. 清风注解-Swift程序设计语言:Point1~5

    目录索引 清风注解-Swift程序设计语言 Point 1. Swift 风格的"Hello, world" 代码事例: println("Hello, world&qu ...

  9. Swift程式语言(中国版)(8.8 %)

    前言 今天Apple宣布了一项新的编程语言Swift.还提供了一个近400页The Swift Programming Language(Swift程式语言). 虽然我没有开发者账户.不能实际锻炼机S ...

随机推荐

  1. 17.1 Replication Configuration

    17.1 Replication Configuration 17.1.1 How to Set Up Replication 17.1.2 Replication Formats 17.1.3 Re ...

  2. Windows Azure 网站开发Stacks支持

    编辑人员注释:本文章由 Windows Azure 网站团队的项目经理 Daria Grigoriu 和 Windows Azure 网站开发人员体验合作伙伴共同撰写. Windows Azure 网 ...

  3. c语言中scanf()、printf()函数

    函数调用scanf(“%d”,  &weight) 包含两个参数:“%d” 和&weight.C用逗号来隔开函数调用中的多个参数: 但是printf()和scanf()函数比较特殊,其 ...

  4. 2008r2 做windows域控制器

    新配一个: 1.装DNS服务. 2.装domain管理. config domain: 客户端172.16.1.34  ping zyctest

  5. POJ1700(过河问题)

    #include<iostream> #include<algorithm> using namespace std; ]; int main() { int t,i; cin ...

  6. sencha touch笔记(5)——DataView组件(1)

    1.DataView组件可以显示列表,图像等等的组件或者元素,特别适用于数据仓库频繁更新的情况.比如像显示新闻或者微博等等的很多相同样式的组件的列表这种一次性从后台或者数据源拿取很多数据展示的样式.比 ...

  7. 香蕉派 BPI-M1+ 双核开源硬件单板计算机

    香蕉派 BPI-M1+ 开源硬件开发板 深圳市源创通信技术有限公司公司 http://www.sinovoip.com.cn/cp_view.asp?id=562 产品介绍 Banana PI BPI ...

  8. Dynamic Pivot table wizard SQL Server

    原文 http://www.gyurcit.hu/pivot.html Dynamic Pivot table wizard This stored procedure generate dynami ...

  9. Stack栈的三种含义

    理解stack栈对于理解程序的执行至关重要.easy混淆的是,这个词事实上有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即 ...

  10. QTableWidget表格合并若干问题及解决方法

    Qt提供 QTableWidget作为表格的类以实现表格的基本功能,表格中所装载的每一个单元格由类QTableWidgetItem提供.这是基于表格实现 Qt提供的一个基础类,若想实现定制表格和单元格 ...