IOS Apps 开发(Swift)(6)——Implement a Custom Control(1)

来源:互联网 发布:unity3d ugui官方demo 编辑:程序博客网 时间:2024/06/06 17:23

前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。

博客链接:mcf171的博客

原文链接:Implement a Custom Control

——————————————————————————————

在本次课程中,我们将让FoodTracker app支持打分的功能。最终完成的图如下:


学习目标

在本次课程中,你可以了解到:

  • 创建和关联自定义代码和在storyboard中的元素
  • 定义一个自定义类
  • 实现一个自定义类的初始化器
  • 使用UIView作为容器
  • 知道如何通过编程的方式展现视图

创建一个自定义视图

为了能够对一个菜品进行打分,用户需要一个控制器通过选取星星的个数来进行打分。有很多种方式可以实现这种功能,但是我们主要关注的是如何创建一个自定义视图然后在storyboard中使用。

下图是打分控制器的实现效果:


打分控制器可以让用户选择0,1,2,3,4或者5来对菜品进行打分。当一个用户点击一个星星的时候,所有的星星将从左到用户点击星星的位置。黑色的表示选中的,白色则相反。


为了设计这个UI、交互和控制器的行为,我们通过创建一个UIView的子类来实现。

创建一个UIView 的子类

1、选择File > New > File (or press Command-N).

2、在所编的对话框中选择iOS中的Source

3、选择Cocoa Touch Class,然后点击Next

4、在Class的选项中填写:RatingControl

5、在Subclass of中选取UIView

6、设置编程语言为Swift


7、下一步

选择存储的目录

Group 选项默认是 app 名称

8、点击创建

Xcode将生成一个RatingControl.swift文件

9、在RatingControl.swift中删除注释。

最终内容为:

import UIKit class RatingControl: UIView {    }

到目前为止,我们已经通过两种常见的方式创建了一个视图:通过一个框架来初始化一个视图从而使我们可以手动的将这个视图添加到我们的UI中欧哲通过允许这个视图被storyboard加载。对于框架来说相应的初始化方法是init(frame:),但是对于storyboard来说是init(coder:)?。


因为我们将在storyboard中使用这个视图,所有我们通过重写父类中以实现的构造器init?(coder:)

重写初始化器

1、在RatingControl.swift中,在类名的下一行加入以下注释

// MARK: Initialization

2、在注视下添加init

代码提示功能将会出现


3、选择第三个方法,然后回车

init?(coder aDecoder: NSCoder) {}

4、点击 error消息从而添加required 关键字


每个实现一个初始化器的UIView子类必须包括init?(coder:)的实现。Swift编辑器知道这一点,因此提供了一个解决错误的方法。Fix-its 提供了编译器对于错误的可能解决方案。

5、增加父类的初始化器

super.init(coder: aDecoder)

最终代码应该如下

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)}

显示自定义视图

为了显示我们自定义的视图,我们需要将视图添加到UI中,并确保视图和代码之间有联系。

显示视图

1、打开Storyboard

2、在我们的storyboard中 选择对象库从而找到一个 View 对象,然后将这个对象拖到我们的 storyboard中,使这个视图在 image view的下面。

3、选中这个视图,打开尺寸查看器



4、在 Intrinsic Size中选择 Placeholder

5、输入高44,宽240。那么现在效果如下


6、选中视图,点击身份查看器



7、在身份查看其中,在Class 中选择RatingControl


给视图添加按钮

到目前为止,我们以及指导如何得到一个基础的自定义UIView的子类——RatingControl。下一步是我们需要在我们的视图上添加一个按钮从而允许我们的用户进行打分。首先我们还是从简单开始。

在视图上创建一个按钮

1、在init?中增加下面的代码

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))button.backgroundColor = UIColor.redColor()

2、在最后一行添加如下代码

addSubview(button)

addSubview()方法让我们在RatingControl视图上添加了一个按钮

最后我们的init?(coder:)初始化器应该如下

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))    button.backgroundColor = UIColor.redColor()    addSubview(button)}

为了告诉 stack view 如何布局我们的按钮,我们应该提供 intrinsic 内容尺寸。具体做法是通过重写 intrinsicContentSize方法

override func intrinsicContentSize() -> CGSize {    return CGSize(width: 240, height: 44)}

里程碑:运行 app我们能看到一个有着红色防空的视图。这个红色方块就是我们初始化器里面加的按钮。


给按钮增加行为

1、在RatingControl.swift中最后一个花括号前,添加如下注释

// MARK: Button Action
2、在注释下,加入以下代码

func ratingButtonTapped(button: UIButton) {    print("Button pressed")  

到目前为止,当我们点击按钮的时候会在控制台显示消息。

3、找到init?(coder:)初始化器

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))    button.backgroundColor = UIColor.redColor()    addSubview(button)}

4、在addSubview前添加代码

button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)

上述代码将动作ratingButtonTapped方法绑定到button对象上,当.TouchDown事件发生的时候,动作将会被触发。设置的目标是self也就是RatingControl类自身。

因为我们没有使用Interface Builder 所以我们的动作方法前不需要加上IBAction 属性。

init?(coder:)方法现在为:

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))    button.backgroundColor = UIColor.redColor()    button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)    addSubview(button)}

里程碑:运行app,当点击红色方框的时候,能在控制台看见消息"Button pressed"


那么为了实现我们的打分功能RatingControl还需要一些什么信息。首先我们需要记录打分的值,其次当用户点击打分按钮的时候我们需要显示打分。我们可以用Int 来记录打分的值,使用UIButton 对象的数组来存 按钮组


增加记录打分相关的属性

1、打开RatingControl.swift,找到类声明的地方

class RatingControl: UIView {

2、在下面添加如下代码

// MARK: Properties var rating = 0var ratingButtons = [UIButton]()

到目前为止,我们已经在屏幕上有了一个按钮,为了创建5个按钮我们可以使用for-in循环


创建5个按钮

1、打开RatingControl.swift,找到 init?(coder:) 初始器

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))    button.backgroundColor = UIColor.redColor()    button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)    addSubview(button)}

2、增加for-in循环

for _ in 0..<5 {    let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))    button.backgroundColor = UIColor.redColor()    button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)    addSubview(button)}

我们可以通过Control + I 来进行代码格式化,因为在每次迭代中我们不需要使用迭代变量,所以使用下划线做占位符

3、在 addSubview上添加代码

ratingButtons += [button]

上述代码可以让每次循环创建的按钮都添加到数组中。

最早初始化器为:

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        for _ in 0..<5 {        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))        button.backgroundColor = UIColor.redColor()        button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)        ratingButtons += [button]        addSubview(button)    }}

里程碑:运行app,我们会发现还是只有一个按钮,这是因为在for-in循环中,我们只是仅仅将按钮堆积在了一个地方,所以我们需要去调整这些按钮的位置。


上述想要的布局效果可以调用 layoutSubview方法,这个方法是定义在UIView类中,系统会在合适的时候调用 layoutSubview方法从而让UIView 的子类有机会对于他们的子视图进行布局,所以我们需要的就是重写这个方法来规定如何放置这些按钮。


放置按钮

1、打开 RatingControl.swif ,在 初始化器方法下添加一个方法

override func layoutSubviews() {}
我们可以通过代码提示快速完成上述代码
2、在方法体中添加代码

var buttonFrame = CGRect(x: 0, y: 0, width: 44, height: 44) // Offset each button's origin by the length of the button plus spacing.for (index, button) in ratingButtons.enumerate() {    buttonFrame.origin.x = CGFloat(index * (44 + 5))    button.frame = buttonFrame}

上述代码创建了一个框架(frame),并且通过for-in循环来迭代生成所有按钮的框架。

enumerate() 方法放回在 ratingButtons数组中的元素和其索引的集合。这个元组(tuples)集合中的每个元素包含了一个索引和一个按钮。


最终的layoutSubviews方法如下

