国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Swift3.0學習實踐-一個簡單的畫板(七色軌跡、可撤銷、可清除、帶橡皮擦)

2019-11-09 15:51:00
字體:
來源:轉載
供稿:網友

寫著玩兒的小程序,繼續學習swift.運行效果+代碼+知識點總結

運行效果:

           

代碼:

Canvas類:畫布,畫圖板狀態管理、交互、處理手勢
class Canvas:UIView{    //負責線條的生成、操作與管理    let pathCreator:PathCreator    //是否處于擦除狀態    var isInErasering:Bool    //橡皮擦視圖    let eraserView:UIView        override init(frame: CGRect) {        isInErasering = false        pathCreator = PathCreator()                eraserView = UIView.init()        eraserView.frame = CGRect(x: 0, y: 0, width: 10, height: 10)        eraserView.backgroundColor = UIColor.white        eraserView.alpha = 0        super.init(frame: frame)                self.backgroundColor = UIColor.black                self.addSubview(eraserView)                let revokeBut = UIButton(type: UIButtonType.system)        revokeBut.frame = CGRect(x: 20, y: 20, width: 80, height: 30)        revokeBut.setTitle("撤銷", for: UIControlState.normal)        revokeBut.addTarget(self, action: #selector(revokeButClick), for: UIControlEvents.touchUpInside)        self.addSubview(revokeBut)                let cleanBut = UIButton(type: UIButtonType.system)        cleanBut.frame = CGRect(x: 110, y: 20, width: 80, height: 30)        cleanBut.setTitle("清空", for: UIControlState.normal)        cleanBut.addTarget(self, action: #selector(cleanButClick), for: UIControlEvents.touchUpInside)        self.addSubview(cleanBut)            let eraserBut = UIButton(type: UIButtonType.system)        eraserBut.frame = CGRect(x: 200, y: 20, width:80, height: 30)        eraserBut.setTitle("橡皮", for: UIControlState.normal)        eraserBut.setTitle("畫筆", for: UIControlState.selected)        eraserBut.addTarget(self, action: #selector(eraserButClick(but:)), for: UIControlEvents.touchUpInside)        self.addSubview(eraserBut)                let ges = UipanGestureRecognizer(target: self, action:#selector(handleGes(ges:)))        ges.maximumNumberOfTouches = 1        self.addGestureRecognizer(ges)    }        required public init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }        override public func layoutSubviews() {            }        @objc PRivate func handleGes(ges:UIPanGestureRecognizer) -> Void {        let point = ges.location(in: self)        switch ges.state {        case UIGestureRecognizerState.began:            if isInErasering {                //擦除狀態,顯示出橡皮擦                eraserView.alpha = 1                eraserView.center = point            }            //生成新的一筆            pathCreator.addNewPath(to: point,isEraser: isInErasering)            self.setNeedsDisplay()        case UIGestureRecognizerState.changed:            if isInErasering {                //移動橡皮擦                eraserView.center = ges.location(in: self)            }            //更新當前筆畫路徑            pathCreator.addLineForCurrentPath(to: point,isEraser:isInErasering)            self.setNeedsDisplay()        case UIGestureRecognizerState.ended:            if isInErasering {                //擦除狀態,隱藏橡皮擦                eraserView.alpha = 0                eraserView.center = ges.location(in: self)            }            //更新當前筆畫路徑            pathCreator.addLineForCurrentPath(to: point,isEraser: isInErasering)            self.setNeedsDisplay()        case UIGestureRecognizerState.cancelled:            print("cancel")        case UIGestureRecognizerState.failed:            print("fail")        default:            return        }    }        override public func draw(_ rect: CGRect) {        //畫線        pathCreator.drawPaths()    }        @objc private func revokeButClick()->Void{        //撤銷操作        pathCreator.revoke()        self.setNeedsDisplay()    }        @objc private func cleanButClick()->Void{        //清空操作        pathCreator.clean()        self.setNeedsDisplay()    }        @objc private func eraserButClick(but:UIButton)->Void{        //切換畫圖與擦除狀態        if but.isSelected {            but.isSelected = false            isInErasering = false        }else{            but.isSelected = true            isInErasering = true        }    }}

PathCreator:具體線條繪制、管理

//每條子線段信息struct BezierInfo{    let path:UIBezierPath//具體線段    let color:UIColor//線段對應顏色    init(path:UIBezierPath,color:UIColor){        self.path = path        self.color = color    }}class PathCreator{    //所有筆畫    private var paths:[NSMutableArray]?    //筆畫內當前子線段    private var currentBezierPathInfo:BezierInfo?    //當前筆畫的所有子線段    private var currentPath:NSMutableArray?    //當前筆畫已經采集處理了幾個觸摸點    private var pointCountInOnePath = 0        static let colors = [UIColor.red,UIColor.orange,UIColor.yellow,UIColor.green,UIColor.blue,UIColor.gray,UIColor.purple]    init() {        paths = []    }    //添加新筆畫    func addNewPath(to:CGPoint,isEraser:Bool)->Void{        //創建起始線段        let path = UIBezierPath()        path.lineWidth = 5        path.move(to: to)        path.lineJoinStyle = CGLineJoin.round        path.lineCapStyle = CGLineCap.round        if !isEraser {            //綁定線段與顏色信息            currentBezierPathInfo = BezierInfo(path: path, color: PathCreator.colors[0])        }else{            //處于擦除模式,顏色與畫板背景色相同            currentBezierPathInfo = BezierInfo(path: path, color: UIColor.black)        }        //新建一個筆畫        currentPath = NSMutableArray.init()        //將起始線段加入當前筆畫        currentPath!.add(currentBezierPathInfo)        pointCountInOnePath = 0        //將當前筆畫加入筆畫數組        paths!.append(currentPath!)    }    //添加新的點,更新當前筆畫路徑    func addLineForCurrentPath(to:CGPoint,isEraser:Bool) -> Void {        pointCountInOnePath += 1//同一筆畫內,每7個點換一次顏色        if pointCountInOnePath % 7 == 0{//換顏色            if let currentBezierPathInfo = currentBezierPathInfo{                //將當前點加入當前子線段,更新當前子線段路徑                currentBezierPathInfo.path.addLine(to: to)            }            //生成新的子線段            let path = UIBezierPath()            path.lineWidth = 5            path.move(to: to)            path.lineJoinStyle = CGLineJoin.round            path.lineCapStyle = CGLineCap.round            if !isEraser{                //給當前子線段設置下一個顏色                currentBezierPathInfo = BezierInfo(path: path, color: PathCreator.colors[currentPath!.count % 7])            }else{                //處于擦除模式,顏色與畫板背景色相同                currentBezierPathInfo = BezierInfo(path: path, color: UIColor.black)            }            //將當前子線段加入當前筆畫            currentPath!.add(currentBezierPathInfo)        }else{            if let currentBezierPathInfo = currentBezierPathInfo{                //將當前點加入當前子線段,更新當前子線段路徑                currentBezierPathInfo.path.addLine(to: to)            }        }    }        func drawPaths()->Void{        //畫線        let pathCount = paths!.count        for i in 0..<pathCount{            //取出所有筆畫            let onePath = paths![i]            let onePathCount = onePath.count            for j in 0..<onePathCount{                //繪制每條筆畫內每個子線段                let pathInfo = onePath.object(at: j) as! BezierInfo                pathInfo.color.set()                pathInfo.path.stroke()            }        }    }        func revoke()->Void{        //移走上一筆畫        if paths!.count > 0 {            paths!.removeLast()        }    }        func clean()->Void{        //移走所有筆畫        paths!.removeAll()    }}

知識點總結:

1.結構體是值傳遞

一個基礎概念,但開始使用時還是給忘了。數組[]在swift中是結構體(struct)實現,值傳遞。最開始把currentPath聲明為了[],添加到paths[]中后,后續再去往currentPath中添加元素,paths中的對應的currentpath對象內容并未隨之發生改變,后將currentPath改為了NSMutableArray(引用傳遞).

2.selector、@objc、private

(純)swift與oc采用了不同的運行機制,swift不再采用與oc一樣的運行時(runtime)與消息分發機制,selector作為oc運行機制的產物,swift中也對其進行了保留與支持。

@objc修飾符的作用是將swift定義的類、方法等暴露給oc。

于是,下列selector中指定的方法,都要使用@objc進行修飾

cleanBut.addTarget(self, action: #selector(cleanButClick), for: UIControlEvents.touchUpInside)
let ges = UIPanGestureRecognizer(target: self, action:#selector(handleGes(ges:)))如果一個swift類繼承自NSObject,swift會默認給該類的非private屬性或方法加上@objc修飾。因為Canvas類(->UIView->UIResponder->NSObject)繼承自NSObject,所以其屬性或方法(非private)都會被自動加上@objc修飾但是因為我代碼中的這幾個selector指向的方法都聲明為了private,所以還是需要手動去做@objc修飾(如果是非private的,可以不寫@objc)
@objc private func handleGes(ges:UIPanGestureRecognizer) -> Void

3.required的構造函數

required用于修飾構造方法,用于要求子類必需實現對應的構造方法如果子類中沒有實現任何構造方法,則不必去顯式的實現父類要求的required構造方法;而當子類中有定義實現構造方法時,則必需顯式的去實現父類要求的required構造方法,同時還要保留required修飾.當實現一個類Canvas繼承自UIView時,我們可以看到編譯器強制要求我們實現構造方法
public init?(coder aDecoder: NSCoder)通過xcode找到該方法是在NSCoding協議中被定義的
public protocol NSCoding {    public func encode(with aCoder: NSCoder)    public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER}可以看到,此處并沒有進行requird修飾,為什么還要求強制實現該構造方法呢?因為在協議中規定的構造方法,不用顯式進行requird修飾,實現協議的對應類默認必需要去實現協議中規定的構造方法,且加上requird修飾

4.as

let x:UInt16 = 100let y:UInt8 = 10//x + y會報錯,不自動類型轉換,更安全let n = UInt8(x) + y上面例子中,當我們進行值類型之間的類型轉換(UInt16->UInt8)時,其實借助的是UInt8的構造方法
/// Create an instance initialized to `value`.    public init(integerLiteral value: UInt8)而當引用類型之間需要進行強制轉換時,則需要借助as操作符因為轉換可能失敗(兩個不相關的類之間進行轉換),所以需要使用as?,轉換結果為一個可選型,不成功時,可選型值為nil當然,如果可以肯定轉換是成功的,則可以使用as!進行轉換,結果為目標類型的對象。另外,看下面這個例子
var people:People?let man:Man = Man()people = manprint(people)//可選型變量let beMan = people as! Manprint (beMan)//強制轉化后beMan不是可選型
var people:People?let man:Man = Man()people = manprint(people)//可選型變量let beMan = people as! Man?print (beMan)//強制轉化后beMan為可選型轉換后的結果類型完全由as!后面的目標類型決定,即便原對象在轉換之前是可選型對象,但如果轉換的目標類型不是可選型,則轉換后得到的也就不是一個可選型了
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 灌南县| 稻城县| 克拉玛依市| 丁青县| 双城市| 小金县| 敦化市| 工布江达县| 深圳市| 凯里市| 独山县| 小金县| 翁牛特旗| 宜春市| 徐水县| 峨眉山市| 收藏| 永兴县| 鸡泽县| 绵阳市| 盘锦市| 金川县| 井研县| 武定县| 洛川县| 莫力| 乌拉特前旗| 靖西县| 安岳县| 无为县| 阳信县| 德州市| 拜泉县| 马尔康县| 蒙阴县| 普兰县| 民乐县| 二连浩特市| 灵宝市| 赣榆县| 岳普湖县|