您的位置:首页 > 移动开发 > Swift

如何用Swift制作一个简单的画板APP

2016-09-08 11:10 465 查看


怎样用Swift的UIKit制作一个简单的画板APP???                                                  ---翻译自https://www.raywenderlich.com/87899/make-simple-drawing-app-uikit-swift

在我们成长的某些阶段,很多人都喜欢画画,卡通等。对于我来说,我是伴随着笔和纸长大的一代人,但是计算机和触屏设备的快速发展已经快要取代笔和纸的用途!画画可以在触屏设备上,就像你看到Apple Store里有很多画画的App.

想要学习怎样制作一个画板app?? 好消息就是相对比较简单,感谢在ios里有很多的可用的画板API。

在整个学习过程中,你需要学习到:

1.使用Quartz2D来勾画线条。

2.使用多种颜色。

3.设置画笔的宽度和透明度。

4.创建一个橡皮。

5.设置RGB的颜色属性。

6.保存用户在画板中画的画。

7.共享你的画的朋友圈或微博。

开始制作

首先,下载这个项目的基本框架starter
project。

打开xcode, 打开你的项目浏览一下里面的文件,你可以看到我基本上没有为你做太多的工作。我只是添加了所有你需要的图片,然后创建了一个app的主视图和该有的约束。这个项目是基于single View Application的模版。

现在打开Main.storyboard然后看一下界面。这个视图控制器在顶部有3个按钮。就像文字所示,这三个按钮分别用来“重置”,“设置画板”和“保存”。 在界面的底部,你可以看到有很多的按钮(有“笔”的背景图片),和一个橡皮。它们都被用来选择颜色。

另外,还有两个视图叫“mainImageView"和“tempImageView"--你之后就会知道为什么你需要这两个视图,这些可以让用户在画画的时候设置不同的画笔透明级别。



视图控制器有actions和outlets两种设置,每一个顶部的按钮都有一个action, 笔的颜色都会连接到相同的action, 另外有两个Image View的outlets.

接下来,我们需要添加一些代码到你的项目里面。

你的App需要以一个简单的画画功能作为开始,用你的手指在屏幕上滑动,来画出一条简单的黑色的线条。

打开ViewController.swift,然后添加下面的属性到类之中:

var lastPoint = CGPoint.zeroPoint
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false


接下来,我来解释一下上面所声明的变量:

1.last point存储的是在画板上最后的一个绘制点,这是使用在一个连续的触笔画在画板上。

2.red,green,blue存储的是当前RGB的值,用来选择颜色。

3.brushWidth和opacity存储的是画笔的宽度和透明度。

4.swiped定义了是否触笔是连续的。

5.RGB的默认值是0,也就是说画画的颜色以黑色为默认值。默认的透明度设置为1.0, 线条的宽度设置为10.0.

现在,终于到了画画的阶段了! 所有的touch-notifying方法都是来自于父类UIResponder, 它会响应触碰开始,移动,触碰结束的事件。你会用到这3种方法来实现你的画画逻辑。

现在开始添加以下的方法:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
swiped = false
if let touch = touches.first as? UITouch {
lastPoint = touch.locationInView(self.view)
}
}

touchesBegan是当用户把手指按在屏幕上的时候调用的方法,这也就是画画事件开始的第一件事,所以你首先要初始化swiped为false在触碰还没有移动的时候。你还需要保存触碰的移动轨迹在最后一个点位的时候,所以,当用户开始用手指画画的时候,你可以追踪用户手指的轨迹(何时开始)。

接下来,添加以下两个方法到你的代码里:

func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
 
// 1
UIGraphicsBeginImageContext(view.frame.size)
let context = UIGraphicsGetCurrentContext()
tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
 
// 2
CGContextMoveToPoint(context, fromPoint.x, fromPoint.y)
CGContextAddLineToPoint(context, toPoint.x, toPoint.y)
 
// 3
CGContextSetLineCap(context, kCGLineCapRound)
CGContextSetLineWidth(context, brushWidth)
CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)
CGContextSetBlendMode(context, kCGBlendModeNormal)
 
// 4
CGContextStrokePath(context)
 
