【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记24 popovers弹窗
上幾話中我們詳細了解了幾種segue,我們也了解到了多MVC模式的幾種控制器,比如導航、選項卡和分欄,除了這三種多MVC的模式之外,還有一種popover,它跟其他三種不太一樣。首先先來認識一下popover(彈窗)
你可以看到彈窗會有一個小箭頭指向觸發彈窗的地方:
它像一個白色的三角形。出了彈窗的區域是白色,其他區域都是灰色的,單擊其他區域的唯一功能就是讓彈窗消失。
說popover不同的原因是,它不是一個UIViewController。它通常是presentation controller來出現在屏幕上的。所以popover并不真的需要一個viewcontroller,它的view是這個MVC,它可以純粹依靠presentation controller機制來做到這一點。
雖然它不是自己的viewcontroller,但是它依舊有所有的segue,用法并沒有區別。我們剛才在示例中看到的彈窗是ipad上的效果,在iphone它被modal替代了,IOS自動為你配適的。但是如果你使用代理或者presentation controller,你可以影響這個配適。我們來看幻燈片:
綠色部分和其他segue沒有什么區別,但是黃色這行我從viewcontroller 過渡到popover得presentationcontroller。當你設置自身為代理時,你能做些什么呢?
我們看到代理中有兩個代理方法。第一個方法用來配適設備,默認iphone上全屏展示,如果你把它的返回值的風格設為none,表示不配適,那么它的彈窗會和iphone上一樣。
彈窗的另外一個重點是尺寸,你可能需要用一種面向對象的方式,也就是系統調用的方式來詢問MVC合適的尺寸是多少,這只是控制器的一個屬性,你可以重寫它:
下面來展示一個Demo,讓我們的彈窗顯示瀏覽歷史,并且適應內容的尺寸。
我們回到Psychologist這個Demo中,在storyboard中給HappinessVeiwController右上角添加一個按鈕History用來顯示我們點擊的按鈕的值,這些值組成整數數組用來表達小人臉的開心程度。注意這個按鈕不要用UIButton,用BarButtonItem,這是個輕量級的按鈕,專門放置在導航欄或者工具欄上。
我們需要讓這個按鈕展示一個新的控制器,所以我們向storyboard中拖一個新的控制器,然后把History按鈕和這個控制器連線,注意segue方式要選擇popover present。
和其他segue一樣,給Identifier命名,我們取名為Show Diagnostic History。
雖然現在控制器是空白的,但是我們已經可以運行了。我們創建一個UIViewController和這個控制器對應起來,取名為TextViewController。
在storyboard中拖一個text view到新控制器中,這個textview可以顯示多行文本,設置它為不可編輯,但是可以選中,修改文本文字為24號。你會在storyboard中看到textview中有很多文字,這些是占位文字,沒有關系我們會在運行的時候重新寫值,這些占位文字是不會顯示的。
我們在代碼中創建多行文本的outlet。
import UIKitclass TextViewController: UIViewController {@IBOutlet weak var textView: UITextView!{didSet{textView.text = text}}var text:String = ""{didSet{textView?.text = text}} }現在該為我們的segue做些準備了。那么這些準備工作應該在哪里做呢?顯然我們不應該在HappinessViewController中做,因為這個控制器你可能是從別處拷貝來的,它的設計者希望它是專門用來管理笑臉的,它應該對瀏覽歷史一無所知。那么我們該如何做呢?答案是創建一個新的控制器,然后繼承HappinessViewController,再在其中增加瀏覽歷史的功能。 import UIKitclass DiagonsedHappinessViewController: HappinessViewController {}
那么現在回到storyboard中,笑臉的類應該不再是HappinessViewController了,而是我們剛剛修改的新的類。
我們之前設置的各種outlet不會有問題,因為它是子類,繼承了父類的所有東西,包括outlet。這就是控制器的多態性,通常你會有一個可重用的控制器,也許你想給某個特定的控制器中增加功能,這樣你就可以創建它的子類。
import UIKitclass DiagonsedHappinessViewController: HappinessViewController {override var happiness:Int{didSet{diagnostHistory += [happiness]}}var diagnostHistory = [Int]()private struct History{static let SegueIdentifier = "Show Diagnostic History"}override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {if let identifier = segue.identifier{switch identifier{case History.SegueIdentifier:if let tcv = segue.destinationViewController as? TextViewController{tcv.text = "\(diagnostHistory)"}default:break}}}}我們重寫了屬性happiness,這里的屬性觀察器和父類中的觀察器不會沖突,程序會先執行父類中happiness的觀察器運行你會發現這個記錄只能記錄上一次的點擊記錄,這是因為我們之前講過的使用segue每次打開的MVC都是新創建的,所以這個瀏覽記錄需要存在我們之前講過的NSUserDefaults中。我們把diagnosticHistory改成計算屬性,靠它讀取或者寫入NSDuserDefaults。
新的代碼:
import UIKitclass DiagonsedHappinessViewController: HappinessViewController {override var happiness:Int{didSet{diagnostHistory += [happiness]}}private let defaults = NSUserDefaults.standardUserDefaults()var diagnostHistory:[Int]{get{return defaults.objectForKey(History.DefaultsKey) as? [Int] ?? []}set{defaults.setObject(newValue, forKey: History.DefaultsKey)}}private struct History{static let SegueIdentifier = "Show Diagnostic History"static let DefaultsKey = "DiagnosedHappinessViewController.History"}override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {if let identifier = segue.identifier{switch identifier{case History.SegueIdentifier:if let tcv = segue.destinationViewController as? TextViewController{tcv.text = "\(diagnostHistory)"}default:break}}}}來運行一下看看,我們看到在iphone6上雖然出現了瀏覽記錄,但是popover依舊會布滿屏幕,下一個任務是修改它的尺寸了。
首先我們需要讓我們的控制器可以作為自己的彈窗代理,所以:
class DiagonsedHappinessViewController: HappinessViewController,UIPopoverControllerDelegate然后如我們之前講的,在segue中做處理,把原來的語句修改如下: case History.SegueIdentifier:if let tvc = segue.destinationViewController as? TextViewController,let ppc = tvc.popoverPresentationController {ppc.delegate = selftvc.text = "\(diagnostHistory)"}
可以看到popoverPresentationController是在UIViewController中的,只當這個MVC真的在一個彈窗中的時候它才會返回,否則返回nil,然后我們把代理設為自己,這是我們第一次控制系統的代理。之后我們實現控制尺寸的代理方法; func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!, traitCollection: UITraitCollection!) -> UIModalPresentationStyle {return UIModalPresentationStyle.None}
這個代理的返回表示我們不做任何配適,運行看看:
完整代碼如下:
import UIKitclass DiagonsedHappinessViewController: HappinessViewController,UIPopoverPresentationControllerDelegate{override var happiness:Int{didSet{diagnostHistory += [happiness]}}private let defaults = NSUserDefaults.standardUserDefaults()var diagnostHistory:[Int]{get{return defaults.objectForKey(History.DefaultsKey) as? [Int] ?? []}set{defaults.setObject(newValue, forKey: History.DefaultsKey)}}private struct History{static let SegueIdentifier = "Show Diagnostic History"static let DefaultsKey = "DiagnosedHappinessViewController.History"}override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {if let identifier = segue.identifier{switch identifier{case History.SegueIdentifier:if let tvc = segue.destinationViewController as? TextViewController,let ppc = tvc.popoverPresentationController {ppc.delegate = selftvc.text = "\(diagnostHistory)"}default:break}}}func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!, traitCollection: UITraitCollection!) -> UIModalPresentationStyle {return UIModalPresentationStyle.None}}現在還有最后一個步驟,彈窗的窗口有點大,我們需要它的尺寸適應彈窗中的內容的大小。這次我們需要到TextViewController中去做修改:
override var preferredContentSize:CGSize {get{if textView != nil && presentingViewController != nil{//presentationViewController也是父類的屬性,表示當前顯示的頁面return textView.sizeThatFits(presentingViewController!.view.bounds.size)} else {return super.preferredContentSize //無論如何要考慮所有情況}}set{super.preferredContentSize = newValue}}
我們重寫父類中的這個屬性
然后再次運行:
成功了!
總結
以上是生活随笔為你收集整理的【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记24 popovers弹窗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XState Viz 可视化和调试状态机
- 下一篇: hdu水仙花