应用示例(一) —— 基于UIKit的绘图应用示例(一)

版本记录

版本号 时间
V1.0 2018.09.09

前言

平时大家除了做工程的需求以外,有时候也会写一些小Demo或者小的应用,这里我就重新开了一个专题,专门收集一些小的应用或者示例。

开始

首先说一下本文的写作环境

注意:写作环境 Swift 4.2, iOS 12, Xcode 10

在本文中主要完成以下内容:

  • 使用Quartz2D绘制线条和笔触。
  • 使用多种颜色。
  • 设置画笔描边宽度和不透明度。
  • 创建一个橡皮擦。
  • 创建自定义RGB颜色选择器。
  • 分享你的画作!

新建工程,并在sb中进行设置。打开Main.storyboard并查看界面。 最初的ViewController场景顶部有三个按钮。 您将使用这些来重置或共享绘图并调出“设置”屏幕。 在底部,您可以看到更多带铅笔图像和橡皮擦的按钮。 您将使用它们来选择预定义的颜色。

最后,有两个名为MainImageViewTempImageView的图像视图 - 稍后您将看到为什么在使用它们时需要两个图像视图以允许用户使用不同的画笔不透明度级别进行绘制。

View Controller窗口按照您的预期设置操作和出口:顶部的每个按钮都有一个动作,铅笔颜色都链接到相同的动作 - 它们设置了不同的tag以区分它们 - 并且有outlets用于两个图像视图。

下面我们开始添加一些代码!


Being Quick on the Draw - 从绘制快速开始

您将从简单的绘图功能开始,您可以在屏幕上滑动手指以绘制简单的黑色线条。

打开ViewController.swift并将以下属性添加到类中:

var lastPoint = CGPoint.zero
var color = UIColor.black
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false

以下是对这些变量的快速解释:

  • lastPoint存储画布上的最后一个绘制点。 当用户在画布上绘制连续画笔笔划时,您将需要此选项。
  • color存储当前选定的颜色。 它默认为黑色。
  • brushWidth存储画笔描边宽度。 默认为10.0。
  • opacity存储画笔不透明度。 它默认为1.0。
  • swiped表示画笔笔划是否连续。

现在为绘图部分! 所有触摸通知方法都来自父类UIResponder,它们响应触摸的开始,移动或结束。 您将使用这三种方法来实现绘图逻辑。

首先添加以下方法:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  guard let touch = touches.first else {
    return
  }
  swiped = false
  lastPoint = touch.location(in: view)
}

当用户将手指放在屏幕上时,系统调用touchesBegan(_:with :)。 这是绘图活动的开始,所以你首先要确保你确实收到了一个触摸。 然后重置swipedfalse,因为触摸尚未移动。 您还可以在lastPoint中保存触摸位置,这样当用户开始绘图时,您可以跟踪笔画的开始位置。 可以这么说,刷子点击纸张的位置。

接下来添加以下两种方法:

func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
  // 1
  UIGraphicsBeginImageContext(view.frame.size)
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
  tempImageView.image?.draw(in: view.bounds)
    
  // 2
  context.move(to: fromPoint)
  context.addLine(to: toPoint)
  
  // 3  
  context.setLineCap(.round)
  context.setBlendMode(.normal)
  context.setLineWidth(brushWidth)
  context.setStrokeColor(color.cgColor)
  
  // 4  
  context.strokePath()
  
  // 5 
  tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  tempImageView.alpha = opacity   
  UIGraphicsEndImageContext()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
  guard let touch = touches.first else {
    return
  }

  // 6
  swiped = true
  let currentPoint = touch.location(in: view)
  drawLine(from: lastPoint, to: currentPoint)
    
  // 7
  lastPoint = currentPoint
}

以下是这些方法的内容:

  • 1) 第一种方法负责在两点之间绘制一条线。请记住,这个应用程序有两个图像视图:mainImageView,其中包含“绘图到目前为止”,以及tempImageView,它包含“您当前正在绘制的线条”。在这里,您想要绘制到tempImageView中,因此您需要使用当前在tempImageView中的图像设置绘图上下文,该图像应该在第一次为空时。
  • 2) 接下来,获取当前触摸点,然后从lastPointcurrentPoint绘制一条线。您可能认为这种方法会产生一系列直线,结果看起来像一组锯齿状的线条。这将产生直线,但触摸事件发生得如此之快,以至于线条足够短,结果看起来像一条漂亮的平滑曲线。
  • 3) 在这里,您可以为画笔大小和笔触颜色设置一些绘图参数。如果您有兴趣了解这些内容,请查看Apple的CGContext文档。
  • 4) 这就是你实际绘制路径的地方!
  • 5) 接下来,您需要结束绘图上下文以将新行渲染到temporary图像视图中。您将获得上下文的图像表示,并将其设置为tempImageViewimage属性。
  • 6) 当用户在屏幕上拖动手指时,系统调用touchesMoved(_:with :)。在这里,您将swiped设置为true,以便跟踪当前是否正在进行滑动。由于这是touchesMoved,答案是肯定的,有一个滑动正在进行中!然后,您调用刚刚编写的帮助器方法来绘制线条。
  • 7) 最后,您更新lastPoint,以便下次触摸事件将继续您刚离开的位置。

