Standford 2015 week4: 1.Protocols and Delegation, Gestures 2. Multiple MVCs
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