Standford 2015 week4: 1.Protocols and Delegation, Gestures 2. Multiple MVCs

Post on 28-Jul-2015

447 views 2 download

Transcript of Standford 2015 week4: 1.Protocols and Delegation, Gestures 2. Multiple MVCs

Standford 2015 iOS讀書會 week4

1. Protocols and Delegation, Gestures 2. Multiple MVCs

彼得潘

Protocols and Delegation, Gestures

@IBDesignable 在storyboard顯⽰示客製化的UI元件

@IBDesignable class FaceView: UIView {

@IBInspectable @IBInspectable var scale:CGFloat = 0.9 { didSet { setNeedsDisplay() } } @IBInspectable var lineWidth:CGFloat = 3 { didSet { setNeedsDisplay() } } @IBInspectable var color:UIColor = UIColor.blueColor() { didSet { setNeedsDisplay() } }

在storyboard增加元件的設定欄位⽀支援的欄位型別:

booleans, strings, numbers CGPoint, CGSize, CGRect, UIColor, NSRange, UIImage

var happiness:Int = 50 { didSet { happiness = min(max(happiness, 0), 100) updateUI() } }

在property observer裡修改property, 不會觸發observer再次被呼叫

升級原有型別能⼒力的extension

• computed property。(不⽀支援stored property) • ⽅方法 • initializer • subscript • 遵從protocol,定義protocol裡宣告的⽅方法屬性 • nested type

升級原有型別能⼒力的extension

出⼀一張嘴,只宣告不定義的protocol可宣告⽅方法,computed property,

subscript,initializer

get⼀一定要有,set可有可無

class, struct , enum可實作protocol

出⼀一張嘴,只宣告不定義的protocol

出⼀一張嘴,只宣告不定義的protocol

要⽤用 as!

宣告遵從多個protocol的變數

protocol Idol { func sing() }

protocol Artist { func paint() }

var man:Idol! var woman:protocol<Idol, Artist>!

不⼀一定要定義的optional

要加上@objc 和import Foundation

import Foundation

@objc protocol Idol { func sing() func run() optional var money:Double { get } optional func playPiano() -> String }

不⼀一定要定義的optional

加了optional的protocol只有class可以遵從

此時class也要加上@objc

@objc class Baby:Idol { func sing() { } func run() { } func playPiano() -> String { return "hi" } }

var cuteBaby:Idol = Baby() cuteBaby.playPiano?()

protocol

protocol

實作protocol的init要加上required (因為subclass可能沒有繼承init)

demoprotocol FaceViewDataSource:class { func smilinessForFaceView(sender:FaceView) -> Double? }

delegate的⽅方法常常會將⾃自⼰己當參數代理⼈人可依此做不同的處理

ex: smilinessForFaceView的sender

weak var dataSource:FaceViewDataSource?

weak只能作⽤用於物件,所以要加class

let smiliness = self.dataSource?.smilinessForFaceView(self) ?? 0 let smilePath = bezierPathForSmile(smiliness)

遇nil即臨陣脫逃的 optional chaining

optional chaining的回傳結果也會是optional

遇nil即臨陣脫逃的optional chaining

利用optional binding判斷可能為nil的cuteBaby和eatFood

定義Food類別和Baby類別

遇nil即臨陣脫逃的optional chaining

利用optional chaining在function eat回傳的Food存取name

利用optional chaining在optional的cuteBaby上呼叫eat function

遇nil即臨陣脫逃的optional chaining

透過optional chaining存取的age也會是optional,不能直接做加法運算

遇nil即臨陣脫逃的optional chainingdog1當初宣告時不是optional,不用加?存取下一層的food

dog1並非宣告為optional,此生注定和!無緣

變出預設值的雙重問號 當age等於nil時,一律回覆18歲

當age為nil時,回傳雙重問號右邊的預設值18

demo

func smilinessForFaceView(sender: FaceView) -> Double? { return Double(self.happiness - 50)/50 }

class HappinessViewController: UIViewController, FaceViewDataSource {

@IBOutlet weak var faceView: FaceView! { didSet { self.faceView.dataSource = self } }

當faceView連結設定時,設定dataSource為self

func updateUI() { faceView.setNeedsDisplay() }

cmd + shift + o

開啟open quickly視窗

gestureUITapGestureRecognizer

UISwipeGestureRecognizer

UIPanGestureRecognizer

UIPinchGestureRecognizer

UIRotationGestureRecognizer

UILongPressGestureRecognizer

UIScreenEdgePanGestureRecognizer類似UIPanGestureRecognizer,但限制⼿手勢須從螢幕邊緣開始

使⽤用gesture

1.加gesture加到作⽤用的view上

2.設定gesture發⽣生時觸發的method

gesture觸發的⽅方法

gesture觸發的⽅方法實例

不同gesture包含 不同的資訊

demo func pan(gesture:UIPanGestureRecognizer) { switch gesture.state { case .Changed: fallthrough case .Ended: let translation = gesture.translationInView(blueView) println("translation \(translation)") gesture.setTranslation(CGPointZero, inView:blueView) default: break } }

觀察呼叫gesture.setTranslation和沒有呼叫gesture.setTranslation的差別

利⽤用pan移動view:⽅方法1

func pan(gesture:UIPanGestureRecognizer) { switch gesture.state { case .Began: originalCenter = blueView.center case .Changed: fallthrough case .Ended: let translation = gesture.translationInView(blueView) blueView.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y); default: break } }

class ViewController: UIViewController { var originalCenter:CGPoint!

利⽤用pan移動view:⽅方法2func pan(gesture:UIPanGestureRecognizer) { switch gesture.state { case .Changed: fallthrough case .Ended: let translation = gesture.translationInView(blueView) let originalCenter = blueView.center blueView.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y); gesture.setTranslation(CGPointZero, inView:blueView) default: break } }

pan gesture

var minimumNumberOfTouches: Int // default is 1. the minimum number of touches required to match

var maximumNumberOfTouches: Int // default is UINT_MAX. the maximum number of touches that can be down

standford demo

func scale(gesture:UIPinchGestureRecognizer) { scale = scale * gesture.scale gesture.scale = 1 }

@IBOutlet weak var faceView: FaceView! { didSet { self.faceView.dataSource = self self.faceView.addGestureRecognizer(UIPinchGestureRecognizer(target: self.faceView, action: "scale:")) } }

從storyboard加gesture

從storyboard加gesture private struct Constants { static let HappinessGestureScale:CGFloat = 4 } @IBAction func changeHappiness(sender: UIPanGestureRecognizer) { switch sender.state { case .Ended: fallthrough case .Changed: let translation = sender.translationInView(faceView) let happinessChange = -Int(translation.y / Constants.HappinessGestureScale) if happinessChange != 0 { happiness += happinessChange sender.setTranslation(CGPointZero, inView: faceView) } default: break } }

⼿手指往下 -> 不開⼼心⼿手指往上 -> 開⼼心

模擬器pinch

按住option後,按觸控版移動

https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/iOS_Simulator_Guide/

InteractingwiththeiOSSimulator.html

Multiple MVCs

第五個tab: more

For iPadiPhone只有6 plus的landscape會分割畫⾯面iPhone模式下的問題

UITabBarController & UINavigationController的結合

Show: 1.有navigation controller⽤用push2. 沒有navigation controller⽤用modal

Show Detail: 1.有Split View Controller顯⽰示detail(Split View Controller若搭配navigation controller,

在iPhone會採⽤用push顯⽰示detail,除了6 plus landscape例外) 2. 沒有Split View Controller⽤用modal

func performSegueWithIdentifier(identifier: String, sender: AnyObject?)

從程式觸發切換到另⼀一個controller畫⾯面

Once the MVC is prepared, it should run on its own power (only using delegation to talk back)

It is crucial to understand that this preparation is happening BEFORE outlets get set!!

防⽌止segue發⽣生

func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool

⽐比較Reset to Suggested Constraints & Add Missing Constraints

⾃自動縮放字型⼤大⼩小

Split View Controller的iPhone 問題

解法: master加上navigation controller

func updateUI() { faceView.setNeedsDisplay() }

func updateUI() { faceView?.setNeedsDisplay() }

修正nil crash問題

optional chaining

笑臉被bar檔到了

修正笑臉檔到問題

iPad版笑臉加標題

iPad版笑臉加標題

property observer的override保留⽗父類別的observer

property observer的override保留⽗父類別的observer

property observer的override

問題: history只有⼀一筆資料

因為DiagnosedHappinessViewController會重新⽣生成

問題: history只有⼀一筆資料

利⽤用user defaults儲存

private let defaults = NSUserDefaults.standardUserDefaults() var diagnosticHisotry:[Int] { get { return defaults.objectForKey(History.DefaultsKey) as? [Int] ?? [] } set { defaults.setObject(newValue, forKey: History.DefaultsKey) } }

修正iPhone Popover 全螢幕的問題

修正iPhone Popover 全螢幕的問題

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle { return UIModalPresentationStyle.None }

class DiagnosedHappinessViewController : HappinessViewController, UIPopoverPresentationControllerDelegate

實作UIPopoverPresentationControllerDelegate的⽅方法

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let identifier = segue.identifier { switch identifier { case History.SegueIdentifier: if let tvc = segue.destinationViewController as? TextViewController { if let ppc = tvc.popoverPresentationController { ppc.delegate = self } tvc.text = "\(diagnosticHisotry)" } default: break } } }

修正iPhone Popover 全螢幕的問題

修正iPhone Popover 全螢幕的問題

調整pop over的size

override var preferredContentSize:CGSize { get { if textView != nil && presentingViewController != nil { let size = textView.sizeThatFits(presentingViewController!.view.bounds.size) println("size \(size)") return textView.sizeThatFits(presentingViewController!.view.bounds.size) } else { return super.preferredContentSize } } set { super.preferredContentSize = newValue } }

定義UIViewController的preferredContentSize property

調整pop over的size