接下来,添加最终的触摸处理程序:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  if !swiped {
    // draw a single point
    drawLine(from: lastPoint, to: lastPoint)
  }
    
  // Merge tempImageView into mainImageView
  UIGraphicsBeginImageContext(mainImageView.frame.size)
  mainImageView.image?.draw(in: view.bounds, blendMode: .normal, alpha: 1.0)
  tempImageView?.image?.draw(in: view.bounds, blendMode: .normal, alpha: opacity)
  mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
    
  tempImageView.image = nil
}

当用户将手指从屏幕上抬起时,iOS会调用touchesEnded(_:with :)。在这里,首先检查用户是否在滑动中。如果没有,则表示用户只需点击屏幕即可绘制单个点。在这种情况下,只需使用您之前编写的辅助方法绘制单个点。

如果用户正在滑动中,这意味着您可以跳过绘制该单点 - 因为之前调用了touchesMoved(_:with :),您不需要进一步绘制。

最后一步是将tempImageViewmainImageView合并。您在tempImageView而不是mainImageView上绘制了笔刷线条。额外的UIImageView有什么意义,你不能直接绘制到mainImageView吗?

你可以,但双图像视图用于保持不透明度。当您在tempImageView上绘图时,不透明度设置为1.0(完全不透明)。但是,将tempImageViewmainImageView合并时,可以将tempImageView不透明度设置为配置的值,从而为画笔描边提供所需的不透明度。如果要直接在mainImageView上绘制,绘制具有不同不透明度值的笔刷线条将非常困难。

好的,是时候画了!构建并运行您的应用程序。您将看到您现在可以在画布上绘制漂亮的黑线!

这是一个很好的开始! 只需使用这些触摸处理方法,您就拥有了大量的功能。 现在是时候填写更多选项,从颜色开始。


Adding Colors - 添加颜色

现在是时候为场景增添色彩了 - 单独的艺术线条有点单调。

此刻屏幕上有10个颜色按钮,但如果你现在点击任何按钮,则没有任何反应。 要实现这一点,首先需要定义所有颜色。

打开Pencil.swift。 Pencil是一个枚举,表示用户可以选择的各种颜色选项。 它有一个初始化程序init?(tag :),它接受一个按钮tag并将其转换为所需的铅笔。 将以下计算属性添加到Pencil

var color: UIColor {
  switch self {
  case .black:
    return .black
  case .grey:
    return UIColor(white: 105/255.0, alpha: 1.0)
  case .red:
    return UIColor(red: 1, green: 0, blue: 0, alpha: 1.0)
  case .darkblue:
    return UIColor(red: 0, green: 0, blue: 1, alpha: 1.0)
  case .lightBlue:
    return UIColor(red: 51/255.0, green: 204/255.0, blue: 1, alpha: 1.0)
  case .darkGreen:
    return UIColor(red: 102/255.0, green: 204/255.0, blue: 0, alpha: 1.0)
  case .lightGreen:
    return UIColor(red: 102/255.0, green: 1, blue: 0, alpha: 1.0)
  case .brown:
    return UIColor(red: 160/255.0, green: 82/255.0, blue: 45/255.0, alpha: 1.0)
  case .orange:
    return UIColor(red: 1, green: 102/255.0, blue: 0, alpha: 1.0)
  case .yellow:
    return UIColor(red: 1, green: 1, blue: 0, alpha: 1.0)
  case .eraser:
    return .white
  }
}

这将为每个选定的铅笔返回适当的UIColor

接下来,打开ViewController.swift,将以下内容添加到pencilPressed(_ :)

// 1
guard let pencil = Pencil(tag: sender.tag) else {
  return
}

// 2
color = pencil.color

// 3
if pencil == .eraser {
  opacity = 1.0
}

一步一步看看:

  • 1) 首先,您需要知道用户选择的颜色索引。 有很多地方可能会出错,比如使用不正确的tag或未设置的tag,这样就可以防止没有匹配的Pencil
  • 2) 接下来,将绘图颜色设置为用户选择的颜色。
  • 3) 最后一种颜色是橡皮擦,所以它有点特别。 橡皮擦按钮将颜色设置为白色,不透明度设置为1.0。 由于您的背景颜色也是白色,这将为您提供非常方便的橡皮擦效果!

Build并运行,并准备让颜色飞起来。 现在,点击颜色按钮可更改画笔笔划以使用该按钮的颜色。


Resetting to Tabula Rasa - Reset操作处理

下面开始处理在应用中设置的“ Reset”按钮。

ViewController.swift中,将以下内容添加到resetPressed(_ :)

mainImageView.image = nil

就是这样,上面的所有代码都将mainImageView的图像设置为nil,瞧! 你的画布被清除了! 请记住,您在图像视图的图像上下文中绘制了线条,因此将其清除为nil将重置所有内容。

再次构建并运行代码。 绘制一些东西,然后点击“ Reset”按钮清除绘图。


Adding Finishing Touches — Settings - 添加完成触摸 - 设置

您现在有一个功能性绘图应用程序,但仍然有第二个设置屏幕要处理。

打开Main.storyboard并单击Settings View Controller场景。 “ Settings”场景有五个UISlider组件,用于设置画笔宽度,画笔不透明度和画笔RGB颜色值。 您还可以看到UIImageView,它显示从所有选定值生成的预览。

现在,打开SettingsViewController.swift并将以下属性添加到类中:

var brush: CGFloat = 10.0
var opacity: CGFloat = 1.0
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0

这将让您跟踪用户选择的画笔大小,不透明度和颜色。

接下来,添加以下方法:

func drawPreview() {
  UIGraphicsBeginImageContext(previewImageView.frame.size)
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
    
  context.setLineCap(.round)
  context.setLineWidth(brush)
  context.setStrokeColor(UIColor(red: red, 
                                 green: green,
                                 blue: blue, 
                                 alpha: opacity).cgColor)
  context.move(to: CGPoint(x: 45, y: 45))
  context.addLine(to: CGPoint(x: 45, y: 45))
  context.strokePath()
  previewImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
}

此方法使用相同的技术绘制ViewController中使用的drawLine(from:to :)设置的预览。 但是,在这种情况下,该方法从滑块值绘制单个点而不是具有适当线宽和不透明度的线。

接下来,将以下内容添加到brushChanged(_ :)

brush = CGFloat(sender.value)
labelBrush.text = String(format: "%.1f", brush)
drawPreview()

并将以下内容添加到opacityChanged(_ :)

opacity = CGFloat(sender.value)
labelOpacity.text = String(format: "%.1f", opacity)
drawPreview()

在上面的代码中,当用户更改滑块值时,您需要相应地设置相应的值,然后通过调用drawPreview()更新预览图像。

Build并运行,打开“设置”屏幕,然后使用滑块。 您会看到现在预览图像和值标签在移动时会发生变化! 颜色滑块尚未使用,但您很快就会完善它们。


Integrating Settings - 集成设置

这里还有一件重要的没有做。 你有没有注意到它是什么?

更新的不透明度和宽度值仍未应用于ViewController绘图画布! 那是因为您尚未将“设置”屏幕中指定的值传递给ViewController。 这是委托协议的完美工作。

打开SettingsViewController.swift并在导入下方添加以下代码:

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

这定义了一个具有一个必需方法的类协议。 设置屏幕将使用此信息与任何对更新设置感兴趣的一方进行通信。

还要向SettingsViewController添加一个属性:

weak var delegate: SettingsViewControllerDelegate?

这将保留对代表的引用。 如果有代理,您需要在用户点击“Close”按钮时通知它。 将以下内容添加到closePressed(_ :)

delegate?.settingsViewControllerFinished(self)

这将调用代理方法,以便它可以使用新值更新代理。

现在,打开ViewController.swift并将协议的新类扩展添加到文件的底部:

// MARK: - SettingsViewControllerDelegate

extension ViewController: SettingsViewControllerDelegate {
  func settingsViewControllerFinished(_ settingsViewController: SettingsViewController) {
    brushWidth = settingsViewController.brush
    opacity = settingsViewController.opacity
    dismiss(animated: true)
  }
}

这声明该类符合SettingsViewControllerDelegate并实现其单个方法。 在实现中,它需要做的就是将当前画笔宽度和不透明度设置为设置视图的滑块控件中的值。

当用户从“绘图”移动到“设置”屏幕时,您将希望滑块显示当前所选的画笔大小和不透明度值。 这意味着当您打开“设置”时,您需要传递它们。

将以下方法覆盖添加到ViewController类:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  guard 
    let navController = segue.destination as? UINavigationController,
    let settingsController = navController.topViewController as? SettingsViewController 
  else {
      return
  }
  settingsController.delegate = self
  settingsController.brush = brushWidth
  settingsController.opacity = opacity
}

当用户通过点击“设置”按钮触发segue时,此方法通过将自身设置为委托并传递当前画笔和不透明度值来配置新的SettingsViewController

Build并运行。 在此阶段,您将看到在设置屏幕中更改画笔和不透明度值后,它们现在已更新。 现在,您可以使用不同画笔大小和不透明度级别的多种颜色进行绘制!


Adding Finishing Touches — A Custom Color Selector - 添加完成触摸- 自定义颜色选择器

目前,“绘图画布”屏幕上有10个颜色按钮。 但是,使用自定义RGB颜色选择器,使用您的应用程序的识别艺术家将能够从RGB范围中选择任何可用的颜色。

设置屏幕中有一组RGB颜色滑块,您将在下一步实施。

由于您已经提供了画笔大小和不透明度的预览,因此您可以集成新画笔颜色的预览!

SettingsViewController.swift中,将以下内容添加到colorChanged(_ :)

red = CGFloat(sliderRed.value / 255.0)
labelRed.text = Int(sliderRed.value).description
green = CGFloat(sliderGreen.value / 255.0)
labelGreen.text = Int(sliderGreen.value).description
blue = CGFloat(sliderBlue.value / 255.0)
labelBlue.text = Int(sliderBlue.value).description
    
drawPreview()

当您移动任何RGB滑块时,iOS会调用colorChanged(_ :)。 在上面的代码中,请注意您所做的只是更新属性值和更新标签。

现在您已经使用所有正确设置绘制了画笔和不透明度样本,您将需要在出现“设置”屏幕时正确显示它们。 将以下内容添加到viewDidLoad()的底部:

sliderBrush.value = Float(brush)
labelBrush.text = String(format: "%.1f", brush)
sliderOpacity.value = Float(opacity)
labelOpacity.text = String(format: "%.1f", opacity)
sliderRed.value = Float(red * 255.0)
labelRed.text = Int(sliderRed.value).description
sliderGreen.value = Float(green * 255.0)
labelGreen.text = Int(sliderGreen.value).description
sliderBlue.value = Float(blue * 255.0)
labelBlue.text = Int(sliderBlue.value).description
    
drawPreview()

如您所见,这只是使用正确的值预设所有标签和滑块。 对drawPreview()的调用可确保预览图像视图也显示正确的内容。

最后,打开ViewController.swift。 和以前一样,您需要确保当前颜色与设置屏幕相对应,因此在准备结束时添加以下行(for:sender :)

var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
color.getRed(&red, green: &green, blue: &blue, alpha: nil)
settingsController.red = red
settingsController.green = green
settingsController.blue = blue

这会传递当前的红色,绿色和蓝色值,以便正确设置RGB滑块。 这种语法可能看起来很奇怪,但不要担心 - 它是旧Objective-C时代的遗留功能。 当你调用getRed(_:green:blue:alpha :)时,它会使用color中的组件值设置传入的变量。

最后,在类扩展中找到settingsViewControllerFinished(_ :)并在调用dismiss(animated:true)之前添加以下行:

color = UIColor(red: settingsViewController.red,
                green: settingsViewController.green,
                blue: settingsViewController.blue,
                alpha: opacity)

这会使用新的RGB值更新颜色。

再次Build并运行并将颜色滑块放入其中。 另请参阅预览中显示的指定RGB颜色现在是绘图画布上的画笔描边颜色!

这就基本完成了,下面我们一起看一下分享,好让你的作品分享给别人~


Adding Sharing - 添加分享

在最后一步中,您将使用iOS共享表将您的艺术作品发送到世界各地!

这有两个部分:首先,你需要将绘图作为UIImage对象;然后,您只需将其传递给UIActivityViewController,以确定哪些共享选项最有效,具体取决于可用的帐户和服务。

ViewController.swift中,将以下内容添加到sharePressed(_ :)

guard let image = mainImageView.image else {
  return
}
let activity = UIActivityViewController(activityItems: [image], 
                                        applicationActivities: nil)
present(activity, animated: true)

这种方法非常简单。 首先,它验证mainImageView确实有一个图像。 然后,UIActivityViewController做了大部分繁重的工作! 你需要做的就是传递一系列东西来分享,在这种情况下,这只是单个图像。

初始化程序applicationActivities的第二个参数允许您限制活动,因此传递nil意味着iOS将提供尽可能多的共享选项。 你的绘画应该不会少!

Build并运行应用程序,并创建您的杰作。 当您点按Share时,您现在可以让全世界了解您的才能。


源码

1. ViewController.swift
import UIKit

class ViewController: UIViewController {
  
  @IBOutlet weak var mainImageView: UIImageView!
  @IBOutlet weak var tempImageView: UIImageView!
  
  var lastPoint = CGPoint.zero
  var color = UIColor.black
  var brushWidth: CGFloat = 10.0
  var opacity: CGFloat = 1.0
  var swiped = false
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard let navController = segue.destination as? UINavigationController,
      let settingsController = navController.topViewController as? SettingsViewController else {
        return
    }
    settingsController.delegate = self
    settingsController.brush = brushWidth
    settingsController.opacity = opacity
    
    var red: CGFloat = 0
    var green: CGFloat = 0
    var blue: CGFloat = 0
    color.getRed(&red, green: &green, blue: &blue, alpha: nil)
    settingsController.red = red
    settingsController.green = green
    settingsController.blue = blue
  }
  
  // MARK: - Actions
  
  @IBAction func resetPressed(_ sender: Any) {
    mainImageView.image = nil
  }
  
  @IBAction func sharePressed(_ sender: Any) {
    guard let image = mainImageView.image else {
      return
    }
    let activity = UIActivityViewController(activityItems: [image], applicationActivities: nil)
    present(activity, animated: true)
  }
  
  @IBAction func pencilPressed(_ sender: UIButton) {
    guard let pencil = Pencil(tag: sender.tag) else {
      return
    }
    color = pencil.color
    if pencil == .eraser {
      opacity = 1.0
    }
  }
  
  func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
    UIGraphicsBeginImageContext(view.frame.size)
    guard let context = UIGraphicsGetCurrentContext() else {
      return
    }
    tempImageView.image?.draw(in: view.bounds)
    
    context.move(to: fromPoint)
    context.addLine(to: toPoint)
    
    context.setLineCap(.round)
    context.setBlendMode(.normal)
    context.setLineWidth(brushWidth)
    context.setStrokeColor(color.cgColor)
    
    context.strokePath()
    
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    tempImageView.alpha = opacity
    
    UIGraphicsEndImageContext()
  }
  
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
      return
    }
    swiped = false
    lastPoint = touch.location(in: view)
  }
  
  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
      return
    }
    swiped = true
    let currentPoint = touch.location(in: view)
    drawLine(from: lastPoint, to: currentPoint)
    
    lastPoint = currentPoint
  }
  
  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if !swiped {
      // draw a single point
      drawLine(from: lastPoint, to: lastPoint)
    }
    
    // Merge tempImageView into mainImageView
    UIGraphicsBeginImageContext(mainImageView.frame.size)
    mainImageView.image?.draw(in: view.bounds, blendMode: .normal, alpha: 1.0)
    tempImageView?.image?.draw(in: view.bounds, blendMode: .normal, alpha: opacity)
    mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    tempImageView.image = nil
  }
}

// MARK: - SettingsViewControllerDelegate

extension ViewController: SettingsViewControllerDelegate {
  func settingsViewControllerFinished(_ settingsViewController: SettingsViewController) {
    brushWidth = settingsViewController.brush
    opacity = settingsViewController.opacity
    color = UIColor(red: settingsViewController.red,
                    green: settingsViewController.green,
                    blue: settingsViewController.blue,
                    alpha: opacity)
    dismiss(animated: true)
  }
}
2. SettingsViewController.swift
import UIKit

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

class SettingsViewController: UIViewController {
  
  @IBOutlet weak var sliderBrush: UISlider!
  @IBOutlet weak var sliderOpacity: UISlider!
  @IBOutlet weak var previewImageView: UIImageView!
  
  @IBOutlet weak var labelBrush: UILabel!
  @IBOutlet weak var labelOpacity: UILabel!
  
  @IBOutlet weak var sliderRed: UISlider!
  @IBOutlet weak var sliderGreen: UISlider!
  @IBOutlet weak var sliderBlue: UISlider!
  
  @IBOutlet weak var labelRed: UILabel!
  @IBOutlet weak var labelGreen: UILabel!
  @IBOutlet weak var labelBlue: UILabel!
  
  var brush: CGFloat = 10.0
  var opacity: CGFloat = 1.0
  var red: CGFloat = 0.0
  var green: CGFloat = 0.0
  var blue: CGFloat = 0.0
  
  weak var delegate: SettingsViewControllerDelegate?
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    sliderBrush.value = Float(brush)
    labelBrush.text = String(format: "%.1f", brush)
    sliderOpacity.value = Float(opacity)
    labelOpacity.text = String(format: "%.1f", opacity)
    sliderRed.value = Float(red * 255.0)
    labelRed.text = Int(sliderRed.value).description
    sliderGreen.value = Float(green * 255.0)
    labelGreen.text = Int(sliderGreen.value).description
    sliderBlue.value = Float(blue * 255.0)
    labelBlue.text = Int(sliderBlue.value).description
    
    drawPreview()
  }
  
  // MARK: - Actions
  
  @IBAction func closePressed(_ sender: Any) {
    delegate?.settingsViewControllerFinished(self)
  }
  
  @IBAction func brushChanged(_ sender: UISlider) {
    brush = CGFloat(sender.value)
    labelBrush.text = String(format: "%.1f", brush)
    drawPreview()
  }
  
  @IBAction func opacityChanged(_ sender: UISlider) {
    opacity = CGFloat(sender.value)
    labelOpacity.text = String(format: "%.1f", opacity)
    drawPreview()
  }
  
  @IBAction func colorChanged(_ sender: UISlider) {
    red = CGFloat(sliderRed.value / 255.0)
    labelRed.text = Int(sliderRed.value).description
    green = CGFloat(sliderGreen.value / 255.0)
    labelGreen.text = Int(sliderGreen.value).description
    blue = CGFloat(sliderBlue.value / 255.0)
    labelBlue.text = Int(sliderBlue.value).description
    
    drawPreview()
  }
  
  func drawPreview() {
    UIGraphicsBeginImageContext(previewImageView.frame.size)
    guard let context = UIGraphicsGetCurrentContext() else {
      return
    }
    
    context.setLineCap(.round)
    context.setLineWidth(brush)
    context.setStrokeColor(UIColor(red: red, green: green, blue: blue, alpha: opacity).cgColor)
    context.move(to: CGPoint(x: 45, y: 45))
    context.addLine(to: CGPoint(x: 45, y: 45))
    context.strokePath()
    previewImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
  }
}
3. Pencil.swift
import Foundation
import UIKit

enum Pencil {
  case black
  case grey
  case red
  case darkblue
  case lightBlue
  case darkGreen
  case lightGreen
  case brown
  case orange
  case yellow
  case eraser
  
  init?(tag: Int) {
    switch tag {
    case 1:
      self = .black
    case 2:
      self = .grey
    case 3:
      self = .red
    case 4:
      self = .darkblue
    case 5:
      self = .lightBlue
    case 6:
      self = .darkGreen
    case 7:
      self = .lightGreen
    case 8:
      self = .brown
    case 9:
      self = .orange
    case 10:
      self = .yellow
    case 11:
      self = .eraser
    default:
      return nil
    }
  }
  
  var color: UIColor {
    switch self {
    case .black:
      return .black
    case .grey:
      return UIColor(white: 105/255.0, alpha: 1.0)
    case .red:
      return UIColor(red: 1, green: 0, blue: 0, alpha: 1.0)
    case .darkblue:
      return UIColor(red: 0, green: 0, blue: 1, alpha: 1.0)
    case .lightBlue:
      return UIColor(red: 51/255.0, green: 204/255.0, blue: 1, alpha: 1.0)
    case .darkGreen:
      return UIColor(red: 102/255.0, green: 204/255.0, blue: 0, alpha: 1.0)
    case .lightGreen:
      return UIColor(red: 102/255.0, green: 1, blue: 0, alpha: 1.0)
    case .brown:
      return UIColor(red: 160/255.0, green: 82/255.0, blue: 45/255.0, alpha: 1.0)
    case .orange:
      return UIColor(red: 1, green: 102/255.0, blue: 0, alpha: 1.0)
    case .yellow:
      return UIColor(red: 1, green: 1, blue: 0, alpha: 1.0)
    case .eraser:
      return .white
    }
  }
}

后记

本篇主要讲述了一个有关绘图应用示例,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容