override func layoutSubviews() {    var buttonFrame = CGRect(x: 0, y: 0, width: 44, height: 44)        // Offset each button's origin by the length of the button plus spacing.    for (index, button) in ratingButtons.enumerate() {        buttonFrame.origin.x = CGFloat(index * (44 + 5))        button.frame = buttonFrame    }}

里程碑:运行app。现在这些按钮将一个接一个的排列。同时当我们点击任何一个按钮控制台都会输出消息


我们可以通过点击调试区抽屉按钮来折叠控制台


给按钮尺寸声明一个常量值

在之前的代码中我们已经设置了44。通常来说在代码中到处写着硬编码数值是一个非常不好的习惯。如果说我们想将按钮调大一点,那么我们不得不将代码中所有的44都改了。相反如果我们声明一个常量来记录这个吃妇女,那么我们只需要改变一个地方就好了。


从现在开始,我们要确保我们的按钮尺寸随着我们的视图变化。

给按钮组声明一个常量

1、在layoutSubviews方法中,在第一行代码前增加以下代码

// Set the button's width and height to a square the size of the frame's height.let buttonSize = Int(frame.size.height)
上述代码可以让布局更加灵活
2、将剩余的代码进行调整

var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize) // Offset each button's origin by the length of the button plus spacing.for (index, button) in ratingButtons.enumerate() {    buttonFrame.origin.x = CGFloat(index * (buttonSize + 5))    button.frame = buttonFrame}


3、在//MARK:properties下添加两个属性

var spacing = 5
var stars = 5

4、正如我们在第一次添加按钮做的,我们需要更新按钮的intrinsic content size从而使 按钮在 stck view 中可以正确的布局。现在我们需要 intrinsicContentSize 方法来自动计算按钮的尺寸

let buttonSize = Int(frame.size.height)let width = (buttonSize + spacing) * stars return CGSize(width: width, height: buttonSize)

5、修改 初始化器中的for-in循环的第一行

let button = UIButton()

最后 layoutSubview 方法应该看起来像

override func layoutSubviews() {    // Set the button's width and height to a square the size of the frame's height.    let buttonSize = Int(frame.size.height)    var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)        // Offset each button's origin by the length of the button plus some spacing.    for (index, button) in ratingButtons.enumerate() {        buttonFrame.origin.x = CGFloat(index * (buttonSize + 5))        button.frame = buttonFrame    }}

IntrinsicContentSiz方法应该为

override func intrinsicContentSize() -> CGSize {    let buttonSize = Int(frame.size.height)    let width = (buttonSize + spacing) * stars        return CGSize(width: width, height: buttonSize)}

init?(coder:)方法为:

required init?(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)        for _ in 0..<5 {        let button = UIButton()        button.backgroundColor = UIColor.redColor()        button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)        ratingButtons += [button]        addSubview(button)    }}

里程碑:运行app。效果应该跟之前一样。点击每个方块都会在控制台输出消息。


给按钮添加图片

接下来我们需要给按钮添加两种图片



上述图片资源可以通过下载本课程最后的链接,图片资源在Images目录下。同样你也可以使用你自己的图片。

将图片添加到工程中

1、在工程导航中选择 Assets.xcassets 来查看资源目录

2、在左下角点击 + 号,选择创建一个新的文件夹


3、双击文件夹名字,重命名为:Rating Images

4、选中文件夹,点击左下角的+号。选择新建 Image Set

一个图片集显示一个单一的图片,但是可以包含不同版本屏幕解决方案的图片

5、双击图片集,重命名为emptyStar

6、在电脑上选择 空心的星星图片

7、拖拽到2x的框框中


2x是针对iPhone6模拟器的显示解决方案。

8、在左下角点击加号,同样新建一个图片集

9、双击图片集重命名为 filledStar

10、在电脑上选择实心的星星

11、拖拽到 2x 的框框中



现在你的资源目录应该是这样


下一步就是在合适的时候讲按钮设置为合适的图片


本课程的工程文件在下一节给出链接


0 0
原创粉丝点击