这一节,继续为大家提供一个Demo,用来说明Swift中的各种语法及在UIView中的基本使用。效果图如下:
看起来,这个Demo非常的简单。但是为了进行详细的语法说明,它的实现,均是用代码实现的,并且下方的进度条都是"组装"起来的。
结构分析:
1. 有一个根控制器ViewController.swift, 它只是用来展示上面显示的文字(秋恨雪)。
2. 然后下面的文字大小切换及进度条这个整体是一个自定义的View(LFFontView.swift)
3. 进度条部分又有三部分内容组成:(底部的灰色的总进度View,进度条上面的滑块View,实时显示进度的红色的View)。
大致的结构图如下:
1.初始化相关的属性
lazy private var buttons: [UIButton] = [UIButton]() private var selectedButton: UIButton? lazy private var bottomLine = UIImageView(image: UIImage(named: "正文字号-滑条")) lazy private var topLine = UIImageView(image: UIImage(named: "正文字号-滑条红")) lazy private var slider = UIImageView(image: UIImage(named: "正文字号-滑块"))
1) 数组buttons用来存放 "Aa" 这四个按钮。
2) 属性selectedButton 用来存放当前选中了哪一个"Aa"。
3) 三个ImageView就是上面3D图中的前三个内容,即他们组成滑动条。
2. 初始化相关的View
convenience init() { self.init(frame: CGRectZero) } override init(frame: CGRect) { super.init(frame: frame) // init(frame: CGRect)是UIView的默认构造方法 self.frame = CGRectMake(0,kFontViewY,UIScreen.mainScreen().bounds.width,kFontViewH) initDetailViews() }
1) 便利的无参构造方法调用了直接的有参构造方法,并且进行View的frame的设置。
2) initDetailViews方法进行具体的子View的初始化工作。
3. 具体子View的设置及事件处理
func initDetailViews() { // Init Buttons createButton(normal: "正文字号-小(默认)",selected: "正文字号-小",itemStyle: .Small) createButton(normal: "正文字号-中(默认)",selected: "正文字号-中",itemStyle: .Middle) createButton(normal: "正文字号-大(默认)",selected: "正文字号-大",itemStyle: .Big) createButton(normal: "正文字号-大+(默认)",selected: "正文字号-大+",itemStyle: .SuperBig) // Init Others addSubview(bottomLine) addSubview(topLine) addSubview(slider) } func createButton(#normal: String,selected: String,itemStyle: LFFontViewItemStyle) { let fontBtn = UIButton() fontBtn.adjustsImageWhenHighlighted = false fontBtn.tag = itemStyle.rawValue fontBtn.setImage(UIImage(named: normal),forState: UIControlState.Normal) fontBtn.setImage(UIImage(named: selected),forState: UIControlState.Selected) fontBtn.addTarget(self,action: "buttonClick:",forControlEvents: UIControlEvents.TouchDown) fontBtn.imageView?.contentMode = UIViewContentMode.ScaleAspectFit fontBtn.backgroundColor = UIColor.grayColor() buttons.append(fontBtn) addSubview(fontBtn) } func buttonClick(sender: UIButton) { selectedButton?.selected = false sender.selected = true selectedButton = sender setNeedsLayout() } override func layoutSubviews() { super.layoutSubviews() let btnY: CGFloat = 0 let btnW = frame.size.width / 4 let btnH: CGFloat = 68 let count = buttons.count for i in 0..<count { var btn = buttons[i] var btnX = CGFloat(i) * btnW btn.frame = CGRectMake(btnX,btnY,btnW,btnH) } // self.bottomLine let bottomLineX: CGFloat = btnW * 0.5 let bottomLineY: CGFloat = 80 let bottomLineW: CGFloat = frame.size.width - btnW let bottomLineH: CGFloat = 3 bottomLine.frame = CGRectMake(bottomLineX,bottomLineY,bottomLineW,bottomLineH) // self.slider let sliderCenterX = selectedButton == nil ? bottomLineX : selectedButton?.center.x; slider.center.x = sliderCenterX! slider.center.y = bottomLine.center.y // self.topLine topLine.frame = bottomLine.frame; topLine.frame.size.width = sliderCenterX! - bottomLineX }
代码中使用到了常量及枚举,他们的定义如下:
let kFontViewH: CGFloat = 100 let kFontViewY: CGFloat = UIScreen.mainScreen().bounds.height - kFontViewH enum LFFontViewItemStyle : Int { case Small case Middle case Big case SuperBig }
这些代码书写完毕后,效果就像文章开头贴出的效果,只是点击按钮切换的时候,文字大小不会发生改变。在解决这个问题之前,我先对上面所涉及到的知识点进行归纳总结。
1) Swift中没有了宏的概念,所以定义常量,我们可以直接使用let关键字,如上面的kFontViewH 常量。
2) Swift中枚举的定义与objective-c有了很大的不同,如上面的LFFontViewItemStyle 枚举成员直接都用case标识,并且在使用枚举的时候,有两种写法:
LFFontViewItemStyle.Small 或者 .Small。
3) 代码中使用到的成员或者属性等最好使用let修饰符,除非这些成员或者属性需要进行赋值变化等操作。例如:在layoutSubviews方法中,很多的成员都是用了let修饰符,而在属性定义的地方,很多都使用的var修饰符,因为它们要么是需要动态计算或者有赋值等操作,要么涉及到懒加载操作。
4) 在objective-c中,对属性的调用,我们一般使用“self.属性” 的写法。在Swift中可以直接使用属性名称进行操作,也就是“self.”可以省略。例如:addSubview(bottomLine),buttons.append(fontBtn),selectedButton?.selected 等。 但有一些self的地方是不可以省略的,例如:
<span style="font-family:SimSun;font-size:18px;">self.frame = CGRectMake(0,kFontViewH)</span>如果这里直接写frame的话,会和init(frame: CGRect) 中的frame产生歧义。
5) 文章中还出现了很多的 "?" 及 "!" 的使用,如果有什么不清楚的,请参考《Swift:可选类型(Optional)》。
<span style="font-family:SimSun;font-size:18px;">func createButton(#normal: String,itemStyle: LFFontViewItemStyle) {</span>形参normal的前面加上#,如有疑问的,请参考《Swift:函数与方法》。
好的,基本知识已经讲解完毕,下面我们来看看点击按钮后,怎样使得文字的大小随之发生变化。
由于LFFontView.swift是ViewController.swift的子View,所以我们应该可以想到使用代理或者闭包。
代理的实现方式
1)定义代理
protocol LFFontViewDelegate : NSObjectProtocol { func fontView(fontView: LFFontView,didSelectedItem item: LFFontViewItemStyle) }
2)在LFFontView.swift定义代理属性
var delegate: LFFontViewDelegate?3)在buttonClick方法中执行代理方法
delegate?.fontView(self,didSelectedItem: LFFontViewItemStyle(rawValue: sender.tag)!)
4)在ViewController.swift中遵守代理协议及实现代理方法
class ViewController: UIViewController,LFFontViewDelegate { @IBOutlet weak var testLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() let fontView = LFFontView() fontView.delegate = self view.addSubview(fontView) } func fontView(fontView: LFFontView,didSelectedItem item: LFFontViewItemStyle) { switch(item){ case .Small: testLabel.font = UIFont.systemFontOfSize(13) case .Middle: testLabel.font = UIFont.systemFontOfSize(16) case .Big: testLabel.font = UIFont.systemFontOfSize(18) case .SuperBig: testLabel.font = UIFont.systemFontOfSize(25) } } }
这样就可以完成点击按钮,使得文字大小随之变化的效果了。但是程序刚启动的时候,应该选中第一个"Aa" 按钮并显示对应大小的文字,所以我们可以考虑使用“属性监听器”来完成第一次的点击事件。如果对属性监听器不怎么了解的朋友,可以参考《Swift:面向对象(属性)》。所以我们改造代理属性的定义,完成属性监听的工作,改造后的代码如下:
var delegate: LFFontViewDelegate? { didSet { buttonClick(buttons[0]) } }
这里的didiSet在初始化的时候并不会执行,而是等到有具体的控制器成为它的代理的时候才会触发,也就是在ViewController中执行fontView.delegate = self 的时候会触发didSet的执行。
大家注意代理定义的时候,是继承自 NSObjectProtocol的,说明它还是沿用了objective-c时候的语法。其实我们定义的protocol完成可以不继承自任何内容。但有时候会出现问题。因为在Swift中的协议与objective-c的协议有所不同。在objective-c中的协议的实现者必须是类;而Swift中协议的实现者既可以是类,也可以是enum或者struct。所以为了严谨起见,我们定义的协议,可以直接指明实现者是类,所以改写协议的定义如下:
protocol LFFontViewDelegate : class { func fontView(fontView: LFFontView,didSelectedItem item: LFFontViewItemStyle) }
闭包的实现方法 (闭包的知识如果有什么不理解,请参考《Swift:闭包(Closures)》)。
1) 定义闭包
var textClosure: (LFFontViewItemStyle -> ())?2) 在buttonClick中调用闭包
func buttonClick(sender: UIButton) { selectedButton?.selected = false sender.selected = true selectedButton = sender textClosure?(LFFontViewItemStyle(rawValue: sender.tag)!) setNeedsLayout() }3) 在ViewController中实现闭包方法
override func viewDidLoad() { super.viewDidLoad() let fontView = LFFontView() //fontView.delegate = self fontView.textClosure = { [unowned self] (FontStyle: LFFontViewItemStyle) -> () in switch(FontStyle){ case .Small: self.testLabel.font = UIFont.systemFontOfSize(13) case .Middle: self.testLabel.font = UIFont.systemFontOfSize(16) case .Big: self.testLabel.font = UIFont.systemFontOfSize(18) case .SuperBig: self.testLabel.font = UIFont.systemFontOfSize(25) } } view.addSubview(fontView) }
4) 首次加载时候触发
在LFFontView.swift 中的 init(frame: CGRect) 中添加下面的最后一句代码即可。
override init(frame: CGRect) { //super.init() init是NSObject的默认构造方法 super.init(frame: frame) // init(frame: CGRect)是UIView的默认构造方法 self.frame = CGRectMake(0,kFontViewH) initDetailViews() buttonClick(buttons[0]) }