// 5
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
tempImageView.alpha = opacity
UIGraphicsEndImageContext()
 
}
 
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
// 6
swiped = true
if let touch = touches.first as? UITouch {
let currentPoint = touch.locationInView(view)
drawLineFrom(lastPoint, toPoint: currentPoint)
 
// 7
lastPoint = currentPoint
}
}

我在来解释一下:

1. 在第一个方法中,负责在两点之间画一条线。记住这个app会有两个image View--mainImageView(目前为止所画的)和tempImageView(线条你现在所画的)。现在你希望画进tempImageView, 所以你需要定义一个常量,来存储画画的内容。

2. 接下来,你拿到了用户当前触碰的点位,然后画一条线用CGContextAddLineToPoint从lastPoint到currentPoint. 这个将会生产出一些直线,所谓两点一线。

3. 这些就是所有画板的参数,笔刷的尺寸,透明度,颜色。

4. 这就是神奇的部分,这就是你画的路径。

5. 接下来,你需要完成绘画内容,然后渲染整条线到temporary image view.

6. 在touchesMoved里,你设置了swiped为true, 所以你可以追踪到当前的触屏滑动。接着你可以调用hepler方法也就是你刚刚写的drawLine.

7.最后,因为你更新了lastPoint, 所以接下来会继续你的触碰事件。

下一步,添加最后的触屏处理程序touchesEnded.

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
 
if !swiped {
// draw a single point
drawLineFrom(lastPoint, toPoint: lastPoint)
}
 
// Merge tempImageView into mainImageView
UIGraphicsBeginImageContext(mainImageView.frame.size)
mainImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: 1.0)
tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: opacity)
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
 
tempImageView.image = nil
}

首先,你检测是否用户在屏幕滑动当中,如果不在,那么就意味着用户点击了屏幕画了一个点。

如果用户是在手指滑动之中,那就是说你可以跃过画一个点--在touchesMoved方法调用之前,当调用touchesEnded方法的时候你就不需要画更多的东西。

最后一步就是合并tempImageView和mainImageView. 你画的触笔tempImageView而不是mainImageView. 

当你在tempImageView里画图的时候,透明度设置为1.0(完全不透明),但当你合并tempImageView和mainImageView时,你可以设置tempImageView的透明度来配置值,可以给你的笔刷定义你想要的透明度。如果你直接画在mainImageView上,那么以不同透明度的画笔画画将是一件非常困难的事。

好啦,现在准备画图吧!运行你的app,你会看见你现在可以画一些黑色的线条在你的画板上了!!!



这是一个非常好的开始哦! 用这些触摸的处理方法你可以有很多的功能。现在到时候填写更多的选项,那我们先从颜色开始。

设置颜色

是时候添加颜色到你的场景上了!

目前总共有10款颜色按钮在界面上,但是如果你想点击其他颜色的笔,现在是没效果的。所以首先,你需要定义这些颜色。添加下面数组属性到类里。

let colors: [(CGFloat, CGFloat, CGFloat)] = [
(0, 0, 0),
(105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0),
(1.0, 0, 0),
(0, 0, 1.0),
(51.0 / 255.0, 204.0 / 255.0, 1.0),
(102.0 / 255.0, 204.0 / 255.0, 0),
(102.0 / 255.0, 1.0, 0),
(160.0 / 255.0, 82.0 / 255.0, 45.0 / 255.0),
(1.0, 102.0 / 255.0, 0),
(1.0, 1.0, 0),
(1.0, 1.0, 1.0),
]

这些建立了一个关于RGB值的数组,每一个数组的元素都是3个CGFloats的元组。这里的每一个颜色都匹配每一个界面按钮的标签。

接下来,找到pencilPressed这个方法,然后添加下面的代码:

// 1
var index = sender.tag ?? 0
if index < 0 || index >= colors.count {
index = 0
}
 
// 2
(red, green, blue) = colors[index]
 
// 3
if index == colors.count - 1 {
opacity = 1.0
}

这是一个很短的方法,但是让我们来一起一步一步的看一下。

1. 首先,你需要知道哪一个颜色的索引是用户选择的。这里会有一些地方会导致错误-错误的标签,标签没有设置,数组里没有足够的颜色--所以你需要检查这些地方。默认值是黑色,也就是第一个颜色。

2. 接下来,你设置red, green, blue的属性。

3. 最后一个按钮是橡皮,所以有一点特殊。橡皮按钮设置了颜色是白色,透明度为1.0。就像你看到的背景是白色一样,这会给你一个非常方便的橡皮效果。

