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)