<b>本文講的是UIScrollView 新手教程,</b>
<b></b>
<code>UIScrollView</code> 是 iOS 中最靈活和有用的控件之一。它是十分流行的 <code>UITableView</code> 控件的基礎,能夠友好地展示超過一屏的内容。在這份 <code>UIScrollView</code> 教程中,通過建構一個類似自帶的「照片」應用,你将會掌握以下内容:
如何使用 <code>UIScrollView</code> 來縮放圖像,檢視大圖
如何在縮放時保持 <code>UIScrollView</code> 的内容居中
如何在自動布局時使用 <code>UIScrollView</code> 進行豎直滾動
如何在鍵盤呼出時保持文本輸入控件可見
如何和 <code>UIPageControl</code> 一起使用 <code>UIPageViewController</code> ,實作内容多頁連播
編譯并運作,看看我們最初的項目:
<a href="https://camo.githubusercontent.com/3410ee58ef3c7e60605a9fb0cd2d85e5bf2ea4cd/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031352f31322f73746172746572312e676966" target="_blank"></a>
選中圖檔時,你看到它變成了全屏。但遺憾的是,圖檔被裁剪了。由于裝置尺寸的限制,你無法看到整張圖檔。你真正想要的是讓圖檔預設适應裝置的螢幕,并且能夠放大觀察細節,就像在 Photos 應用中一樣。
你能解決嗎?當然了!
這份 <code>UIScrollView</code> 教程教給你的第一件事是,如何設定一個滾動視圖,允許使用者縮放、移動圖檔。
首先,你需要添加一個滾動視圖。打開 Main.storyboard ,從 Object Library 拖動一個 Scroll View ,放到 Zoomed Photo View Controller Scene 視圖下的 Document Outline 。将 Image View 移動到你建立的 Scroll View 中。你的 Document Outline 現在應該是這樣的:
看到紅點了麼?Xcode 正在提示你有一些自動布局的規則沒有被正确地定義。為了解決這個問題,選中你的 Scroll View ,點選 Storyboard 視窗底部的鎖定按鈕。添加四個新的限制:頂部、底部、前後間距。取消選中 Constrain to margins ,将所有的限制值都設為 0。
接下來選中 Image View 并添加相同限制。
選中 Document Outline 中的 Zoomed Photo View Controller 來消除自動布局的警告,然後選擇 Editor (編輯器)\ Resolve Auto Layout Issues (解決限制問題)\ Update Frames (更新控件位置)。
最後,在 Zoomed Photo View Controller 的 Attribute Inspector 中取消選中 Adjust Scroll View Insets 。
編譯并運作。
<a href="https://camo.githubusercontent.com/0b0dbe18af36e6019e12ec7b07180baa31858311/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031352f31322f73746172746572322e676966" target="_blank"></a>
多虧了滾動視圖,你現在可以滑動檢視原尺寸的圖檔了。但如果你希望圖檔大小适應螢幕呢?或者如果你希望放大或縮小圖檔呢?
準備好開始寫代碼了嗎?
打開 ZoomedPhotoViewController.swift ,在類聲明中,添加下面的 outlet 屬性:
回到 Main.storyboard ,為了将 Scroll View 和 Zoomed View Controller 協同工作,我們需要将它添加到 <code>scrollView</code>outlet ,将 Zoomed View Controller 設定為 Scroll View 的代理。同樣地,将 Zoomed View Controller 中新的限制 outlet 連接配接到 Document Outline 中相應的限制,就像這樣:
現在,你要開始接觸到代碼。在 ZoomedPhotoViewController.swift 中,将 <code>UIScrollViewDelegate</code> 方法的實作添加為擴充:
這就是滾動視圖縮放原理的關鍵。你告訴它捏住滾動視圖時,哪個視圖應該變大或變小。在這裡,就是你的 <code>imageView</code> 。
現在,将 <code>updateMinZoomScaleForSize(_:)</code> 的實作添加到 <code>ZoomedPhotoViewController</code> 類:
你需要确定滾動視圖的最小縮放比例。縮放比例的意思是内容以正常大小顯示的比例。小于這個比例内容會被縮小,大于這個比例内容會被放大。為了确定最小縮放比例,你要計算圖檔的寬度需要縮小多少才能緊緊地貼合在滾動視圖的邊界上。然後對圖檔的高度做同樣的事。這兩者中更小的比例就是滾動視圖的最小縮放比例。按這個比例縮小之後你可以看到整張圖檔。注意最大縮放比例預設為 1 。你不用修改這個比例,因為放大到超過了圖檔的分辨率會使圖檔看上去模糊。
你可以将初始的縮放比例設為最小縮放比例,這樣圖像一開始就是完全縮小到适應螢幕的。
最後,每次控制器更新子視圖時更新最小縮放比例:
編譯并運作。你可以看到下面的結果:
<a href="https://camo.githubusercontent.com/27dca7bd333b2d84329a928e39abe44f4ad5fb5c/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031352f31322f73746172746572332e676966" target="_blank"></a>
手機豎放時圖檔填充了整個螢幕。你可以縮放圖檔,但還有一些小問題:
圖檔被固定在視圖頂部。如果能夠居中就更好了。
如果你将手機将手機水準過來,你的視圖不會重新計算尺寸。
還是在 ZoomedPhotoViewController.swift 中,實作 <code>updateConstraintsForSize(size:)</code> 函數來解決這些問題:
這個方法解決了 <code>UIScrollView</code> 一個煩人的現象:如果滾動視圖的内容尺寸小于視圖邊界,它會被固定在左上角而不是正中間。由于你允許使用者随意縮放,如果圖檔能放置在視圖中央就更好了。這個函數通過修改布局限制實作了這個特性。
用 <code>view</code> 的高度減去 <code>imageView</code> 的高度除以二可以得到整個螢幕的垂直中心,你将可以用它來确定 <code>imageView</code> 的頂部和底部限制。
類似地,你可以計算 <code>imageView</code> 左右間距的偏移量。
在 <code>UIScrollViewDelegate</code> 擴充中,添加 <code>scrollViewDidZoom(_:)</code> 的實作:
在這個函數中,每當使用者滾動時都會重新居中視圖——不然的話,縮放看上去不會那麼自然,而是被固定在了左上角。
現在,深呼吸,放輕松,編譯并運作你的項目!按下一張圖檔,如果一切順利的話,你可以對它雙指縮放、單指拖拽和點按縮放 :]
<a href="https://camo.githubusercontent.com/9b641c124c01fd7310d29a18ccd60abb8f3e9e77/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031352f31322f73746172746572342e676966" target="_blank"></a>
假設你想要改變一下 PhotoScroll ,在頂部顯示圖像,在下面添加評論區。取決于評論有多長,文字可能會超過裝置的顯示區域:讓滾動視圖來拯救你吧!
注意:一般來說,自動布局将視圖的上下左右邊界作為可視邊界。但是, <code>UIScrollView</code> 通過修改邊界區域來滾動内容。為了和自動布局一起使用,滾動視圖的邊界事實上指的是内容視圖的邊界。
為了在自動布局中設定滾動視圖的邊框大小,要麼根據滾動視圖的寬高顯式指定限制,要麼滾動視圖邊界必須貼合自身子樹外側的視圖。
你會在實踐中學到,如果使用 Storyboard 的自動布局來修複滾動視圖的寬度,或是内容的真實寬度。
打開 Main.storyboard ,建立一個場景:
首先,添加一個新的 View Controller 。在 Size Inspector 中,将 Simulated Size 的 Fixed 替換為 Freeform ,并輸入寬度 340 、高度 800 。你會注意到控制器的布局變得更窄更長了,模拟長條形的豎直内容的行為。模拟尺寸幫助你在 Interface Builder 中可視化顯示效果。它不會影響運作時的效果。
在建立的視圖控制器中的 Attribute Inspector 中取消選中 Adjust Scroll View Insets 。
添加一個滾動視圖,填充整個視圖控制器的空間。在視圖管理器中添加首尾限制為常數 0 (确認取消選中了 Constrain to margin )。将 Scroll View 中的頂部和底部限制分别添加到頂部和底部布局向導。它們的值應該也是常數0。
添加一個 Scroll View 的子視圖,填充 Scroll View 所有的空間。将它的 Storyboard Label 重命名為 Container View 。和以前一樣,添加頂部、底部、前後限制。
為了定義滾動視圖的大小,并修複自動布局的錯誤,你需要定義它的内容大小。定義 Container View 的寬度貼合視圖控制器。将 View Controller 主視圖的寬度限制設定與 Container View 一緻。将 Container View 的高度限制設定為 500 。
注意:自動布局的規則必須完備地定義滾動視圖的 <code>contentSize</code> 。這是在自動布局下讓滾動視圖正确顯示大小的關鍵一步。
在 Container View 内添加一個 Image View 。在 Attribute Inspector 中:将圖像指定為 photo1 ,選擇 Aspect Fit 模式,選中 Clip Subviews 。像之前一樣給 Container View 添加頂部、首尾限制。為圖檔視圖添加高度限制為 300 。
在 Container View 中的圖檔下方添加一個 Label 。指定文字為“ What name fits me best? ”。在 Container View 中添加一個水準居中的寬度限制。添加與 Photo View 的豎直間距限制為 0 。
在 Container View 内建立的标簽下方添加一個 Text Field 。在 Container View 中添加值為 8 的首尾限制,無外邊距。添加與标簽的豎直間距限制為30。
最後,通過聯線 (segue) 連接配接建立的視圖控制器和另一個螢幕。移除已有的 Photo Scroll 場景和 Zoomed Photo View Controller 場景之間的push聯線。不要擔心,你在 Zoomed Photo View Controller 中所做的會在後面加回到應用中。
在 Photo Scroll 場景中,将 PhotoCell 拖到視圖控制器中,添加一個 show 聯線. 命名為 showPhotoPage 。
你可以看到布局在豎直方向是正确的。試着将手機水準旋轉。在水準模式下,沒有足夠的豎直空間來顯示所有内容,盡管滾動視圖使你能夠滾動檢視标簽和文本框。不幸的是,因為新的視圖控制器中的圖檔被寫死在代碼裡,顯示的并不是你在合輯視圖中選中的那張圖檔。
為了修複這個問題,你需要在聯線被執行時将圖檔傳送到視圖控制器。是以,建立一個新的檔案,使用 iOS\Source\Cocoa Touch Class 模闆。将類命名為 PhotoCommentViewController ,将子類設定為 UIViewController 。确認語言設為了Swift 。點選下一步,儲存以備後用。
用下面的代碼更新 PhotoCommentViewController.swift :
更新後的 <code>PhotoCommentViewController</code> 實作添加了 <code>IBOutlet</code> ,并根據 <code>photoName</code> 設定 <code>imageView</code> 的圖檔。
回到 Storyboard ,打開 View Controller 中的 Identity Inspector ,将 Class 設定為 PhotoCommentViewController 。打開 Connections Inspector ,連接配接 <code>PhotoCommentViewController</code> 中滾動視圖、圖像、文本框的 IBOutlet 。
打開 CollectionViewController.swift ,将 <code>prepareForSegue(_:sender:)</code> 替換為下面的代碼:
當你輕按一張圖檔時,這張圖檔的名稱會被顯示在 <code>PhotoCommentViewController</code> 。
内容優雅地顯示在了視圖中,必要時允許你向下滾動檢視更多内容。你會注意到鍵盤帶來的兩個問題:首先,輸入文字時,鍵盤遮住了文本框。其次,鍵盤無法隐藏。怎麼辦呢?
鍵盤偏移量
和使用 <code>UITableViewController</code> 不同,前者會将内容移出螢幕鍵盤遮擋的區域,而使用 <code>UIScrollView</code> 時,你需要自己處理鍵盤的顯示。
視圖控制器可以通過監聽 iOS 發送的 <code>NSNotifications</code> 來獲知鍵盤呼出,進而調整内容。通知包含了一組幾何和動畫參數,用于将内容絲滑地移出鍵盤區域。 你首先要更新代碼來監聽這些通知。打開 PhotoCommmentViewController.swift ,在<code>viewDidLoad()</code> 底部添加這些代碼:
當視圖加載後,你會開始監聽通知,獲知鍵盤出現或消失。
接下來,添加下面的代碼,在對象生命周期結束時停止監聽通知:
接下來在視圖控制器中添加下面的方法:
<code>adjustInsetForKeyboardShow(_:,notification:)</code> 接受推送到的通知中的鍵盤高度,從滾動視圖的 <code>contentInset</code> 中加上或減去 20 的内間距。這樣, <code>UIScrollView</code> 就會向上或向下滾動,使 <code>UITextField</code> 總是在螢幕上可見。
當通知被觸發時, <code>keyboardWillShow(_:)</code> 或 <code>keyboardWillHide(_:)</code> 之一會被調用。這些方法會接着調用<code>adjustInsetForKeyboardShow(_:,notification:)</code> ,訓示視圖滾動的方向。
隐藏鍵盤
為了隐藏鍵盤,将這個方法加到 <code>PhotoCommentViewController.swift</code> 中去:
這個方法會取消文本框的第一響應對象狀态,随之關閉鍵盤。
最後,打開 Main.storyboard 。從 Object Library 拖一個 Tap Gesture Recognizer 到根視圖下。接下來,将它和 Photo Comment View Controller 中的 <code>hideKeyboard(_:) IBAction</code> 連接配接起來。
<a href="https://camo.githubusercontent.com/0d377a05df8df040707558759b17872dfc1c9122/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30312f76697369742d322e676966" target="_blank"></a>
按下文本框,然後按下螢幕其他區域。鍵盤應該根據螢幕内容正确地顯示或隐藏。
在這份 <code>UIScrollView</code> 教程的第三部分,你将要建立一個允許連播的滾動視圖。這意味着在你停止滑動時,滾動視圖會鎖定在一頁上。當你在 App Store 檢視應用截圖時,你會看到這個操作。
__添加 UIPageViewController __
回到 Main.storyboard ,從對象庫面闆拖一個 Page View Controller 。打開 Identifier Inspector , Storyboard ID 輸入PageViewController ,在 Attribute Inspector 中, Transition Style 預設設為 Page Curl ;改為 Scroll 并将 Page Spacing設為 8 。
在 Photo Comment View Controller 場景的 Identity Inspector 中,指定 Storyboard ID 為PhotoCommentViewController ,然後你可以在代碼中引用它。
打開 PhotoCommentViewController.swift ,然後添加:
它會引用即将顯示的圖像的編号,将會用在頁面視圖控制器中。
使用 iOS\Source\Cocoa Touch Class 模闆建立一個新檔案。将類命名為 ManagePageViewController ,子類為UIPageViewController 。确認語言設為 Swift 。點選 Next 以備後用。
打開 ManagePageViewController.swift ,用下面的代碼替換檔案内容:
這段代碼做了這兩件微小的事情:
<code>viewPhotoCommentController(_:_)</code> 通過Storyboard建立了 <code>PhotoCommentViewController</code> 的一個執行個體。你将圖像的名字作為參數傳遞,這樣視圖中顯示的圖檔和前一屏中選中的會是同一張。
通過傳入一個數組,包含剛建立的各個視圖控制器,你完成了 <code>UIPageViewController</code> 的設定。
你會發現Xcode報了一個錯,提示 <code>delegate</code> 的值不能被設為 <code>self</code> 。這是因為現在 <code>ManagePageViewController</code> 還沒有遵從<code>UIPageViewControllerDataSource</code> 。在 ManagePageViewController.swift 中, <code>ManagePageViewController</code> 定義外添加下面的代碼:
<code>UIPageViewControllerDataSource</code> 允許你在頁面變化時提供内容。你提供了視圖控制器的執行個體,實作向前和向後的分頁。在這兩者情況中, <code>photoIndex</code> 用來決定目前顯示的圖像(傳給兩個方法的 <code>viewController</code> 訓示目前顯示的視圖控制器)。新的控制器根據 <code>photoIndex</code> 建立并傳回。
為了讓分頁視圖生效,還需要做一些事情。首先,你将要修複應用流。回到 Main.storyboard ,選擇你剛建立的 Page View Controller 視圖。然後,在 Identity Inspector 中,将類指定為 ManagePageViewController 。删除你之前建立的 push 聯線 showPhotoPage 。按住 Control 将 Scroll View Controller 中的 Photo Cell 連接配接到 Manage Page View Controller 場景下,選擇 Show 聯線。在聯線的 Attributes Inspector 中,指定名稱為 showPhotoPage 。
打開 CollectionViewController.swift ,将 <code>prepareForSegue(_:sender:)</code> 的實作修改為:
<a href="https://camo.githubusercontent.com/9a3c5da7ad1c9f25f856a38145aad7536bc34249/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30312f76697369742d332e676966" target="_blank"></a>
你現在可以通過水準滑動切換不同的詳情視圖。:]
顯示PageControl訓示
在這份 <code>UIScrollView</code> 教程的最後一節中,你将會為應用添加一個 <code>UIPageControl</code> 。
<code>UIPageViewController</code> 可以自動提供一個 <code>UIPageControl</code> 。為了這樣做,你的<code>UIPageViewController</code> 必須擁有一個<code>UIPageViewControllerTransitionStyleScroll</code> 的過渡樣式,而且你必須提供 <code>UIPageViewControllerDataSource</code> 兩個特殊方法的實作(如果你還記得的話,你已經在 Storyboard 中将 Transition Style 設為 Scroll )在ManagePageViewController.swift 中為 <code>UIPageViewControllerDataSource</code> 擴充添加這些方法:
在第一個方法中,你指定了頁面視圖控制器中顯示頁面的編号。在第二個方法中,你告訴頁面視圖控制器初始應該選擇哪個頁面。
在你實作了需要的代理方法之後,你可以更進一步地自定義 <code>UIAppearance</code> API 。在 AppDelegate.swift ,将<code>application(application: didFinishLaunchingWithOptions:)</code> 替換為:
這段代碼将會自定義 <code>UIPageControl</code> 的顔色。
拼接起來
馬上就大功告成了!最後一步,讓輕按圖檔的時候能傳回縮放視圖。打開 PhotoCommentViewController.swift ,添加下面的代碼:
在 Main.storyboard 中,添加一個從 Photo Comment View Controller 到 Zoomed Photo View Controller 的 Show Detail 聯線。選中這個聯線後,打開 Identifier Inspector ,将 Identifier 設為 zooming 。
選擇 Photo Comment View Controller 中的 Image View ,打開 Attributes Inspector ,選中 User Interaction Enabled。添加一個 Tap Gesture Recognizer ,并連接配接到 <code>openZoomingController(_:)</code> 。
現在,當你輕按 Photo Comment View Controller Scene 中的一張圖檔時,你會被帶到 Zoomed Photo View Controller Scene ,然後可以縮放圖像。
編譯,讓我們運作起來看看最終的效果。
<a href="https://camo.githubusercontent.com/c90a15c0ff677f57420374a44831d5734cf9ef88/687474703a2f2f7777772e72617977656e6465726c6963682e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30312f76697369742d342e676966" target="_blank"></a>
棒!大功告成!你建立了一個山寨的 Photos 應用:一個可以選擇、滑動浏覽的圖像合輯,以及具有縮放圖像的功能。
接下來用這些酷炫的滾動視圖技巧,做一些有趣的應用吧!
如果你遇到了什麼問題或者想要留下回報,請在下面評論區中讨論。
<b>原文釋出時間為:2016年04月20日</b>
<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>