现在准备好画彩色的画了么?? 运行你的app,你可以绘画出彩色的线条啦!现在你可以点击颜色按钮,改变笔刷的颜色。



白板

所有伟大的艺术家都有退后一步然后摇着头低声说“不,不,这永远不会做!”这种情况。那么你要提供一种情况来清理你的画板,并让用户可以一遍又一遍的开始画图。你已经有一个reset(重置)按钮设置在您的应用程序里了。

找到reset()这个方法,并添加这句代码:

mainImageView.image = nil

就是这样,信不信由你。上面的代码就是设置mainImageView的图为空。瞧!你的画板已经清理啦。记住,你画的线是在imageView的内容里面,所以把这清除为0,将会重置所有的事情。构建和运行你的app, 随便画一些东西,然后点击重置按钮来清除你的画。就是这样,你可没必要因为撕毁你的画而沮丧。

完成触摸--设置

好啦!你现在拥有了一个功能性的画板app,但是我们还有第二个设置界面要做。

首先,打开SettingsViewController.swift然后添加下面两行属性到类里:

var brush: CGFloat = 10.0
var opacity: CGFloat = 1.0

这个会让你一直追踪用户选择的笔刷的尺寸和透明度。

现在添加以下代码到sliderChanged():

if sender == sliderBrush {
brush = CGFloat(sender.value)
labelBrush.text = NSString(format: "%.2f", brush.native) as String
} else {
opacity = CGFloat(sender.value)
labelOpacity.text = NSString(format: "%.2f", opacity.native) as String
}
 
drawPreview()

这上面的代码,随着滑动条控制的变化,滑块值将适当的匹配。那么你就需要更新那些在drawPreview的预览图像,那么你将添加下一段到drawPreview里:

func drawPreview() {
UIGraphicsBeginImageContext(imageViewBrush.frame.size)
var context = UIGraphicsGetCurrentContext()
 
CGContextSetLineCap(context, kCGLineCapRound)
CGContextSetLineWidth(context, brush)
 
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0)
CGContextMoveToPoint(context, 45.0, 45.0)
CGContextAddLineToPoint(context, 45.0, 45.0)
CGContextStrokePath(context)
imageViewBrush.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
 
UIGraphicsBeginImageContext(imageViewBrush.frame.size)
context = UIGraphicsGetCurrentContext()
 
CGContextSetLineCap(context, kCGLineCapRound)
CGContextSetLineWidth(context, 20)
CGContextMoveToPoint(context, 45.0, 45.0)
CGContextAddLineToPoint(context, 45.0, 45.0)
 
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, opacity)
CGContextStrokePath(context)
imageViewOpacity.image = UIGraphicsGetImageFromCurrentImageContext()
 
UIGraphicsEndImageContext()
}

这个方法用了同样的技术来画一个预览设置,就像视图控制器用触摸的处理方法。在这两种情况下,该方法绘制一个点,而不是一条线(有着适当宽度和不透明度的滑块的值)。

运行你的代码,打开设置界面,调动滑动框。你会看见预览图和值会跟着你的滑动框移动而变化。



集成设置

这里有一个重要的地方丢失了,你有没有注意到??

更新过的透明度和宽度值还没有被应用于ViewController绘图板!这是因为你还没有把设置界面特定的值放到ViewController里。这就需要用到代理和协议了!

打开SettingViewController.swift文件,然后添加以下代码:

protocol SettingsViewControllerDelegate: class {
func settingsViewControllerFinished(settingsViewController: SettingsViewController)
}

这段代码的意思就是定义一个协议,里面有一个叫settingsViewControllerFinished的方法。

然后添加一个属性到SettingsViewController的类里:

weak
var
delegate:
SettingsViewControllerDelegate?

我们将引用这个代理。如果有一个代理,那么你需要通知它当用户点击了关闭按钮。找到close()然后添加以下代码到这个方法的最后:

self.delegate?.settingsViewControllerFinished(self)

这个就是调用代理的方法,然后可以自己更新新的值。
现在,打开ViewController.swift文件,然后添加一个扩展的类在文件底部给协议。

extension ViewController: SettingsViewControllerDelegate {
func settingsViewControllerFinished(settingsViewController: SettingsViewController) {
self.brushWidth = settingsViewController.brush
self.opacity = settingsViewController.opacity
}
}

这就表明了类符合SettingViewControllerDelegate和实现它的一个方法。在实现中,需要做的就是设置视图的滑动控件来调节当前画笔的宽度和透明度的值。当用户从画图转向设置界面,这就意味着你需要让用户打开你的设置界面。

添加下面的方法复写这个类:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let settingsViewController = segue.destinationViewController as! SettingsViewController
settingsViewController.delegate = self
settingsViewController.brush = brushWidth
settingsViewController.opacity = opacity
}

当用户点击设置按钮触发segue,这个方法将配置新的SettingsViewController, 通过设置代理。

运行你的app,在这个阶段,你会看到笔刷和透明度值现在更新后可以在设置界面改变。现在,你可以画许多颜色的画,也可以有不同大小的笔和透明级别啦!!



收尾工作--自定义颜色选择器

现在,你有10个颜色按钮在你的画板界面中,但是在颜色选择器里,有一系列的RGB颜色调动栏在你的设置界面中。

既然你已经提供了一个预览画笔大小和透明度,你不妨再提供一个预览新画笔的颜色!。 预览效果会显示在预览图像里,就像透明度和画笔预览会显示在RGB颜色里。不需要额外的图片,重用你已经有的!

打开SettingsViewController.swift 然后添加下面的属性:

var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0

你要用到这些来保存你当前的RGB值。
现在添加以下代码到colorChanged:

red = CGFloat(sliderRed.value / 255.0)
labelRed.text = NSString(format: "%d", Int(sliderRed.value)) as String
green = CGFloat(sliderGreen.value / 255.0)
labelGreen.text = NSString(format: "%d", Int(sliderGreen.value)) as String
blue = CGFloat(sliderBlue.value / 255.0)
labelBlue.text = NSString(format: "%d", Int(sliderBlue.value)) as String
 
drawPreview()

这是将要被你调用的方法,当你的RGB滑块移动的时候。在上面的代码中,请注意你所做的事情就是更新属性值,更新标签。

如果你现在运行你的项目,你会发现颜色变化将不会显示在预览里。为了显示,你需要在drawPreview()里做一点小小的改变。搜索行调用CGContextSetRGBStrokeColor,替换所有的0.0值和红色,绿色 蓝色变量。

在第一个方法中,替换调用CGContextSetRGBStrokeColor,用下面的代码:

CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)

接下来,在下半部分,替换调用CGContextSetRGBStrokeColor,用下面的代码:

CGContextSetRGBStrokeColor(context, red, green, blue, opacity)

现在,你已经有了所有的正确的设置,你想要正确显示这个设置界面,添加以下代码到viewWillAppear:

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
 
sliderBrush.value = Float(brush)
labelBrush.text = NSString(format: "%.1f", brush.native) as String
sliderOpacity.value = Float(opacity)
labelOpacity.text = NSString(format: "%.1f", opacity.native) as String
sliderRed.value = Float(red * 255.0)
labelRed.text = NSString(format: "%d", Int(sliderRed.value)) as String
sliderGreen.value = Float(green * 255.0)
labelGreen.text = NSString(format: "%d", Int(sliderGreen.value)) as String
sliderBlue.value = Float(blue * 255.0)
labelBlue.text = NSString(format: "%d", Int(sliderBlue.value)) as String
 
drawPreview()
}

这样你就可以看到,这个方法预设了正确的值给所有的标签和滑动栏。 drawPreview调用预览视图会正确的显示出来。

最后,打开ViewController.swift. 就像之前一样,你需要确定当前的颜色到设置界面,所以添加下面3行代码到prepareForSegue的最后:

settingsViewController.red = red
settingsViewController.green = green
settingsViewController.blue = blue

这样RGB滑动框就被正确设置了。

最后,找到settingsViewControllerFinished在扩展类里,添加下面3行代码到方法里:

self.red = settingsViewController.red
self.green = settingsViewController.green
self.blue = settingsViewController.blue

大功告成! 是时候运行你的app啦。 滑动你的颜色选择器,选中RGB颜色,显示在RGBPreview里,现在默认的笔的颜色就在画板上啦。

你得到了一款属于你自己的画板。。现在可以尽情的画图享受了。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  swift uikit app