1. 程式人生 > >Swift-貝賽爾曲線實現畫圖板 && 截圖儲存到相簿中

Swift-貝賽爾曲線實現畫圖板 && 截圖儲存到相簿中

本文內容參考自 傳送門原文是用 OC 寫的,我把它改成了 Swift 的。

我們先來看看效果圖:


第一幅圖是我們畫了一個 “iOS” 的影象,第二幅圖是我們點選儲存成功,第三幅圖是可以在相簿中看到我們剛才畫的圖。

感覺很不錯有木有?接下來我們就來說說是怎麼實現的。

我們分兩部分來說:上半部分的畫圖板和下半部分的控制區。

上半部分的畫圖板是我們自定義的 view,我們設定如下屬性:

class MyView: UIView {
    var color = UIColor.redColor() // 線條顏色
    var lineWidth : Float = 1.0 // 線條寬度
    private var allLine: [Dictionary<String, AnyObject>] = [] // 儲存已有的線條
    private var cancelLine: [Dictionary<String, AnyObject>] = [] // 儲存被撤銷的線條
    private var bezier = UIBezierPath() // 貝賽爾曲線
}
其中線條的顏色和寬度在 controller 中要用到,其餘的三個不需要所以我們設定成私有的 private。

先說說後退功能,它其實非常簡單。

allLine 和 cancelLine 這兩個陣列就相當於兩個棧。後退時,讓已有的一條線出棧,進入到撤銷線條的棧中。

具體的程式碼其實只有短短數行:

func backImage() { // 兩個陣列相當於兩個棧。後退時,讓已有的一條線出棧,進入到撤銷線條的棧中。
    if allLine.isEmpty == false { // 如果陣列不為空才執行
        cancelLine.append(allLine.last!) // 入棧
        allLine.removeLast() // 出棧
        setNeedsDisplay() // 重繪介面
    }
}

前進功能正好和後退相反。前進時,讓被撤銷的一條線出棧,進入到已有線條的棧中。
func forwardImage() { // 前進時正好與後退相反,讓被撤銷的一條線條出棧,進入到已有線條的棧中。
    if cancelLine.isEmpty == false { // 如果陣列不為空才執行
        allLine.append(cancelLine.last!) // 入棧
        cancelLine.removeLast() // 出棧
        setNeedsDisplay() // 重繪介面
    }
}

接下來說說貝賽爾曲線。對於本例,簡單來說就是:

1。在每次開始觸控時新建一個貝賽爾曲線並存入陣列中,記錄下觸控的座標作為貝賽爾曲線的當前座標。

2。在滑動時記錄每一瞬時的座標,更新貝賽爾曲線的當前座標為這個新座標。

3。重新繪製介面。

4。執行第二步。直到這次觸控結束為止。

開始觸控時的程式碼如下:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    bezier = UIBezierPath() // 新建貝塞爾曲線
    let point = touches.first!.locationInView(self) // 獲取觸控的點
    bezier.moveToPoint(point) // 把剛觸控的點設定為bezier的起點
    var tmpDic = Dictionary<String, AnyObject>()
    tmpDic["color"] = color
    tmpDic["lineWidth"] = lineWidth
    tmpDic["line"] = bezier
    allLine.append(tmpDic) // 把線存入陣列中
}

觸控滑動時的程式碼如下(沒錯,就三行,哈哈):
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let point = touches.first!.locationInView(self) // 獲取觸控的點
    bezier.addLineToPoint(point) // 把移動的座標存到貝賽爾曲線中
    setNeedsDisplay() // 重繪介面
}

重繪介面的程式碼如下(注意:不要直接呼叫 drawRect: 方法,如果想重繪的話,呼叫 setNeedsDisplay() 方法):
override func drawRect(rect: CGRect) {
    for i in 0..<allLine.count {
        let tmpDic = allLine[i]
        let tmpColor = tmpDic["color"] as! UIColor
        let tmpWidth = tmpDic["lineWidth"] as! CGFloat
        let tmpPath = tmpDic["line"] as! UIBezierPath
        tmpColor.setStroke()
        tmpPath.lineWidth = tmpWidth
        tmpPath.stroke()
    }
}

以上幾乎就是畫圖板的全部內容,並不多,而且不難理解。

下面我們來說說如何使用它。在 controller 中建立一個 MyView 的例項,MyView 就是上面所說的畫圖板。

class ViewController: UIViewController {
    private let drawingBoard = MyView() // 自定義view,也就是畫圖板部分
    private let mySlider = UISlider() // 滑動條,控制線條寬度
    private let mySegment = UISegmentedControl(items: ["紅", "黑", "綠"]) // 分段控制器,控制線條顏色
    private let backBtn = UIButton(type: .Custom)
    private let saveBtn = UIButton(type: .Custom)
    private let forwardBtn = UIButton(type: .Custom)
}

這些 UI 控制元件的屬性設定沒啥好說的,無非就是設定 frame、text、title、titleColor、backgroundColor 之類的,不贅述。

我們來說說這些控制元件的響應事件即可,其實也很簡單。

滑動條的響應事件:

func onClickSlider(slider: UISlider) {
    drawingBoard.lineWidth = slider.value
}

分段控制器的響應事件:
func onClickSegment(segment: UISegmentedControl) {
    switch segment.selectedSegmentIndex {
    case 0:
        drawingBoard.color = UIColor.redColor()
    case 1:
        drawingBoard.color = UIColor.blackColor()
    case 2:
        drawingBoard.color = UIColor.greenColor()
    default:
        drawingBoard.color = UIColor.redColor()
    }
}

後退和前進按鈕的響應事件:
func onClickBack(button: UIButton) {
    drawingBoard.backImage()
}

func onClickForward(button: UIButton) {
    drawingBoard.forwardImage()
}

最後來說儲存到相簿的功能。
func onClickSave(button: UIButton) {
    UIGraphicsBeginImageContext(drawingBoard.bounds.size) // 開始擷取畫圖板
    view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
    let img : UIImage = UIGraphicsGetImageFromCurrentImageContext() // 擷取到的影象
    UIGraphicsEndImageContext() // 結束擷取
    UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil) // 把擷取到的影象儲存到相簿中
    // 最後提示使用者儲存成功即可
    let alert = UIAlertView.init(title: "儲存照片成功",
                                 message: "您已將照片儲存於圖片庫中,開啟照片程式即可檢視。",
                                 delegate: self,
                                 cancelButtonTitle: "OK")
    alert.show()
}

也許有的同學不知道怎麼新增響應事件?那我們就拿後退按鈕的響應事件舉個栗子
backBtn.addTarget(self, action: Selector("onClickBack:"), forControlEvents: .TouchUpInside)


相關推薦

Swift曲線實現圖板 && 儲存相簿

本文內容參考自 傳送門。原文是用 OC 寫的,我把它改成了 Swift 的。 我們先來看看效果圖: 第一幅圖是我們畫了一個 “iOS” 的影象,第二幅圖是我們點選儲存成功,第三幅圖是可以在相簿中看到

Swift曲線扇形、弧線、圓形、多邊形——UIBezierPath實現App下載時的動畫效果

上篇文章提到了使用貝賽爾曲線實現畫圖板(傳送門),頓時就對貝賽爾曲線興趣大增有木有。 之所以接觸貝賽爾曲線,多虧了師父。週五下班前師父給我留了個任務,讓我週末回家研究研究 iPhone 手機下載 Ap

自定義View,曲線實現水波紋進度條

最終的效果:  思路就是在onDraw()中畫一些內容,主要方法有這些: /** * 剪裁圓形區域 */ clipCircle(canvas); /** * 畫圓邊線 */ drawCircle(canvas); /** * 畫波浪線 */ drawWave(

react-native-art二次曲線實現

1、關於react-native ART庫的使用,目前網上能搜到的少之又少,簡書上的一篇react-native-art 繪圖入門,從基本上講解了一下react-native-art的使用方法,但是隻是簡單的橫豎曲線的繪製,但專案中有一個需求就是繪製網速的速率曲線, (專案

曲線實現的購物車添加商品動畫效果

right map 繪制 開始 enter 監聽 idg 過程 protected 效果圖如下: 1.activity_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xm

unity 實現物體沿指定的平滑曲線移動(通過曲線實現

在實際專案開發中,為了實現某種動畫或者特效,策劃都會要求讓物體實現沿編輯的軌跡進行移動,今天這裡就講一下如何讓物體沿可編輯的路線進行移動,這裡主要是通過貝塞爾曲線實現。 首先要了解貝塞爾曲線的基礎知識及原理,具體可參考改連結: 這裡的思路就是首先就是把關鍵節點儲存起來

自定義view,曲線實現水波紋效果的動畫

作為一名碼農,除了用基本的姿勢去搬磚,還應該get一些炫酷的技能,用高逼格的姿態去搬磚。而貝塞爾曲線無疑是炫酷技能之一。 簡介: Bézier curve(貝塞爾曲線)是應用於二維圖形應用程式的數學曲線。 曲線定義:起始點、終止點(也稱錨點)、控制點。通過調整控

Android曲線實現水波紋的效果

前兩天朋友找我實現一個水波紋的效果,因為這塊一直沒做過,所以花了一上午時間研究一下,參考了網上的一些方法,得知Android還有Path.quadTo()這麼一個方法。 話不多說,程式碼如下: public class MyView extends View implem

Path使用--二階曲線實現水波效果

上面這個效果是使用Path繪製二階貝塞爾曲線實現的;二階貝塞爾曲線涉及到三個點,起始點、拐點、終點,而拐點有決定著曲線的形狀;下面這張圖大致展示了二階貝塞爾曲線: A點是起始點,C點是終點,B點是拐點,當然根據繪製的需求,B是變動的,繪製出來的曲線也就

一個精美的跳動小球—手把手教你用曲線實現一個酷炫跳動動畫。

前言介紹 手把手教你用貝塞爾曲線實現一個精美的跳動的小球。 正文 效果展示: 說點題外話 一開始呢,我就想實現一個這樣的效果,於是就新建了一個專案開始擼,結果中間嘗試了幾種實現方案都不是很理想,也有一些條件未能實現就停止開發了,後面又去惡補了一下相關知識,突然在某一天的某一刻的某一瞬間靈感來了

自定義控制元件三部曲之繪圖篇(六)——Path之曲線和手勢軌跡、水波紋效果

前言:好想義無反顧地追逐夢想從這篇開始,我將延續androidGraphics系列文章把圖片相關的知識給大家講完,這一篇先稍微進階一下,給大家把《android Graphics(二):路徑及文字》略去的quadTo(二階貝塞爾)函式,給大家補充一下。 本篇最終將以兩個例子給

Android自定義View——曲線實現水波紋進度球

效果圖 原理分析 首先需要了解的水波紋實現效果,可以在部落格的自定義View專題找到,其實現原理如下 利用貝塞爾曲線繪製螢幕外和螢幕內的sin曲線 利用path將sin曲線的左下角和右下角連線起來成為一塊區域 通過不斷的平移sin曲線,然後平移完

Android自定義控制元件-Path之曲線和手勢軌跡、水波紋效果

從這篇開始,我將延續androidGraphics系列文章把圖片相關的知識給大家講完,這一篇先稍微進階一下,給大家把《android Graphics(二):路徑及文字》略去的quadTo(二階貝塞爾)函式,給大家補充一下。 本篇最終將以兩個例子給大家演示貝塞爾曲線

iOS開發-------塗鴉板(UIBezierPath 曲線)與 MVC初嘗試

         塗鴉板,顧名思義就是能夠在上面畫點東西,貝賽爾曲線(UIBezierPath),也可以叫做貝賽爾路徑。因為path的直譯就是路徑,看起來很高大上,之前樓主也確實這麼認為的,很高大上,細細瞭解,其實也不難,畢竟難的東西蘋果都給我們封裝好了。初次用MVC模式來

cocos2d-x學習筆記(9)BezierTo和BezierBy曲線運動

Bezier貝塞爾曲線,任何一條曲線都可以通過與它相切的控制線兩端的點的位置來定義。因此,貝塞爾曲線可以用4個點描述,其中兩個點描述兩個端點,另外兩個點描述每一端的切線。 Sprite* sprite=Sprite::create("sprite.png"); spri

css_transition_animation(內含曲線詳解)

區別: transition也叫過渡動畫,主要是用於讓一個元素從一種狀態過渡到另一種狀態效果,常用於主動觸發的效果。例如移動端的頁面切換(很常用)、button點選效果(也很常見)。 animation才是css3正宗的動畫,主要是用於實現某種持續的動畫效果(當然簡單的過渡動畫也可以實現),常用於自動觸發的

三次曲線關於點與長度在C++實現

三階貝塞爾曲線只能計算近似解,由於使用時對長度的精度要求不高,因此用部落格 【Unity】貝塞爾曲線關於點、長度、切線計算在 Unity中的C#實現 中提供的C#方法改寫為C++的,只是替換了一個結構體,因為並不懂原文中的Vector3類的使用而已。 定義一個POINT結構體,用

【Unity3d遊戲開發】遊戲曲線以及其在Unity實現

轉載收藏:原文連結https://www.cnblogs.com/msxh/p/6270468.html 閱讀目錄 一、簡介 二、公式 三、實現與應用   RT,馬三最近在參與一款足球遊戲的開發,其中涉及到足球的各種運動軌跡和路徑,比如射門的軌跡,高吊球

OpenGL實現攝像機漫遊/三次曲線

通過openglAPI實現攝像機漫遊,以及觀察生成的貝塞爾曲面 #include "stdafx.h" #include <GL/glut.h> #include <stdlib.h> #include<iostream> using namesp

曲線 WPF MVVM N階實現 公式詳解+原始碼下載

原始碼下載 效果圖:     本程式主要實現: N階貝塞爾曲線(通用公式) 本程式主要使用技術 MVVM InterAction 事件繫結 動態新增Canvas的Item   第一部分公式: