如何在Swift中qt 创建自定义控件件

在Xcode6中使用IBDesignable创建自定义控件(翻译) - 简书
下载简书移动应用
写了36994字,被314人关注,获得了323个喜欢
在Xcode6中使用IBDesignable创建自定义控件(翻译)
在 Xcode 的旧版本中,试图创建一个自定义控件,并不是很容易,因为在IB中,并不能实时预览到你的设计成果,只能在模拟器中测试。对于设计一个单一组件,可能需要花费大量时间。
Xcode6 的发布,苹果为开发者构建自定义控件推出了新功能IBDesignable和IBInspectable,允许在IB中实时预览设计成果。很明显,这会给实际开发提升很高效率。
在本教程中,将介绍IBDesignable IBInspectable,以及展示如何利用这个新功能。除过创建demo示例没有更好地方式来阐述这一新特性,因此,创建一个"Rainbow"自定义界面。
IBDesignable和IBInspectable
使用IBDesignable和IBInspectable,开发者创建界面(或视图)可以实时呈现在IB中。一般来说,为了使用这个新特性,你需要做的是创建一个UIView或者UIControl的子类,然后在定义类的前面加上@IBDesignable关键字。如果是OC,使用IB_DESIGNABLE宏。下面是Swift示例代码:
@IBDesignable
class Rainbow: UIView {
在Xcode旧版本中,你可以在IB中编辑User Defined Runtime Attributes来改变一个对象的属性(例如:layer.cornerRadius),问题是你需要确切知道属性名。IBInspectable只需要一步,对一个可视化类的属性前面加上IBInspectable关键字前缀,该属性会在暴露在IB中,这就是一个更改属性值更简单的方法。
你如果使用Swift开发app,你需要做的只是在你选择的属性前面加上@IBInspectable关键字,下面是个示例代码片段:
@IBInspectable var firstColor: UIColor = UIColor.blackColor() {
// 值改变时更新UI
创建Xcode项目
创建一个新的Xcode项目,选择Single View Application模板,起名为RainbowDemo,在此项目中,将使用Swift语言,因此,创建项目时不要忘记勾选。
完成后,选择Main.storyboard文件,设置View Controller的根视图View的背景颜色Hex Color值为38334C(或者任何你想要的颜色)。然后从对象库中拖一个View放进View Controller,设置它的大小Width为600,Height为434,然后把它放在根视图的中心,设置新视图View和根视图相同背景颜色。
提示:如果想改变RGB颜色值,只需打开调色板和切换到滑块标签来改变RGB值
设置背景颜色
在Xcode6中,为了适配各个iOS设备,你必须为视图View配置自动布局约束。对于简单的约束,你可以在自动布局菜单单击Issues选项,选择Add Missing Contraints,Xcode将自动为View添加布局约束。
创建自定义View类
现在,你已经在storyboard中创建了一个View,是时候创建自定义View类了。使用Cocoa Touch Class文件模板,创建自定义类文件,继承自UIView,起名为"Rainbow"。
创建自定义View类
在自定义类中插入以下代码:
import UIKit
class Rainbow: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
如前所述,这个可视化类是UIView的子类,让自定义类实时呈现,需要override上述两个方法。然后,打开辅助编辑器,更改新拖的View的自定义类为Rainbow类。
实现IBDesignable控制
为了实现实时预览,在自定义类前面加一个前缀@IBDesignable关键字
@IBDesignable
class Rainbow: UIView {
这个关键字确实简单,但是这简单地关键字将使你的开发更加容易。接下来,添加一些设置颜色的属性。在Rainbow类中插入以下代码:
@IBInspectable var firstColor: UIColor = UIColor(red: (37.0/255.0), green: (252.0/255), blue: (244.0/255.0), alpha: 1.0)
@IBInspectable var secondColor: UIColor = UIColor(red: (171.0/255.0), green: (250.0/255), blue: (81.0/255.0), alpha: 1.0)
@IBInspectable var thirdColor: UIColor = UIColor(red: (238.0/255.0), green: (32.0/255), blue: (53.0/255.0), alpha: 1.0)
在这里,我们预先定义每个属性一个默认颜色,每次用户更改它们的值时会重绘视图。更重要的是,我们为每个属性加了一个@IBInspectable关键字前缀,现在去IB的属性检查器,你可以直观地发现这些属性:
IB中的属性
很酷,对吧?IBInspectable通过指示属性,你可以使用颜色选择器可视化地编辑它们。
在Rainbow类中,为了在屏幕上画一个圆,插入以下代码:
func addOval(lineWidth: CGFloat, path: CGPathRef, strokeStart: CGFloat, strokeEnd: CGFloat, strokeColor: UIColor, fillColor: UIColor, shadowRadius: CGFloat, shadowOpacity: Float, shadowOffset: CGSize) {
let arc = CAShapeLayer()
arc.lineWidth = lineWidth
arc.path = path
arc.strokeStart = strokeStart
arc.strokeEnd = strokeEnd
arc.strokeColor = strokeColor.CGColor
arc.fillColor = fillColor.CGColor
arc.shadowRadius = shadowRadius
arc.shadowOpacity = shadowOpacity
arc.shadowOffset = shadowOffset
layer.addSublayer(arc)
为了保证代码的简洁和可读性,我们定义了依据方法调用者传入参数来绘制一个完整的圆或者半圆的公共方法。利用CAShapeLayer类可以很简单的画一个圆或圆弧。你可以使用strokeStart和strokeEnd属性控制渲染的开始和结束。通过改变strokeEnd的值在0.0到1.0之间,你可以绘制一个完整或者部分的圆。其余的属性是只是用于设置渲染颜色,阴影颜色等,在CAShaperLayer官方文档中可以查看更详细的所有可用属性。
接下来,添加以下方法:
override func drawRect(rect: CGRect) {
// 添加圆弧
addCircle(80, capRadius: 20, color: firstColor)
addCircle(150, capRadius: 20, color: secondColor)
addCircle(215, capRadius: 20, color: thirdColor)
func addCircle(arcRadius: CGFloat, capRadius: CGFloat, color: UIColor) {
let x = CGRectGetMidX(bounds)
let y = CGRectGetMidY(bounds)
// 底部圆弧
let pathBottom = UIBezierPath(ovalInRect: CGRectMake((x - (arcRadius/2)),
(y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
addOval(20.0, path: pathBottom, strokeStart: 0, strokeEnd: 0.5,
strokeColor: color, fillColor: UIColor.clearColor(),
shadowRadius: 0, shadowOpacity: 0, shadowOffset: CGSizeZero)
// 中间圆弧
let pathMiddle = UIBezierPath(ovalInRect: CGRectMake((x - (capRadius/2)) - (arcRadius/2),
(y - (capRadius/2)), capRadius, capRadius)).CGPath
addOval(0.0, path: pathMiddle, strokeStart: 0, strokeEnd: 1.0,
strokeColor: color, fillColor: color,
shadowRadius: 5.0, shadowOpacity: 0.5, shadowOffset: CGSizeZero)
// 顶部圆弧
let pathTop = UIBezierPath(ovalInRect: CGRectMake((x - (arcRadius/2)),
(y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
addOval(20.0, path: pathTop, strokeStart: 0.5, strokeEnd: 1.0,
strokeColor: color, fillColor: UIColor.clearColor(),
shadowRadius: 0, shadowOpacity: 0, shadowOffset: CGSizeZero)
drawRect:方法默认什么也不做,为了在自定义View中画圆,我们override此方法来实现自己的绘制代码。addCircle:方法有三个参数:arcRadius,capRadius和color。arcRadius是圆弧的半径,capRadius是圆弧边缘半径。
addCircle:方法利用UIBezierPath画圆弧的简单工作原理:
首先,在底部画了个半圆弧
接下来,在圆弧边缘画了一个完整的小圆
最后,画了另一半圆弧
在drawRect:方法中,我们调用了addCircle:方法三次,传入的参数指定圆弧该怎样画:
画圆弧原理
利用IBInspectable属性,你可以在IB中自由改变每个圆弧的颜色,而不需要写代码:
显然,你可以进一步利用@IBInspectable暴露arcRadius属性,便可以在IB中修改绘制圆弧半径。
通过本教程后,你现在了解了在Xcode6中如何利用IBDesignable和IBInspectable实时预览界面。利用这个新特性,你可以更高效创建自定义组件。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
分享 iOS 开发的知识,解决大家遇到的问题,讨论iOS开发的前沿,欢迎大家投稿~
· 25644人关注
沟通想法,分享知识,欢迎大家分享一些好的文章。
· 8124人关注
· 7335人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:马上着手开发 iOS 应用程序 (六) - 实现自定义控件 - 简书
下载简书移动应用
写了61930字,被595人关注,获得了238个喜欢
马上着手开发 iOS 应用程序 (六) - 实现自定义控件
重要:这是针对于正在开发中的API或技术的预备文档(预发布版本)。苹果提供这份文档的目的是帮助你按照文中描述的方式对技术的选择及界面的设计开发进行规划。这些信息有可能发生变化,因此根据本文档的软件开发应当基于最终版本的操作系统和文档进行测试。该文档的新版本或许会随着API或相关技术未来的发展而进行更新。
翻译自苹果官网:
在本课中,为 app 实现评分功能。完成的样子如下:
学习目标:
课程的最后,你讲能够:
创建自定义源代码文件并与 storyboard 中控件关联
定义自定义类
实现自定义类的构造器
使用 UIView 作为容器
理解如何在程序中显示视图
创建自定义类
我们需要一个控件来让用户为美食评分。虽然有多种实现方式,但是本节重点是使用自定义类的方式。
这是你即将要实现的评分控件:
评分控件让用户为美食选择 0、1、2、3、4或5分。当用户点击星星,在所点星星左边的所有星星都将被填充颜色。计算填充的星星数目作为评分,而空的星星不算。
通过创建一个 UIView 的子类来构建控件的界面、交互和行为。
创建 UIView 的子类
选择 File & New & File(或按 Command-N)。
选择 Cocoa Touch Class 然后点击 Next。
在 Class 区域,输入 RatingControl。
在 SubClass of 区域,选择 UIView。
确保语言设置成了 Swift。
点击 Next。 默认会存到你的项目目录中。 Group 选项默认是你的 app 名字,FoodTracker。 在 Targets 区域,app 选项是选中的而 tests 未选中。
其他都按照默认的来,点击 Create。 Xcode 创建了 RatingControl.swift 文件。RatingControl 是 UIView 的子类。
删除文件中模板自动添加的注释以便轻装上阵。
import UIKit
class RatingControl: UIView {
有两种典型的方式创建视图:第一种通过 frame 初始化视图这样可以手动添加视图到你的界面中,或者通过 storyboard 加载视图。它们都有对应的构造方法:frame 的是 init(frame:) 而 storyboard 对应 int?(coder:)方法。
由于使用 storyboard 来加载 view,所以一开始请覆写父类的 init?(coder:) 构造方法。
覆写构造方法
在 RatingControl.swift 的 class 行下面,添加如下注释。
// MARK: Initialization
在注释下面,输入 init。 弹出代码提示框。
选择列表的第三个 init?(coder:) 构造方法,回车确认。
init?(coder aDecoder: NSCoder) {
Xcode 为你生成基本的构造方法。
点击红色圆圈添加 required 关键字来修复错误。
required init?(coder aDecoder: NSCoder) {
每个 UIView 子类实现自定义构造方法同时必须实现 init?(coder:)。 Swift 编译器知道这个规则,所以提供了 fix-it 功能为错误提供潜在的解决方案。
在子类的构造器中添加如下行。
super.init(coder: aDecoder)
init?(coder:) 构造方法应该像这样:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
显示自定义视图
为了显示自定义视图,需要向界面添加视图并确保视图关联了 RatingControl 类。
打开 storyboard。
在 storyboard 中,使用对象库找到 View 对象并拖到堆栈视图中 image view 的下面。
选中视图,在实用工具区打开尺寸检查器。
在 Intrinsic Size 区域的弹出菜单中,选择 Placeholder。
在 Instrinsic Size 下面的 Height 区域输入 44 ,Width 区域输入 240 。回车确认,界面最后应该像这样:
选中视图,打开识别检查器(Identity inspector)。
在识别检查器中,找到名为 Class 的区域选择 RatingControl。
添加按钮到视图
下一步是添加按钮到视图来允许用户选择评分。
在 init?(coder:) 构造方法中,添加如下代码行来创建红色按钮:
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
button.backgroundColor = UIColor.redColor()
使用 redColor() 更容易让你看到视图的样子。如果你喜欢可以换成其他 UIColor 的值,如 blueColor() 和 greenColor()。
在方法最后添加如下代码:
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)
为了告诉堆栈视图如何布局控件,需要提供固有内容尺寸。如下面这样覆写 intrinsicContentSize 方法。
override func intrinsicContentSize() -& CGSize {
return CGSize(width: 240, height: 44)
检验:运行 app。应该能够看到视图中有个小红色正方形。它是刚才在构造方法中添加的按钮。
给按钮添加动作
在 RatingController.swift 最后 } 的前面添加这行注释:
// MARK: Button Action
在注释下面添加如下代码:
func ratingButtonTapped(button: UIButton) {
print("Button pressed ?")
使用 print() 方法检查 ratingButtonTapped(_:) 动作连接了预期的按钮。这个函数打印消息到 Xcode 调试控制台中,而控制台是在编辑区底部非常有用的调试工具。 稍后会用真实实现来替代这行调试代码。
找到 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)
在 addSubView(button) 行下面,添加如下代码:
button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)
你应该很熟悉 target-action 设计模式因为之前已经使用它连接过控件和方法。现在让我们再次创建连接。连接 ratingButtonTapped: 动作和按钮对象,当用户按下了按钮,会触发这个方法。 注意因为使用了界面构造器(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。
使用整形变量来表示评分值,它的范围是0到5,使用数组来存放所有的按钮。
添加评分属性
在 RatingControl.swift 中,找到类定义行:
class RatingControl: UIView {
在行的下面,添加如下代码:
// MARK: Properties
var rating = 0
var ratingButtons = [UIButton]()
现在只有一个按钮,但我们总共需要5个。使用 for-in 循环遍历 sequence 来创建五个按钮。
创建五个按钮
在 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)
在最后四行添加 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 确保 for-in 中的行缩进正确。
半闭区间运算符(..&) 不包括最大的数,所以范围是从 0 到 4 五次循环来添加五个按钮。当不需要知道当前循环变量使用通配符 _。
在 addSubView(button) 行上面,添加如下代码:
ratingButtons += [button]
添加每个按钮到 ratingButtons 数组。
你的 init?(coder:) 构造方法最后应该像这样:
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 循环仅仅把各个按钮堆叠到一起。需要调整这些按钮的布局。
调整按钮的布局
在 RatingControl.swift 的 // MARK: Initialization 区域添加如下方法:
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
使用 for-in 循环遍历按钮来设置它们的 frames。
enumerate() 方法返回 ratingButtons 中所有控件的集合。集合中每个元组包含一个 index 和 button,分别代表遍历的下标和按钮。使用 index 计算新的 frame 并设置给对应的按钮。frame 的 x 值等于 44 点的标准按钮大小加上 5 点的空隙然后乘以 index 。
最后的 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。此刻按钮应该并排在一起了。注意,此时点击任一按钮应该继续调用 ratingButtonTapped(_:) 并打印消息到控制台。
使用 Debug toggle 收缩控制台。
为按钮大小添加常量
注意在代码中使用了 44 的值。而在代码的各处使用硬编码值是很不好的做法。如果需要一个大一点的按钮,就得在各处修改这个 44 的值。相反,定义常量来表示按钮的大小,这样更方便修改因为值只需修改一处。
现在,根据容器视图的高度调整按钮来适配不同尺寸的容器视图.
定义一个常量作为按钮的大小
在 layoutSubviews() 方法第一行前面添加如下代码:
// Set the button's width and height to a square the size of the frame's height.
let buttonSize = Int(frame.size.height)
这让布局更加灵活。
修改方法剩下部分使用 buttonSize 常量取代 44:
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
和第一次添加按钮一样,你需要更新控件的固有内容尺寸来让堆栈视图正确的布局。使用 intrinsicContentSize 方法来计算控件的大小并返回,代码如下:
修改 init?(coder:) 方法中 for-in 循环的第一行为如下代码 :
let button = UIButton()
因为你在 layoutSubviews() 中设置了按钮的 frame,你不再需要在创建按钮时候设置了。
最后的 layoutSubviews() 方法应该是这样:
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
intrinsicContentSize 方法应该像这样:
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。一切和之前一样。按钮应该并排显示。此刻点击任何一个按钮都会调用 ratingButtonTapped(_:) 并打印信息到控制台。
向按钮中添加星星的图片
下一步,添加空的和填充的星星图片到按钮中。
可以在课程最终项目的 Images/ 文件夹中找到这两张图片,或者用你自己的。
添加图片到项目中
选择项目导航的 Assets.xcassets 查看 asset catalog。 回忆一下 asset catalog 是存放和管理 app 图片资源的地方。
点击左下角的 + 按钮,从弹出的菜单中选择 New Folder。
双击文件夹名字并重命名为 Rating Images。
选中文件夹,点击左下角的 + 按钮,在弹出菜单中选择 New Image Set。 一个图片集合虽然只能代表单张图片,但是包含在不同屏幕分辨率上显示的不同版本图片。
双击图片集合的名字并重命名为 emptyStar。
选择电脑中空的星星图片。
拖动图片到图片集合的 2x 槽中。
2x 是 iPhone 6 模拟器显示的分辨率,图片在这个分辨率显示最好。
点击左下角的 + 按钮,在弹出的菜单中选择 New Image Set。
双击图片集合名字并重命名为 filledStar。
在电脑上,选择想要添加的填充状态的星星图片。
拖动图片到图片集合的 2x 槽中。
asset catalog 应该像这样:
下一步,在适当时候写代码为按钮设置正确的图片。
为按钮设置星星的图片
打开 RatingControl.swift。
在 init?(coder:) 构造方法的 for-in 循环前面添加这两行:
let filledStarImage = UIImage(named: "filledStar")
let emptyStarImage = UIImage(named: "emptyStar")
在 for-in 循环按钮初始化那一行的后面,添加如下代码:
button.setImage(emptyStarImage, forState: .Normal)
button.setImage(filledStarImage, forState: .Selected)
button.setImage(filledStarImage, forState: [.Highlighted, .Selected])
为按钮的不同状态设置两张不同的图片这样当按钮选中的时候就能看到状态变化了。按钮未选中(.Normal 状态)显示空的星星图片。按钮被选中(.Selected 状态)显示填充的星星图片。在用户点击按钮不松手时候按钮同时处于选中和高亮状态。
删除设置背景颜色为红色的那行代码:
button.backgroundColor = UIColor.redColor()
因为按钮已经有图片,所以把背景颜色去掉。
添加如下代码:
button.adjustsImageWhenHighlighted = false
确保图片在状态切换时不显示额外的高亮状态。
init?(coder:) 构造方法应该像这样:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let emptyStarImage = UIImage(named: "emptyStar")
let filledStarImage = UIImage(named: "filledStar")
for _ in 0..&5 {
let button = UIButton()
button.setImage(emptyStarImage, forState: .Normal)
button.setImage(filledStarImage, forState: .Selected)
button.setImage(filledStarImage, forState: [.Highlighted, .Selected])
button.adjustsImageWhenHighlighted = false
button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)
ratingButtons += [button]
addSubview(button)
检验:运行 app。应该看到星星已经取代红色按钮了。此刻点击任意一个按钮仍然会调用 ratingButtonTapped(_:) 并打印信息到控制台上,但是按钮图片并没有改变。下一步会修复这个问题的。
实现按钮动作
用户需要点击星星的时候选择评分,所以使用真实的 ratingButtonTapped(_:) 方法替代调试实现。
实现评分动作
找到 RaingControl.swift 文件的 ratingButtonTapped(_:) 方法:
使用下面这行替代 print 语句:
rating = ratingButtons.indexOf(button)! + 1
indexOf(_:) 方法尝试找出数组中选中的按钮并返回它的下标。 方法返回可选的 Int 因为搜索的对象或许在集合中不存在。然而,因为触发动作的按钮是你自己添加和创建到数组中的,所以确定会返回正确的下标。这时,使用 ! 来访问内在的下标值。因为数组下标是从0开始所以要加1才是合适的评分.
在 RatingControl.swift 的最后一个 } 前面,添加如下代码:
func updateButtonSelectionStates() {
使用帮助方法来更新按钮的选中状态。
在 updateButtonSelectionStates() 方法中,添加 for-in 循环:
for (index, button) in ratingButtons.enumerate() {
// If the index of a button is less than the rating, that button should be selected.
button.selected = index & rating
代码遍历按钮数组根据按钮的下标是否小于评分来设置每个按钮的状态。如果 index & rating 设置按钮的状态为选中并让它显示填充的星星图片。否则,按钮不选中显示空的星星图片。
在 ratingButtonTapped(_:) 方法中,添加 updateButtonSelectionStates() 的调用作为最后一行的实现:
func ratingButtonTapped(button: UIButton) {
rating = ratingButtons.indexOf(button)! + 1
updateButtonSelectionStates()
在 layoutSubviews() 方法中,添加 updateButtonSelectionStates() 的调用作为最后一行的实现:
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
updateButtonSelectionStates()
不仅仅当评分改动的时候,视图加载的时候也要更新按钮的选中状态,这是非常重要的。
在 // MARK: Properties 区域,找到 rating 属性:
var rating = 0
更新 rating 属性让它包含属性观察器:
var rating = 0 {
setNeedsLayout()
属性观察器观察和响应属性值的修改。在设置属性值的前后它会被立即调用。具体来讲,当设完值后 didSet 属性观察器立刻执行。在这里调用 setNeedsLayout() 让每次评分修改后触发布局更新。确保 UI 显示正确的 rating 属性值。
updateButtonSelectionStates() 方法应该像这样:
func updateButtonSelectionStates() {
for (index, button) in ratingButtons.enumerate() {
// If the index of a button is less than the rating, that button shouldn't be selected.
button.selected = index & rating
检验:运行 app。应该能看到五颗星,请尝试点击一个来修改评分。点击第三颗星来修改评分为3,例如。
为间距和星星数量添加属性
确保在代码中没有任何硬编码的值,为评分的数量和评分之间的间距添加属性。这样,你只需修改代码的一处。
用属性代替硬编码的值
找到 RatingControl.swift 的 // MARK: Properties 区域。
// MARK: Properties
var rating = 0 {
setNeedsLayout()
var ratingButtons = [UIButton]()
点击编辑区顶部文件名字使用 functions menu 快速跳过去。
在已存在的属性下面,添加如下代码:
var spacing = 5
使用这个属性给你的按钮增加一些额外间距。
在 layoutSubviews 中,使用 spacing 属性替代那个作为间距的常量。
buttonFrame.origin.x = CGFloat(index * (buttonSize + spacing))
在 spacing 属性下面,添加另外一个属性:
var stars = 5
可以使用这个属性来控制控件的星星数量。
在 init?(coder:) 中,使用 stars 属性替代之前你为星星数量设置的常量。
for _ in 0..&stars {
检验:运行 app。一起都应该和之前一样。
连接评分控件和视图控制器
最后一件要做的事情就是给 ViewController 类添加一个评分控件的引用。
连接评分控件 outlet 到 ViewController.swift 中
打开 storyboard。
点击 Xcode 工具栏中的 Assistant 按钮打开辅助编辑器。
如果想要更多的空间来工作,点击 Xcode 工具栏中的 Navigator 和 Utilties 按钮来收缩项目导航和实用工具区。
同样可以收缩大纲视图。
选择评分控件。
ViewConroller.swift 会显示在右边的编辑器。(如果没有,在右边的编辑器选择栏中选择 Automatic & ViewController.swift)
按住 Control 从画板中的评分控件拖动到右边的编辑器中,在 photoImageView 属性的下面停止拖动。
在出现的对话框中,输入名字 ratingControl。 忽略剩下的选项,对话框最后应该像这样:
点击 Connect。
此刻 ViewController 类应该有评分控件的引用了。
现在已经很接近最后的食物场景界面了,但是还是需要做一些清理工作。app 正在逐渐实现更多牛逼的功能且拥有与前面课程完全不同的界面。需要删掉一些不需要的代码,同时把控件集中到堆栈视图中来平衡界面。
点击 Standard 按钮返回标准编辑器。
点击 Xcode 工具栏的 Navigator 和 Utiliites 按钮展开项目导航和实用工具区。
打开 storyboard。
选择 Set Default Label Text 按钮,点击 Delete 键删除它。 堆栈视图重新排放 UI 控件来填充按钮留下的空间。
如果需要,打开大纲视图。选择 Stack View 对象。
打开属性检查器。
在属性检查器中,找到 Alignment 区域并选择 Center。 堆栈视图中所有控件应该水平居中了:
现在,清理刚才删除按钮的对应动作方法。
打开 ViewController.swift。
删除 ViewController.swift 中的 setDefaultLabelText(_:) 动作方法。
@IBAction func setDefaultLabelText(sender: UIButton) {
mealNameLabel.text = "Default Text"
检验:运行 app。一切都和之前一样,但是 Set Default Label Text 按钮消失了,控件也都水平居中了。按钮并排贴在一起。此刻点击任何一个按钮仍然会调用 ratingButtonTapped(_:) 并相应修改按钮图片。
如果运行出现问题,尝试按 Command-Shift-K 来 clear 项目。
为了查看本课的完整实例项目, 并在 Xcode 中查看它。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
专题是为了集锦代码段的开发,降低开发时间,提升开发效率。欢迎投稿。
· 223人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:}

我要回帖

更多关于 创建自定义控件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信