转载请注明出处:http://www.jb51.cc/article/p-htigfqxi-ber.html。
我们先做一些基础性的工作,比如创建工程。
工程搭建
先创建一个Single View Application
工程:
语言选择Swift
:
为了最大程度的利用屏幕区域,我们完全隐藏掉状态栏,在Info.plist
里修改或添加这两个参数:
然后进入到Main.storyboard
,开始搭建我们的UI。
我们给已存在的ViewController
的View
添加一个UIImageView
的子视图,背景色设为Light Gray
,然后添加4个约束,由于要做一个全屏的画板,必须要让Constraint to margins
保持没有选中的状态,否则左右两边会留下苹果建议的空白区域:
然后我们回到View
上:
- 添加一个放工具栏的容器:
UIView
,为该View设置约束:
同样的不要选择Contraint to margins
。 -
在该View里添加一个
UISegmentedControl
,并给SegmentedControl设置6个选项,分别是:- 铅笔
- 直尺
- 虚线
- 矩形
- 圆形
- 橡皮擦
- 给这个SegmentedControl添加约束:
垂直居中,两边各留20,高度固定为28。
完整的UI及结构看起来像这样:
ImageView将会作为实际的绘制区域,顶部的SegmentedControl提供工具的选择。 到目前为止我们还没有写下一行代码,至此要开始编码了。
你可能会注意到Board有一部分被挡住了,这只是暂时的~
施工…
Board
我们创建一个Board
类,继承自UIImageView
,同时把这个类设置为Main.storyboard
中ImageView
的Class,这样当app启动的时候就会自动创建一个Board的实例了。
增加两个属性以及初始化方法:
由于我们是依赖于touches方法来完成绘图过程,我们需要记录下每次touch的状态,比如began
、moved
、ended
等,为此我们创建一个枚举,在touches方法中进行记录,并调用私有的绘图方法drawingImage
:
在我们实现drawingImage方法之前,我们先创建另外一个重要的组件:BaseBrush
。
BaseBrush
顾名思义,BaseBrush
将会作为一个绘图的基类而存在,我们会在它的基础上创建一系列的子类,以达到弹性的设计目的。为此,我们创建一个BaseBrush
类,并实现一个PaintBrush
接口:
BaseBrush
实现了PaintBrush
接口,PaintBrush
声明了两个方法:
- supportedContinuousDrawing,表示是否是连续不断的绘图
- drawInContext,基于Context的绘图方法,子类必须实现具体的绘图
只要是实现了PaintBrush
接口的类,我们就当作是一个绘图工具(如铅笔、直尺等),而BaseBrush
除了实现PaintBrush
接口以外,我们还为它增加了四个便利属性:
这么一来,子类也可以很方便的获取到当前的状态,并作一些深度定制的绘图方法。
回到Board
我们实现了一个画笔的基类之后,就可以重新回到Board
类了,毕竟我们之前的工作还没有做完,现在是时候完善Board
类了。
我们用Board
实际操纵BaseBrush
,先为Board
添加两个新的属性:
Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',136); Box-sizing: border-Box;">var</span> brush: BaseBrush? <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">var</span> realImage: UIImage? </code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li></ul>
brush对应到具体的画笔类,realImage
保存当前的图形,重新修改touches方法,以便增加对brush
属性的处理,完整的touches方法实现如下:
我们需要防止brush
为nil
的情况,以及为brush
设置好beginPoint
和endPoint
,之后我们就可以完善drawingImage
方法了,实现如下:
步骤解析:
- 开启一个新的ImageContext,为保存每次的绘图状态作准备。
- 初始化context,进行基本设置(画笔宽度、画笔颜色、画笔的圆润度等)。
- 把之前保存的图片绘制进context中。
- 设置
brush
的基本属性,以便子类更方便的绘图;调用具体的绘图方法,并最终添加到context中。 - 从当前的context中,得到Image,如果是
ended
状态或者需要支持连续不断的绘图,则将Image保存到realImage
中。 - 实时显示当前的绘制状态,并记录绘制的最后一个点。
这些工作完成以后,我们就可以开始写第一个工具了:铅笔工具。
铅笔工具
铅笔工具应该支持连续不断的绘图(不断的保存到realImage中),这也是我们给PaintBrush
接口增加supportedContinuousDrawing
方法的原因,考虑到用户的手指可能快速的移动,导致从一个点到另一个点有着跳跃性的动作,我们对铅笔工具采用画直线的方式来实现。
首先创建一个类,名为PencilBrush
,继承自BaseBrush
类,实现如下:
如果lastPoint为nil,则基于beginPoint画线,反之则基于lastPoint画线。
这样一来,一个铅笔工具就完成了,怎么样,很简单吧。
测试
到目前为止,我们的
ViewController
还保持着默认的状态,是时候先为铅笔工具写一些测试代码了。
在ViewController
添加board
属性,并与Main.storyboard
中的Board关联起来;创建一个brushes
属性,并为之赋值为:在
ViewController
中添加switchBrush:
方法,并把Main.storyboard
中的SegmentedControl的ValueChanged
连接到switchBrush:
方法上,实现如下:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">@IBAction func switchBrush(sender: UISegmentedControl) { assert(sender<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.tag</span> < self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.brushes</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.count</span>,0); Box-sizing: border-Box;">"!!!"</span>) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.board</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.brush</span> = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.brushes</span>[sender<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.selectedSegmentIndex</span>] }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li></ul>最后在
viewDidLoad
方法中做一个初始化:
self.board.brush = brushes[0]
编译、运行,铅笔工具可以完美运行~!
其他的工具
接下来我们把其他的绘图工具也实现了。
其他的工具不像铅笔工具,不需要支持连续不断的绘图,所以也就不用覆盖supportedContinuousDrawing
方法了。直尺
虚线
DashLineBrush类,实现如下:
Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',102);">DashLineBrush</span>: <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGContextRef</span>)</span> { let lengths: [<span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGFloat</span>] = [self.strokeWidth * 3,self.strokeWidth * 3] <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGContextSetLineDash</span><span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">lengths</span>,2)</span>; <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li></ul>这里我们就用到了
BaseBrush
的strokeWidth
属性,因为我们想要创建一条动态的虚线。矩形
RectangleBrush类,实现如下:
Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',102);">RectangleBrush</span>: <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGContextAddRect</span><span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,<span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGRect</span>(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">origin</span>: <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGPoint</span>(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">x</span>: <span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">min</span>(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">x</span>)</span>,y: min<span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">y</span>,102);">y</span>)</span>),size: <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">CGSize</span><span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">width</span>: <span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">abs</span>(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">x</span> - <span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,height: abs<span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">y</span> - <span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">y</span>)</span>))) } }</span></code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li></ul>我们用到了一些计算,因为我们希望矩形的区域不是由beginPoint定死的。
圆形
EllipseBrush类,实现如下:
同样有一些计算,理由同上。橡皮擦
从本文一开始就说过了,我们要做一个真正的橡皮擦,网上有很多的橡皮擦的实现其实就是把画笔颜色设置为背景色,但是如果背景色可以动态设置,甚至设置为一个渐变的图片时,这种方法就失效了,所以有些绘图app的背景色就是固定为白色的。
其实Apple的Quartz2D框架本身就是支持橡皮擦的,只用一个方法就可以完美实现。
让我们创建一个EraserBrush
类,实现如下:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',102);">EraserBrush</span>: <span class="hljs-type" style="Box-sizing: border-Box; color: rgb(102,102);">PencilBrush</span> { override func drawInContext<span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">CGContextSetBlendMode</span><span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">kCGBlendModeClear</span>)</span>; super.drawInContext<span class="hljs-container" style="Box-sizing: border-Box;">(<span class="hljs-title" style="Box-sizing: border-Box; color: rgb(102,102);">context</span>)</span> } }</span></code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li></ul>注意,与其他的工具不同,橡皮擦是继承自
PencilBrush
的,因为橡皮擦本身也是基于点的,而drawInContext
里也只是加了一句:
- 提供对画笔颜色、粗细的设置
- 背景设置
- 全屏绘图(不能让Board一直显示不全)
先从画笔开始,Let’s go!
画笔设置
不管是画笔还是背景设置,我们都要有一个能提供设置的工具栏。
设置工具栏
所以我们往Board
上再盖一个UIToolbar
,与顶部的View类似:
- 拖一个
UIToolbar
到Board
的父类上,与Board
的视图层级平级。 - 设置
UIToolbar
的约束:左、右、下间距为0,高为44:
- 往
UIToolbar
上拖一个UIBarButtonItem
,title
就写:画笔设置。 - 在
ViewController
里增加一个paintingBrushSettings
方法,并把UIBarButtonItem
的action
连接paintingBrushSettings
方法上。 - 在
toolar
属性,并把Xib中的UIToolbar
连接到toolbar
上。
UIToolbar配置好后,UI及视图层级如下:
RGBColorPicker
考虑到多个页面需要选取自定义的颜色,我们先创建一个工具类:RGBColorPicker
,用于选择RGB颜色:
Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">class RGBColorPicker: UIView { var colorChangedBlock: ((color: UIColor) -> Void)? private var sliders = [UiSlider]() private var labels = [UILabel]() override init(frame: CGRect) { super<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.init</span>(frame: frame) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.initial</span>() } required init(coder aDecoder: NSCoder) { super<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.init</span>(coder: aDecoder) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.initial</span>() } private func initial() { self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.backgroundColor</span> = UIColor<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.clearColor</span>() let trackColors = [UIColor<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.redColor</span>(),UIColor<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.greenColor</span>(),68); Box-sizing: border-Box;">.blueColor</span>()] for index <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">in</span> <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">1.</span>.<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">.3</span> { let slider = UiSlider() slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.minimumValue</span> = <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">0</span> slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.value</span> = <span class="hljs-number" style="color: rgb(0,68); Box-sizing: border-Box;">.maximumValue</span> = <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">255</span> slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.minimumTrackTintColor</span> = trackColors[index - <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">1</span>] slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.addTarget</span>(self,action: <span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"colorChanged:"</span>,forControlEvents: <span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.ValueChanged</span>) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.addSubview</span>(slider) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.sliders</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.append</span>(slider) let label = UILabel() label<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.text</span> = <span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"0"</span> self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.addSubview</span>(label) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.labels</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.append</span>(label) } } override func layoutSubviews() { super<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.layoutSubviews</span>() let sliderHeight = CGFloat(<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">31</span>) let labelWidth = CGFloat(<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">29</span>) let yHeight = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.bounds</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.size</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.height</span> / CGFloat(sliders<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.count</span>) for index <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">0.</span>.<self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.count</span> { let slider = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.sliders</span>[index] slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.frame</span> = CGRect(<span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">x</span>: <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">0</span>,102); Box-sizing: border-Box;">y</span>: CGFloat(index) * yHeight,width: self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.width</span> - labelWidth - <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">5.0</span>,height: sliderHeight) let label = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.labels</span>[index] label<span class="hljs-preprocessor" style="color: rgb(68,102); Box-sizing: border-Box;">x</span>: CGRectGetMaxX(slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.frame</span>) + <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">5</span>,102); Box-sizing: border-Box;">y</span>: slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.frame</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.origin</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.y</span>,width: labelWidth,height: sliderHeight) } } override func intrinsicContentSize() -> CGSize { return CGSize(width: UIViewNoIntrinsicmetric,height: <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">107</span>) } @IBAction private func colorChanged(slider: UiSlider) { let color = UIColor( red: CGFloat(self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.sliders</span>[<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">0</span>]<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.value</span> / <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">255.0</span>),green: CGFloat(self<span class="hljs-preprocessor" style="color: rgb(68,102); Box-sizing: border-Box;">1</span>]<span class="hljs-preprocessor" style="color: rgb(68,blue: CGFloat(self<span class="hljs-preprocessor" style="color: rgb(68,102); Box-sizing: border-Box;">2</span>]<span class="hljs-preprocessor" style="color: rgb(68,alpha: <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">1</span>) let label = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.labels</span>[find(self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.sliders</span>,slider)!] label<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.text</span> = Nsstring(format: <span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"%.0f"</span>,slider<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.value</span>) if let colorChangedBlock = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.colorChangedBlock</span> { colorChangedBlock(color: color) } } func setCurrentColor(color: UIColor) { var red: CGFloat = <span class="hljs-number" style="color: rgb(0,green: CGFloat = <span class="hljs-number" style="color: rgb(0,blue: CGFloat = <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">0</span> color<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.getRed</span>(&red,green: &green,blue: &blue,alpha: nil) let colors = [red,green,blue] for index <span class="hljs-keyword" style="color: rgb(0,68); Box-sizing: border-Box;">.value</span> = Float(colors[index]) * <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">255</span> let label = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.value</span>) } } }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li><li style="Box-sizing: border-Box; padding: 0px 5px;">12</li><li style="Box-sizing: border-Box; padding: 0px 5px;">13</li><li style="Box-sizing: border-Box; padding: 0px 5px;">14</li><li style="Box-sizing: border-Box; padding: 0px 5px;">15</li><li style="Box-sizing: border-Box; padding: 0px 5px;">16</li><li style="Box-sizing: border-Box; padding: 0px 5px;">17</li><li style="Box-sizing: border-Box; padding: 0px 5px;">18</li><li style="Box-sizing: border-Box; padding: 0px 5px;">19</li><li style="Box-sizing: border-Box; padding: 0px 5px;">20</li><li style="Box-sizing: border-Box; padding: 0px 5px;">21</li><li style="Box-sizing: border-Box; padding: 0px 5px;">22</li><li style="Box-sizing: border-Box; padding: 0px 5px;">23</li><li style="Box-sizing: border-Box; padding: 0px 5px;">24</li><li style="Box-sizing: border-Box; padding: 0px 5px;">25</li><li style="Box-sizing: border-Box; padding: 0px 5px;">26</li><li style="Box-sizing: border-Box; padding: 0px 5px;">27</li><li style="Box-sizing: border-Box; padding: 0px 5px;">28</li><li style="Box-sizing: border-Box; padding: 0px 5px;">29</li><li style="Box-sizing: border-Box; padding: 0px 5px;">30</li><li style="Box-sizing: border-Box; padding: 0px 5px;">31</li><li style="Box-sizing: border-Box; padding: 0px 5px;">32</li><li style="Box-sizing: border-Box; padding: 0px 5px;">33</li><li style="Box-sizing: border-Box; padding: 0px 5px;">34</li><li style="Box-sizing: border-Box; padding: 0px 5px;">35</li><li style="Box-sizing: border-Box; padding: 0px 5px;">36</li><li style="Box-sizing: border-Box; padding: 0px 5px;">37</li><li style="Box-sizing: border-Box; padding: 0px 5px;">38</li><li style="Box-sizing: border-Box; padding: 0px 5px;">39</li><li style="Box-sizing: border-Box; padding: 0px 5px;">40</li><li style="Box-sizing: border-Box; padding: 0px 5px;">41</li><li style="Box-sizing: border-Box; padding: 0px 5px;">42</li><li style="Box-sizing: border-Box; padding: 0px 5px;">43</li><li style="Box-sizing: border-Box; padding: 0px 5px;">44</li><li style="Box-sizing: border-Box; padding: 0px 5px;">45</li><li style="Box-sizing: border-Box; padding: 0px 5px;">46</li><li style="Box-sizing: border-Box; padding: 0px 5px;">47</li><li style="Box-sizing: border-Box; padding: 0px 5px;">48</li><li style="Box-sizing: border-Box; padding: 0px 5px;">49</li><li style="Box-sizing: border-Box; padding: 0px 5px;">50</li><li style="Box-sizing: border-Box; padding: 0px 5px;">51</li><li style="Box-sizing: border-Box; padding: 0px 5px;">52</li><li style="Box-sizing: border-Box; padding: 0px 5px;">53</li><li style="Box-sizing: border-Box; padding: 0px 5px;">54</li><li style="Box-sizing: border-Box; padding: 0px 5px;">55</li><li style="Box-sizing: border-Box; padding: 0px 5px;">56</li><li style="Box-sizing: border-Box; padding: 0px 5px;">57</li><li style="Box-sizing: border-Box; padding: 0px 5px;">58</li><li style="Box-sizing: border-Box; padding: 0px 5px;">59</li><li style="Box-sizing: border-Box; padding: 0px 5px;">60</li><li style="Box-sizing: border-Box; padding: 0px 5px;">61</li><li style="Box-sizing: border-Box; padding: 0px 5px;">62</li><li style="Box-sizing: border-Box; padding: 0px 5px;">63</li><li style="Box-sizing: border-Box; padding: 0px 5px;">64</li><li style="Box-sizing: border-Box; padding: 0px 5px;">65</li><li style="Box-sizing: border-Box; padding: 0px 5px;">66</li><li style="Box-sizing: border-Box; padding: 0px 5px;">67</li><li style="Box-sizing: border-Box; padding: 0px 5px;">68</li><li style="Box-sizing: border-Box; padding: 0px 5px;">69</li><li style="Box-sizing: border-Box; padding: 0px 5px;">70</li><li style="Box-sizing: border-Box; padding: 0px 5px;">71</li><li style="Box-sizing: border-Box; padding: 0px 5px;">72</li><li style="Box-sizing: border-Box; padding: 0px 5px;">73</li><li style="Box-sizing: border-Box; padding: 0px 5px;">74</li><li style="Box-sizing: border-Box; padding: 0px 5px;">75</li><li style="Box-sizing: border-Box; padding: 0px 5px;">76</li><li style="Box-sizing: border-Box; padding: 0px 5px;">77</li><li style="Box-sizing: border-Box; padding: 0px 5px;">78</li><li style="Box-sizing: border-Box; padding: 0px 5px;">79</li><li style="Box-sizing: border-Box; padding: 0px 5px;">80</li><li style="Box-sizing: border-Box; padding: 0px 5px;">81</li><li style="Box-sizing: border-Box; padding: 0px 5px;">82</li><li style="Box-sizing: border-Box; padding: 0px 5px;">83</li><li style="Box-sizing: border-Box; padding: 0px 5px;">84</li><li style="Box-sizing: border-Box; padding: 0px 5px;">85</li><li style="Box-sizing: border-Box; padding: 0px 5px;">86</li><li style="Box-sizing: border-Box; padding: 0px 5px;">87</li><li style="Box-sizing: border-Box; padding: 0px 5px;">88</li><li style="Box-sizing: border-Box; padding: 0px 5px;">89</li></ul>
这个工具类很简单,没有采用Auto Layout进行布局,因为layoutSubviews
方法已经能很好的满足我们的需求了。当用户拖动任何一个UiSlider
的时候,我们能实时的通过colorChangedBlock
回调给外部。它能展现一个这样的视图:
不过虽然该工具类本身没有采用Auto Layout进行布局,但是它还是支持Auto Layout的,当它被添加到某个Auto Layout的视图中的时候,Auto Layout布局系统可以通过intrinsicContentSize
知道该视图的尺寸信息。
最后它还有一个setCurrentColor
方法从外部接收一个UIColor,可以用于初始化。
画笔设置的UI
我打算在用户点击画笔设置
的时候,从底部弹出一个控制面板(就像系统的Control Center
那样),所以我们还要有一个像这样的设置UI:
具体的,创建一个PaintingBrushSettingsView
类,同时创建一个PaintingBrushSettingsView.xib
文件,并把xib中view的Class
设为PaintingBrushSettingsView
,设置view的背景色为透明:
- 放置一个title为“画笔粗细”的
UILabel
,约束设为:宽度固定为68,高度固定为21,左和上边距为8。 - 放置一个title为“1”的
UILabel
,“1”与“画笔粗细”的垂直间距为10,宽度固定为10,高度固定为21,与superview
的左边距为10。 - 放置一个
UiSlider
,用于调节画笔的粗细,与“1”的水平间距为5,并与“1”垂直居中,高度固定为30,宽度暂时不设,在PaintingBrushSettingsView
中添加strokeWidthSlider
属性,与之连接起来。 - 放置一个title为“20”的
UILabel
,约束设为:宽度固定为20,高度固定为21,top与“1”相同,与superview
的右间距为10。并把上一步中的UiSlider
的右间距设为与“20”相隔5。 - 放置一个title为“画笔颜色”的
UILabel
,宽、高、left与“画笔粗细”相同,与上面UiSlider
的垂直间距设为12。 - 放置一个
UIView
至“画笔颜色”下方(上图中被选中的那个UIView),宽度固定为50,高度固定为30,left与“画笔颜色”相同,并且与“画笔颜色”的垂直间距为5,在strokeColorPreview
属性,与之连接起来。 - 放置一个
RGBColorPicker
,约束设为:left与顶部的UiSlider相同,底部与superview的间距为0,右间距为10,与上一步中的UIView的垂直间距为5。
PaintingBrushSettingsView类的完整代码如下:
strokeWidthChangedBlock和strokeColorChangedBlock
两个Block用于给外部传递状态。setBackgroundColor
用于初始化。实现毛玻璃效果
在把
PaintingBrushSettingsView
显示出来之前,我们要先想一想以何种方式展现比较好,众所周知Control Center
是有毛玻璃效果的,我们也想要这样的效果,而且不用自己实现。那如何产生效果? 答案是用UIToolbar
就行了。
UIToolbar
本身就是带有毛玻璃效果的,只要你不设置背景色,并且translucent
属性为true,“恰好”我们页面底部就有一个UIToolbar
,我们把它拉高就可以插入展现PaintingBrushSettingsView
了。
只要get到了这一点,毛玻璃效果就算实现了~~
测试画笔设置
toolbarConstraintHeight连接到
Main.storyboard
中对应的约束上就行了。toolbarEditingItems
能让我们在UIToolbar
上显示不同的items
,本来还需要一个toolbaritems
属性的,因为UIViewController
类本身就自带,我们便不用单独新增。currentSettingsView
是用来保存当前展示的哪个设置页面,考虑到我们后面会增加背景设置
,这个属性还是有必要的。
我们先写一个往toolbar上添加约束的工具方法:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">func addConstraintsToToolbarForSettingsView(view: UIView) { view<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.setTranslatesAutoresizingMaskIntoConstraints</span>(false) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.toolbar</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.addSubview</span>(view) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.addConstraints</span>(NSLayoutConstraint<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.constraintsWithVisualFormat</span>(<span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"H:|-0-[settingsView]-0-|"</span>,options: <span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.DirectionLeadingToTrailing</span>,metrics: nil,views: [<span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"settingsView"</span> : view])) self<span class="hljs-preprocessor" style="color: rgb(68,0); Box-sizing: border-Box;">"V:|-0-[settingsView(==height)]"</span>,metrics: [<span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"height"</span> : view<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.systemLayoutSizefittingSize</span>(UILayoutFittingCompressedSize)<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.height</span>],0); Box-sizing: border-Box;">"settingsView"</span> : view])) }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li><li style="Box-sizing: border-Box; padding: 0px 5px;">12</li><li style="Box-sizing: border-Box; padding: 0px 5px;">13</li></ul>这个工具方法会把传入进来的view添加到toolbar上,同时添加相应的约束。注意高度的约束,我是通过
systemLayoutSizefittingSize
方法计算出设置视图最佳的高度,这是为了达到更好的拓展性(背景设置与画笔设置所需要的高度很可能会不同)。
然后再增加一个setupBrushSettingsView
方法:我们在这个方法里实例化了一个
PaintingBrushSettingsView
,并添加到toolbar上,增加相应的约束,以及一些初始化设置和两个Block回调的处理。
然后修改viewDidLoad
方法,增加以下行为:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136,0); Box-sizing: border-Box;">//---</span> <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.toolbarEditingItems</span> = [ <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIBarButtonItem</span>(barButtonSystemItem:<span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.FlexibleSpace</span>,target: <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">nil</span>,action: <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">nil</span>),102); Box-sizing: border-Box;">UIBarButtonItem</span>(title: <span class="hljs-string" style="color: rgb(0,0); Box-sizing: border-Box;">"完成"</span>,style:<span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.Plain</span>,target: <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">self</span>,0); Box-sizing: border-Box;">"endSetting"</span>) ] <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.toolbaritems</span> = <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.toolbar</span><span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.items</span> <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.setupBrushSettingsView</span>() <span class="hljs-comment" style="color: rgb(136,0); Box-sizing: border-Box;">//---</span></code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li></ul>Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">@IBAction func paintingBrushSettings() { self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.currentSettingsView</span> = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.viewWithTag</span>(<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">1</span>) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.currentSettingsView</span>?<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.hidden</span> = false self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.updatetoolbarForSettingsView</span>() } func updatetoolbarForSettingsView() { self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.toolbarConstraintHeight</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.constant</span> = self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.currentSettingsView</span>!<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.height</span> + <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">44</span> self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.setItems</span>(self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.toolbarEditingItems</span>,animated: true) UIView<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.beginAnimations</span>(nil,context: nil) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.layoutIfNeeded</span>() UIView<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.commitAnimations</span>() self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.bringSubviewToFront</span>(self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.currentSettingsView</span>!) }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li><li style="Box-sizing: border-Box; padding: 0px 5px;">12</li><li style="Box-sizing: border-Box; padding: 0px 5px;">13</li><li style="Box-sizing: border-Box; padding: 0px 5px;">14</li><li style="Box-sizing: border-Box; padding: 0px 5px;">15</li><li style="Box-sizing: border-Box; padding: 0px 5px;">16</li><li style="Box-sizing: border-Box; padding: 0px 5px;">17</li><li style="Box-sizing: border-Box; padding: 0px 5px;">18</li></ul>updatetoolbarForSettingsView也是一个工具方法,用于更新toolbar的高度。
由于我们采用了Auto Layout进行布局,动画要通过调用layoutIfNeeded
方法来实现。
响应点击“完成”按钮的endSetting
方法:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">@IBAction func endSetting() { <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.toolbarConstraintHeight</span><span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.constant</span> = <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">44</span> <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.setItems</span>(<span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.toolbaritems</span>,animated: <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">true</span>) <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIView</span><span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.beginAnimations</span>(<span class="hljs-literal" style="color: rgb(0,context: <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">nil</span>) <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.layoutIfNeeded</span>() <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">.commitAnimations</span>() <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.currentSettingsView</span>?<span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.hidden</span> = <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">true</span> }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li></ul>这么一来画笔设置就做完了,代码应该还是比较好理解,编译、运行后,应该能看到:
完成度已经很高了^^!
背景设置
整体的框架基本上已经在之前的工作中搭好了,我们快速过掉这一节。
在Main.storyboard
中增加了一个title为“背景设置”的UIBarButtonItem
,并将action连接到backgroundSettings
方法上,你可以选择在插入“背景设置”之前,先插入一个FlexibleSpace
的UIBarButtonItem
。
创建BackgroundSettingsVC
类,继承自UIViewController
,这与画笔设置继承于UIView
不同,我们希望背景设置可以在用户的相册中选择照片,而使用UIImagePickerController
的前提是要实现UIImagePickerControllerDelegate
、UINavigationControllerDelegate
两个接口,如果让UIView来实现这两个接口会很奇怪。
创建一个BackgroundSettingsVC.xib
文件:
- 放置一个title为“从相册中选择背景图”的UIButton,约束为:左、上边距为8,宽度固定为135,高度固定为30。
- 放置一个RGBColorPicker,约束为:左、右边距为8,与UIButton的垂直间距为20,底部与superview齐平。
- 把UIButton的
Touch Up Inside
事件连接到BackgroundSettingsVC
的pickImage
方法上;RGBColorPicker连接到colorPicker
属性上。看上去像这样:
BackgroundSettingsVC类的完整代码:
Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">class BackgroundSettingsVC : <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIViewController</span>,UIImagePickerControllerDelegate,UINavigationControllerDelegate { var backgroundImageChangedBlock: ((backgroundImage: <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIImage</span>) -> Void)? var backgroundColorChangedBlock: ((backgroundColor: <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIColor</span>) -> Void)? @IBOutlet private var colorPicker: RGBColorPicker! lazy private var pickerController: UIImagePickerController = { [uNowned <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">self</span>] in let pickerController = UIImagePickerController() pickerController<span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.delegate</span> = <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">self</span> <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">return</span> pickerController }() override func awakeFromNib() { <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.awakeFromNib</span>() <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.colorPicker</span><span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.colorChangedBlock</span> = { [uNowned <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">self</span>] (color: <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIColor</span>) in <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">if</span> let backgroundColorChangedBlock = <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.backgroundColorChangedBlock</span> { backgroundColorChangedBlock(backgroundColor: color) } } } func setBackgroundColor(color: <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIColor</span>) { <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.setCurrentColor</span>(color) } @IBAction func pickImage() { <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.presentViewController</span>(<span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.pickerController</span>,102); Box-sizing: border-Box;">true</span>,completion: <span class="hljs-literal" style="color: rgb(0,102); Box-sizing: border-Box;">nil</span>) } <span class="hljs-comment" style="color: rgb(136,0); Box-sizing: border-Box;">// MARK: UIImagePickerControllerDelegate Methods</span> func imagePickerController(picker: UIImagePickerController,didFinishPickingMediawithInfo info: [<span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">NSObject</span> : AnyObject]) { let image = info[UIImagePickerControllerOriginalImage] as <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIImage</span> <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">if</span> let backgroundImageChangedBlock = <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.backgroundImageChangedBlock</span> { backgroundImageChangedBlock(backgroundImage: image) } <span class="hljs-keyword" style="color: rgb(0,102); Box-sizing: border-Box;">.dismissViewControllerAnimated</span>(<span class="hljs-literal" style="color: rgb(0,0); Box-sizing: border-Box;">// MARK: UINavigationControllerDelegate Methods</span> func navigationController(navigationController: <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UINavigationController</span>,willShowViewController viewController: <span class="hljs-built_in" style="color: rgb(102,animated: Bool) { <span class="hljs-built_in" style="color: rgb(102,102); Box-sizing: border-Box;">UIApplication</span><span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.sharedApplication</span>()<span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.setStatusBarHidden</span>(<span class="hljs-literal" style="color: rgb(0,withAnimation: <span class="hljs-variable" style="color: rgb(102,102); Box-sizing: border-Box;">.None</span>) } }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li><li style="Box-sizing: border-Box; padding: 0px 5px;">12</li><li style="Box-sizing: border-Box; padding: 0px 5px;">13</li><li style="Box-sizing: border-Box; padding: 0px 5px;">14</li><li style="Box-sizing: border-Box; padding: 0px 5px;">15</li><li style="Box-sizing: border-Box; padding: 0px 5px;">16</li><li style="Box-sizing: border-Box; padding: 0px 5px;">17</li><li style="Box-sizing: border-Box; padding: 0px 5px;">18</li><li style="Box-sizing: border-Box; padding: 0px 5px;">19</li><li style="Box-sizing: border-Box; padding: 0px 5px;">20</li><li style="Box-sizing: border-Box; padding: 0px 5px;">21</li><li style="Box-sizing: border-Box; padding: 0px 5px;">22</li><li style="Box-sizing: border-Box; padding: 0px 5px;">23</li><li style="Box-sizing: border-Box; padding: 0px 5px;">24</li><li style="Box-sizing: border-Box; padding: 0px 5px;">25</li><li style="Box-sizing: border-Box; padding: 0px 5px;">26</li><li style="Box-sizing: border-Box; padding: 0px 5px;">27</li><li style="Box-sizing: border-Box; padding: 0px 5px;">28</li><li style="Box-sizing: border-Box; padding: 0px 5px;">29</li><li style="Box-sizing: border-Box; padding: 0px 5px;">30</li><li style="Box-sizing: border-Box; padding: 0px 5px;">31</li><li style="Box-sizing: border-Box; padding: 0px 5px;">32</li><li style="Box-sizing: border-Box; padding: 0px 5px;">33</li><li style="Box-sizing: border-Box; padding: 0px 5px;">34</li><li style="Box-sizing: border-Box; padding: 0px 5px;">35</li><li style="Box-sizing: border-Box; padding: 0px 5px;">36</li><li style="Box-sizing: border-Box; padding: 0px 5px;">37</li><li style="Box-sizing: border-Box; padding: 0px 5px;">38</li><li style="Box-sizing: border-Box; padding: 0px 5px;">39</li><li style="Box-sizing: border-Box; padding: 0px 5px;">40</li><li style="Box-sizing: border-Box; padding: 0px 5px;">41</li><li style="Box-sizing: border-Box; padding: 0px 5px;">42</li><li style="Box-sizing: border-Box; padding: 0px 5px;">43</li><li style="Box-sizing: border-Box; padding: 0px 5px;">44</li><li style="Box-sizing: border-Box; padding: 0px 5px;">45</li><li style="Box-sizing: border-Box; padding: 0px 5px;">46</li><li style="Box-sizing: border-Box; padding: 0px 5px;">47</li><li style="Box-sizing: border-Box; padding: 0px 5px;">48</li><li style="Box-sizing: border-Box; padding: 0px 5px;">49</li><li style="Box-sizing: border-Box; padding: 0px 5px;">50</li></ul>同样用两个Block进行回调;
setBackgroundColor
公共方法用于设置内部的RGBColorPicker的初始颜色状态;在UINavigationControllerDelegate
里隐藏系统默认显示的状态栏。
回到ViewController
,我们对背景设置进行测试。
像setupBrushSettingsView
方法一样,我们增加一个setupBackgroundSettingsView
方法:修改viewDidLoad方法:实现backgroundSettings
方法:编译、运行,现在你可以用不同的背景色(或背景图)了!
全屏绘图
到目前为止,
Board
一直显示不全(事实上,我很早就实现了全屏绘图,但是优先级一直被我排在最后),现在是时候来解决它了。
解决思路是这样的:当用户开始绘图的时候,我们把顶部和底部两个View隐藏;当用户结束绘图的时候,再让两个View显示。
为了获取用户的绘图状态,我们需要在Board
里加个“钩子”:这样一来用户绘图的状态就在ViewController掌握中了。
ViewController想要控制两个View的话,还需要增加几个属性:然后在viewDidLoad方法里增加对“钩子”的处理:Nowrap; word-wrap: break-word; Box-sizing: border-Box; margin-top: 0px; margin-bottom: 1.1em; font-family: 'Source Code Pro',monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.drawingStateChangedBlock</span> = {(state: DrawingState) -> () <span class="hljs-keyword" style="color: rgb(0,136); Box-sizing: border-Box;">in</span> if state != <span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.Moved</span> { UIView<span class="hljs-preprocessor" style="color: rgb(68,context: nil) if state == <span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.Began</span> { self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.topViewConstraintY</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.constant</span> = -self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.topView</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.height</span> self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.toolbarConstraintBottom</span><span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.height</span> self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.layoutIfNeeded</span>() self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.layoutIfNeeded</span>() } else if state == <span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.Ended</span> { UIView<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.setAnimationDelay</span>(<span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">1.0</span>) self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.constant</span> = <span class="hljs-number" style="color: rgb(0,102); Box-sizing: border-Box;">0</span> self<span class="hljs-preprocessor" style="color: rgb(68,102); Box-sizing: border-Box;">0</span> self<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.layoutIfNeeded</span>() } UIView<span class="hljs-preprocessor" style="color: rgb(68,68); Box-sizing: border-Box;">.commitAnimations</span>() } }</code><ul class="pre-numbering" style="Box-sizing: border-Box; position: absolute; width: 50px; background-color: rgb(238,221); list-style: none; text-align: right;"><li style="Box-sizing: border-Box; padding: 0px 5px;">1</li><li style="Box-sizing: border-Box; padding: 0px 5px;">2</li><li style="Box-sizing: border-Box; padding: 0px 5px;">3</li><li style="Box-sizing: border-Box; padding: 0px 5px;">4</li><li style="Box-sizing: border-Box; padding: 0px 5px;">5</li><li style="Box-sizing: border-Box; padding: 0px 5px;">6</li><li style="Box-sizing: border-Box; padding: 0px 5px;">7</li><li style="Box-sizing: border-Box; padding: 0px 5px;">8</li><li style="Box-sizing: border-Box; padding: 0px 5px;">9</li><li style="Box-sizing: border-Box; padding: 0px 5px;">10</li><li style="Box-sizing: border-Box; padding: 0px 5px;">11</li><li style="Box-sizing: border-Box; padding: 0px 5px;">12</li><li style="Box-sizing: border-Box; padding: 0px 5px;">13</li><li style="Box-sizing: border-Box; padding: 0px 5px;">14</li><li style="Box-sizing: border-Box; padding: 0px 5px;">15</li><li style="Box-sizing: border-Box; padding: 0px 5px;">16</li><li style="Box-sizing: border-Box; padding: 0px 5px;">17</li><li style="Box-sizing: border-Box; padding: 0px 5px;">18</li><li style="Box-sizing: border-Box; padding: 0px 5px;">19</li><li style="Box-sizing: border-Box; padding: 0px 5px;">20</li></ul>只有当状态为开始或结束的时候我们才需要更新UI状态,而且我们在结束的事件里延迟了1秒钟,这样用户可以暂时预览下全图。
保存到图库
最后一个功能:保存到图库!
在toolbar上插入一个title为“保存到图库”的UIBarButtonItem
,还是可以先插入一个UIBarButtonItem
,然后把action连接到ViewController的savetoAlbumy
方法上: