亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

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

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

2019-11-09 14:44:28
字體:
來源:轉載
供稿:網友

寫著玩兒的小程序,繼續學習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!后面的目標類型決定,即便原對象在轉換之前是可選型對象,但如果轉換的目標類型不是可選型,則轉換后得到的也就不是一個可選型了
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品女人网站| 深夜成人在线观看| 91久久中文字幕| 欧美日韩亚洲一区二| 日韩小视频网址| 亚洲91精品在线| www.日韩不卡电影av| 日本欧美爱爱爱| 欧美高清视频在线观看| 在线观看欧美视频| 在线观看久久av| 亚洲精品国产品国语在线| 欧美性xxxxhd| 欧美日韩国产91| 日本一区二区三区四区视频| 影音先锋日韩有码| 亚洲精品自拍第一页| 夜夜嗨av色一区二区不卡| 久久在线免费观看视频| 日本亚洲欧洲色α| 日韩精品在线观看网站| 91精品国产免费久久久久久| 欧美wwwxxxx| 色婷婷综合久久久久中文字幕1| 欧美亚洲一区在线| 国产视频自拍一区| 精品一区电影国产| 91在线观看免费观看| 欧美在线影院在线视频| 日韩在线免费视频| 国产精品第2页| 激情成人中文字幕| 欧美在线性爱视频| 午夜精品一区二区三区在线视频| 国产一区二区三区高清在线观看| 26uuu另类亚洲欧美日本老年| 亚洲高清福利视频| 国产视频综合在线| 一区二区三区视频在线| 精品女厕一区二区三区| 日本欧美中文字幕| 午夜精品蜜臀一区二区三区免费| 最新国产精品拍自在线播放| 在线成人一区二区| 色先锋久久影院av| 一区二区三区视频免费| 色yeye香蕉凹凸一区二区av| 国产精品第一第二| 久久久久久久国产精品| 欧美性猛交丰臀xxxxx网站| 日本不卡免费高清视频| 正在播放亚洲1区| 久久久久久亚洲精品不卡| 成人在线播放av| 国产精品电影观看| 亚洲视屏在线播放| 国外色69视频在线观看| 欧美性做爰毛片| 欧美日韩一区二区三区在线免费观看| 亚洲第一福利网| 国产精品久久久久久久久久久久| 中文字幕精品一区二区精品| 免费不卡欧美自拍视频| 国产欧美日韩亚洲精品| 欧美老女人bb| 亚洲免费精彩视频| 欧美另类69精品久久久久9999| 亚洲乱码国产乱码精品精天堂| 国产在线视频2019最新视频| 欧美高清第一页| 精品国产乱码久久久久久虫虫漫画| 欧美做受高潮1| 欧美午夜影院在线视频| 久久手机精品视频| 精品久久久香蕉免费精品视频| 91免费看国产| 91精品国产高清久久久久久久久| 欧美精品videos另类日本| 亚洲国产精品大全| 国内精品久久影院| 国产精品视频一区二区三区四| 亚洲成年人影院在线| 久久99精品久久久久久琪琪| 国产精品久久久久久久9999| 亚洲第一福利在线观看| 狠狠色狠狠色综合日日五| 亚洲第一视频网站| 91精品国产高清自在线看超| 欧美在线观看视频| 国产亚洲欧美日韩美女| 日韩国产高清污视频在线观看| 97婷婷大伊香蕉精品视频| 91久久久久久| 中文字幕亚洲情99在线| 亚洲国产成人精品电影| 久久91亚洲人成电影网站| www.亚洲成人| 亚洲成人久久电影| 国产精品免费观看在线| 精品久久久一区| 国产精品免费在线免费| 欧美裸体男粗大视频在线观看| 国产精品青草久久久久福利99| 亚洲精品国偷自产在线99热| 精品国产一区二区三区久久久| 精品国产一区二区三区久久狼黑人| 欧美丰满少妇xxxxx| 欧美精品一区在线播放| 伊人亚洲福利一区二区三区| 大伊人狠狠躁夜夜躁av一区| 久久精视频免费在线久久完整在线看| 日韩av在线免费看| 亚洲最大中文字幕| 欧美大肥婆大肥bbbbb| 亚洲综合日韩中文字幕v在线| 久久精品中文字幕电影| 久久久久久久久91| 性欧美办公室18xxxxhd| 亚洲肉体裸体xxxx137| 欧美亚洲成人免费| 国产一区二区精品丝袜| 免费不卡在线观看av| 激情成人在线视频| 日本伊人精品一区二区三区介绍| 中文字幕欧美精品在线| 欧美一区二区三区图| 中文日韩在线观看| 欧美专区第一页| 91精品国产综合久久久久久久久| 亚洲成人网在线观看| 国产亚洲人成a一在线v站| 日韩在线欧美在线国产在线| 亚洲欧美制服丝袜| 久久久噜噜噜久久久| 亚洲欧美国产一本综合首页| 精品国产91久久久久久老师| 黑人狂躁日本妞一区二区三区| 日韩在线免费视频观看| 久久久噜噜噜久久久| 亚洲国产私拍精品国模在线观看| 久久久久久久久亚洲| 久久av在线看| 亚洲a∨日韩av高清在线观看| 精品久久久久久国产91| 日韩欧美在线免费观看| 欧美成人免费网| 欧美一区二区.| 久久久久久噜噜噜久久久精品| 中文字幕亚洲字幕| 韩日欧美一区二区| 国产欧美日韩视频| 国产欧美在线观看| 91精品视频播放| 日韩av男人的天堂| 色婷婷综合久久久久中文字幕1| 精品国产拍在线观看| 国内精品400部情侣激情| 欧美黄网免费在线观看| 性欧美暴力猛交69hd| 久久久免费高清电视剧观看| 亚洲人成绝费网站色www| 国产极品jizzhd欧美| 91精品久久久久久综合乱菊| 欧美日韩国产麻豆|