Swift3.0 Learning Practice-a simple artboard (seven-color trajectory, revocable, removable, with eraser)

Source: Internet
Author: User

Write a fun little program and continue learning swift. Run effect + code + Knowledge Point Summary

Operating Effect:


Code:Canvas classes: Canvases, drawing board state management, interaction, handling gestures
Class canvas:uiview{//responsible for line generation, operation and management let Pathcreator:pathcreator//is in the erase state var isinerasering:bool//Eraser View Let Eraserview:uiview override Init (frame:cgrect) {isinerasering = False pathcreator = Pathcreat        or () 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 ("Undo", For:UIControlState.normal) Revokebut.addtarget (Self, Action: #selector (Revokebutclick), For:UIControlEvents.touch Upinside) Self.addsubview (revokebut) Let cleanbut = UIButton (type:UIButtonType.system) Clean But.frame = CGRect (x:110, Y:20, width:80, height:30) cleanbut.settitle ("Empty", For:UIControlState.normal) Cleanbut.addtarget (self , Action: #selector (Cleanbutclick), for:UIControlEvents.touchUpInside) Self.addsubview (cleanbut) Let ER        Aserbut = UIButton (type:UIButtonType.system) eraserbut.frame = CGRect (x:200, y:20, width:80, height:30)        Eraserbut.settitle ("Eraser", For:UIControlState.normal) eraserbut.settitle ("brushes", for:UIControlState.selected) Eraserbut.addtarget (Self, Action: #selector (Eraserbutclick (but:)), for:UIControlEvents.touchUpInside) SELF.ADDSUBV Iew (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:) have not been implemented")} Override public Func layoutsubviews () { } @objc PrivAte func handleges (ges:uipangesturerecognizer), Void {Let point = ges.location (in:self) switch ges.st                ate {case UIGestureRecognizerState.began:if isinerasering {//Erase status, display eraser Eraserview.alpha = 1 Eraserview.center = point}//Generate a new pen pathcreator.ad Dnewpath (to:point,iseraser:isinerasering) self.setneedsdisplay () Case uigesturerecognizerstate.changed            : if isinerasering {//move eraser Eraserview.center = ges.location (in:self) }//Update current stroke path Pathcreator.addlineforcurrentpath (to:point,iseraser:isinerasering) Self.s                Etneedsdisplay () Case UIGestureRecognizerState.ended:if isinerasering {//Erase status, Hide eraser Eraserview.alpha = 0 Eraserview.center = ges.location (in:self)}//Update current pen Draw Path PathcreatOr.addlineforcurrentpath (to:point,iseraser:isinerasering) self.setneedsdisplay () Case UIGestureRecogni        ZerState.cancelled:print ("Cancel") Case UIGestureRecognizerState.failed:print ("fail") Default:return}} Override public Func Draw (_ Rect:cgrect) {//Draw line Pathcreat        Or.drawpaths ()} @objc private func Revokebutclick ()->void{/undo Operation Pathcreator.revoke ()        Self.setneedsdisplay ()} @objc private func Cleanbutclick ()->void{//emptying Operation Pathcreator.clean ()        Self.setneedsdisplay ()} @objc private func Eraserbutclick (But:uibutton)->void{//Toggle paint and Erase status If but.isselected {but.isselected = False isinerasering = False}else{But.iss elected = True isinerasering = True}}}

Pathcreator: Specific line drawing, management

Each stripe segment information struct bezierinfo{let path:uibezierpath//specific segment let color:uicolor//segment corresponds to color init (path:uibezierpath,color:ui color) {Self.path = path Self.color = Color}}class pathcreator{//All strokes private var paths:[nsmutabl    Earray]?    The current sub segment in the stroke is the private var currentbezierpathinfo:bezierinfo?    All sub segments of the current stroke private Var currentpath:nsmutablearray? The current stroke has been collected processing several touch points private var Pointcountinonepath = 0 static let colors = [uicolor.red,uicolor.orange,uicolor.y Ellow,uicolor.green,uicolor.blue,uicolor.gray,uicolor.purple] Init () {paths = []}//Add new stroke func AddNew        Path (To:cgpoint,iseraser:bool)->void{//Create start segment Let Path = Uibezierpath () Path.linewidth = 5 Path.move (to:to) Path.linejoinstyle = Cglinejoin.round Path.linecapstyle = Cglinecap.round if!isEr        Aser {//bound segment and color information currentbezierpathinfo = Bezierinfo (Path:path, color:pathcreator.colors[0])   }else{         In erase mode, the color is the same as the artboard background color currentbezierpathinfo = Bezierinfo (Path:path, Color:UIColor.black)} Create a new stroke Currentpath = Nsmutablearray.init ()//Add the starting segment to the current stroke currentpath!. Add (currentbezierpathinfo) Pointcountinonepath = 0//Add the current stroke to the stroke array paths!.    Append (currentpath!) }//Add new point, update current stroke path func Addlineforcurrentpath (To:cgpoint,iseraser:bool), Void {Pointcountinonepath + = 1 Within the same stroke, each 7 points for the color if pointcountinonepath% 7 = = 0{//change color if let Currentbezierpathinfo = Currentbezierpath            info{//Adds the current point to the current sub-segment, updating the current sub-segment path CurrentBezierPathInfo.path.addLine (to:to)}            Generate new sub-segment Let Path = Uibezierpath () Path.linewidth = 5 Path.move (to:to)                Path.linejoinstyle = Cglinejoin.round Path.linecapstyle = Cglinecap.round if!iseraser{ Sets the next color for the current sub-segment Currentbezierpathinfo = Bezierinfo (Path:path, color:pathcreator.colors[currentpath!. Count% 7])}else{//In erase mode, same color as artboard background color currentbezierpathinfo = Bezierinfo (path:p Ath, Color:UIColor.black)}//Adds the current sub-segment to the current stroke currentpath!. Add (currentbezierpathinfo)}else{if let Currentbezierpathinfo = currentbezierpathinfo{/ /Add current point to Current sub segment, update current sub-segment path CurrentBezierPathInfo.path.addLine (to:to)}}} Func Dr Awpaths ()->void{//Draw line let Pathcount = paths!. Count for I in 0..<pathcount{//Remove all strokes let OnePath = paths![                I] let Onepathcount = Onepath.count to J in 0..<onepathcount{//Draw each sub-segment within each stroke Let PathInfo = Onepath.object (at:j) as! Bezierinfo PathInfo.color.set () PathInfo.path.stroke ()}} Fun C Revoke ()->void{//move onto a stroke if paths!. Count > 0 {paths!. Removelast ()}} func Clean ()->void{//Remove all stroke paths!. RemoveAll ()}}
Summary of Knowledge points: 1. struct is value passinga basic concept, but it's still forgotten when you start using it. An array [] is a struct (struct) implementation in swift and a value is passed. At the beginning of the Currentpath declaration in order to [], add to paths[], the subsequent to the Currentpath to add elements, paths the corresponding Currentpath object content has not changed, The Currentpath is changed to Nsmutablearray (Reference pass).2.selector, @objc, private

(pure) Swift and OC adopt different operating mechanisms, swift no longer adopt the same runtime and message distribution mechanism as OC, selector as the product of OC operating mechanism, it is also retained and supported by Swift.

The role of the @objc modifier is to expose a swift-defined class, method, and so on to OC.

As a result, the methods specified in the following selector are decorated with @objc

Cleanbut.addtarget (Self, Action: #selector (Cleanbutclick), for:UIControlEvents.touchUpInside)
Let Ges = Uipangesturerecognizer (target:self, Action: #selector (Handleges (GES:)))
If a swift class inherits from Nsobject,swift, it will default to the non-private property or method of the class plus the @objc adornment.
Because the canvas class (->uiview->uiresponder->nsobject) inherits from NSObject, its properties or methods (not private) are automatically added @objc decoratedbut because my code of these several selector point to the method is declared for private, so still need to do @objc decoration manually (if it is non-private, can not write @objc)
@objc private func handleges (Ges:uipangesturerecognizer), Void
Constructors for 3.requiredrequired is used to modify the construction method to require that the subclass must implement the corresponding construction methodIf you do not implement any of the constructor methods in the subclass, you do not have to explicitly implement the required construction method required by the parent class, and when there is a definition implementation construct method in the subclass, you must explicitly implement the required construction method required by the parent class, while preserving Required decoration. when implementing a class canvas that inherits from UIView, we can see the compiler forcing us to implement the constructor method
Public init? (Coder Adecoder:nscoder)
the method found through Xcode is defined in the Nscoding protocol.
Public protocol nscoding {public    func encode (with Acoder:nscoder) public    init? ( Coder Adecoder:nscoder)//Ns_designated_initializer}
as you can see, there is no requird modification here, why is it required to enforce the construction method?
Because the construction method stipulated in the Protocol does not have to be requird decorated explicitly, the corresponding class of implementing the Protocol must implement the construction method stipulated in the protocol by default, and add requird adornment
4.as
Let x:uint16 = 100let y:uint8 = 10//x + y will error, not automatic type conversion, more secure get n = UInt8 (x) + y
in the example above, when we make a type conversion between value types (uint16->uint8), we are actually using the UInt8 construction method
Create an instance initialized to ' value '.    Public init (IntegerLiteral value:uint8)
When a cast is required between reference types, the as operator is requiredbecause conversions can fail (conversions between two unrelated classes), you need to use as?, the conversion result is a selectable, unsuccessful, the selectable value is nilof course, if you can be sure that the conversion is successful, you can use as! to convert the result to an object of the target type. also, look at the following example
var people:people?let man:man = Man () people = manprint (people)//selectable variable Let Beman = People as! Manprint (Beman)//Beman not selectable after forced conversion
var people:people?let man:man = Man () people = manprint (people)//selectable variable Let Beman = People as! Man?print (Beman)//after forced conversion Beman to be selectable
The converted result type is entirely determined by the target type behind the as!, even if the original object is a selectable object before conversion, but if the target type of the conversion is not selectable, then the conversion will not be a selectable

Swift3.0 Learning Practice-a simple artboard (seven-color trajectory, revocable, removable, with eraser)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.