UIScrollView新手教程
在iOS開發中,滾動視圖(UIScrollView)通常用于顯示內容尺寸大于屏幕尺寸的視圖。滾動視圖有以下兩個主要作用:
-
讓用戶可以通過拖拽手勢來觀看想看到的內容
-
讓用戶可以通過捏合手勢來放大或縮小觀看的內容
在iOS應用中常見的表格視圖(UITableView)就繼承自滾動視圖,并因此可以通過上下滾動來顯示更多的內容。
在本篇教程中,我們將討論滾動視圖的諸多方面內容,主要包括:使用純代碼和可視化編程兩種方式來創建一個滾動視圖、實現滾動和縮放功能,以及如何嵌套使用滾動視圖。
使用純代碼方式創建UIScrollView
UIScrollView 同其他視圖一樣,可以通過純代碼和可視化編程兩種方式來創建。在創建之后,只需要少量額外設置就可以讓 UIScrollView 獲得基本的滾動功能。
UIScrollView 也和其他視圖一樣,應該被一個控制器管理或者添加到某個視圖層級中。想要完成滾動功能還需要對 UIScrollView 進行以下兩步設置:
-
必須設置 UIScrollView 的 contentSize 屬性,它提供了 UIScrollView 的內容的大小,也就是可以滾動的區域的大小。
-
必須為 UIScrollView 添加一個或多個用于顯示和滾動的子視圖,這些視圖提供了 UIScrollView 顯示的內容。
你還可以根據應用的具體需求設置 UIScrollView 的一些顯示效果,比如:是否顯示水平和豎直方向的滾動條、滾動的彈性效果、縮放的彈性效果,以及允許的滾動方向等。
接下來我們將在代碼中創建一個 UIScrollView 。在下載的資源文件中打開 ScrollViewDemo 工程。它就是一個簡單的 Single View Application 工程,只不過將 storyboard 中根控制器的類型綁定為自己新建的叫做 ScrollViewController 的控制器,還在項目中添加了一張我們要用到的圖片,圖片名稱為 image.png 。
接下來打開 ScrollViewController.swift 文件,添加如下代碼。
var scrollView: UIScrollView!
var imageView: UIImageView!
按如下代碼所示修改 viewDidLoad() 方法。
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(image: UIImage(named: "image.png"))
scrollView = UIScrollView(frame: view.bounds)
scrollView.backgroundColor = UIColor.blackColor()
scrollView.contentSize = imageView.bounds.size
scrollView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
scrollView.addSubview(imageView)
view.addSubview(scrollView)
}
上述代碼創建了一個 UIScrollView 和 UIImageView , UIImageView 被設置為 UIScrollView 的子視圖。 contentSize 屬性控制滾動區域的大小,我們將它設置為跟圖片的尺寸一樣大(2000×1500)。我們將滾動視圖的背景色設置為黑色,這樣圖片就像在一塊黑色幕布上滾動一樣。我們將滾動視圖的 autoresizingMask 屬性設置為 .FlexibleWidth 和 .FlexibleHeight ,使它能夠在設備旋轉之后自動適應新的寬度和高度。運行當前應用,你已經可以通過拖拽手勢來滾動顯示圖片了。
當你啟動應用后,你會發現圖片初始顯示區域是它左上角的部分。
這是因為滾動視圖的 bounds 的起點默認為(0, 0),代表了左上角。如果你想改變啟動后顯示的位置,你需要更改滾動視圖的 bounds 的起點。因為這種需求經常被提起,所以 UIScrollView 專門提供了一個屬性 contentOffset 用來實現這種需求。
在代碼中添加如下語句,注意添加在設置autoresizingMask語句之后。
scrollView.contentOffset = CGPoint(x: 1000, y: 450)
重新運行應用,你會發現一開始就會顯示圖片的另一部分而不是左上角。你可以通過這種方式來決定程序啟動后將要顯示的內容。
縮放
我們已經添加了一個 UIScrollView ,并且能夠讓用戶通過拖拽來觀看尺寸大于屏幕尺寸的內容。相當棒,但如果視圖能夠縮放的話會帶來更好的體驗。
要支持縮放功能,你必須為 UIScrollView 設置一個代理,而且代理必須遵守 UIScrollViewDelegate 協議,代理還需要實現 viewForZoomingInScrollView() 方法,該方法返回想要被縮放的視圖。
你還應該為縮放設置一個比例,可以通過 UIScrollView 的 minimumZoomScale 和 maximumZoomScale 這兩個屬性來實現,它們的默認值都是1.0。
按照如下代碼更改 ScrollViewController 的定義:
class ScrollViewController: UIViewController, UIScrollViewDelegate {
然后添加如下代碼:
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
接下來在 viewDidLoad() 方法的最后添加如下代碼:
scrollView.delegate = self
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 4.0
scrollView.zoomScale = 1.0
在上述代碼中,我們設置了 zoomScale 為1.0,然后設置了縮放的最大和最小比例。在程序運行后,會按照圖片的原始尺寸顯示(因為 zonmScale 為1.0),當你使用捏合手勢來操作圖片時,你會發現圖片可以被縮放了。我們設置了 maximumZoomScale 為4.0,所以圖片最大只能放大到4倍。你也會發現,圖片放大4倍后會變得很模糊,所以接下來我們會把它的縮放比例重新設置為1.0。
從上面的圖片中我們可以發現,我們之前將 minimumZoomScale 設置為0.1實在是太小了,屏幕空出了很多空閑的地方。在橫屏模式下,空閑的區域看上去更大。我們希望圖片能在某一方向上能與屏幕相匹配,讓圖片既能完全顯示,又能盡量減少屏幕的空閑空間。
要達到這樣的效果,你必須通過圖片尺寸和 UIScrollView 的尺寸來計算最小的縮放比例。
首先在 viewDidLoad() 方法中刪除以下三行代碼:
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 4.0
scrollView.zoomScale = 1.0
在控制器類中添加如下方法。在方法中,我們算出圖片同 UIScrollView 的高度和寬度的比值,并將最小縮放比例設置為兩者中更小的那個。注意,我們已經刪除了 maximumZoomScale 的設置,所以它的默認值為1.0。
func setZoomScale() {
let imageViewSize = imageView.bounds.size
let scrollViewSize = scrollView.bounds.size
let widthScale = scrollViewSize.width / imageViewSize.width
let heightScale = scrollViewSize.height / imageViewSize.height
scrollView.minimumZoomScale = min(widthScale, heightScale)
scrollView.zoomScale = 1.0
}
在 viewDidLoad() 方法最后調用這個方法:
setZoomScale()
在 viewWillLayoutSubviews() 方法中也需要調用該方法,這樣當用戶改變屏幕方向后,圖片的尺寸仍然是正確的。
override func viewWillLayoutSubviews() {
setZoomScale()
}
運行程序,現在你會發現無論你縮放到多小,圖片都會完整顯示并且盡量占滿剩余的空間。
我們可以發現,圖片是被定位在屏幕左上角的,我們希望將它放在屏幕中間。
在代碼中添加如下方法。
func scrollViewDidZoom(scrollView: UIScrollView) {
let imageViewSize = imageView.frame.size
let scrollViewSize = scrollView.bounds.size
let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0
let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0
scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
}
這個方法在縮放的時候就會被調用,它會通知代理 UIScrollView 的縮放比例發生改變了。在上面的方法中,我們計算了圖片在滾動視圖中的內間距,從而使圖片始終在屏幕的中間。對于上、下方向的內邊距,我們首先判斷圖片視圖的高度是否小于滾動視圖的高度,如果是就將邊距設為兩者的差值的一半,否則設為0。水平間距我們采用同樣的方式計算。然后通過 contentInset 屬性設置所有方向的內邊距,這個屬性代表了 UIScrollView 的內容距離 UIScrollView 本身四周的距離。
運行程序,你會發現當你縮小圖片時,圖片始終保持在屏幕的中間。
通過雙擊來縮放
UIScrollView 默認只支持通過捏合手勢來實現縮放效果,如果想實現通過雙擊來縮放,則需要自己做些額外的設置。
iOS人機界面指南 中介紹了可以通過雙擊手勢來達到縮放的效果。使用雙擊手勢進行縮放需要一定的前提:要縮放的視圖只能在最大和最小比例兩個固定值之間來回縮放,就像蘋果官方的相冊應用一樣,當你雙擊圖片時,圖片放大至最大,當你再次雙擊時,圖片縮小至最小,或者可以通過連續的雙擊使視圖一點點達到最大,然后再次雙擊的時候,將視圖恢復為全屏顯示。但是大多數應用需要實現更靈活的雙擊縮放效果,例如地圖應用,當你雙擊時會使其放大,繼續雙擊會繼續放大,想要縮小則可以使用雙指捏合手勢來實現。
要想在你的程序中實現雙擊縮放功能,你需要監聽 UIScrollView 的手勢并進行處理。在我們的程序中,我們將模仿蘋果官方的相冊應用的效果,當你雙擊時放大到最大值,再次雙擊時則縮小到最小值。
在代碼中添加如下兩個方法。
func setGestureRecognizer() {
let doubleTap = UITapGestureRecognizer(target: self, action: "handleDoubleTap:")
doubleTap.numberOfTapsRequired = 2
scrollView.addGestureRecognizer(doubleTap)
}
func handleDoubleTap(recognizer: UITapGestureRecognizer) {
if (scrollView.zoomScale > scrollView.minimumZoomScale) {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
} else {
scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
}
}
然后在 viewDidLoad() 方法最后調用上面的方法。
setGestureRecognizer()
在上面的代碼中,我們為 UIScrollView 添加了一個雙擊手勢的監聽,然后根據圖片當前的縮放比例,來判斷是將圖片放大或者縮小。
運行程序,你會發現已經能通過雙擊手勢來縮放圖片了。
用可視化編程方式創建UIScrollView
使用 storyboard 可以實現和我們上面使用代碼方式實現的同樣的功能,而且更為簡單,代碼量更少。
在 Main.storyboard 文件中,拖一個新的視圖控制器,并將其設置為初始控制器(既可以將箭頭拖到新控制器上,也可以在屬性選項卡中選中 Is Initial View Controller 復選框)。
拖一個 UIScrollView 到新的控制器中,然后設置其邊緣始終粘著屏幕。
然后拖一個 UIImageView 到剛才的 UIScrollView 中,將它的邊緣設置為粘著 UIScrollView 。
要記住 UIScrollView 需要知道它的內容的大小,才可以實現滾動。當你為 UIImageView 設置圖片時, UIScrollView 的內容大小就會被自動設置為圖片的大小。
在 UIImageView 的屬性選項卡中,將 Image 屬性設置為 image.png ,然后通過 updating the frames 解決自動布局的問題。運行程序,你會發現已經實現圖片的滾動顯示了,并且沒有敲一行代碼。你還可以在 UIScrollView 的屬性選項卡中查看還有哪些屬性可以設置,比如可以設置最大和最小的縮放比例。
如果想要實現縮放功能,你仍然需要通過代碼,設置代理并實現 viewForZoomingInScrollView() 方法,同我們之前做過的一樣,就不再重復一遍了。
UIScrollView的嵌套使用
可以在一個 UIScrollView 中嵌套另一個 UIScrollView ,兩個 UIScrollView 既可以是相同方向滾動的,也可以是不同方向的。這部分內容的示例代碼請使用 NestedScrollViews 項目。
相同方向的UIScrollView嵌套
相同方向的 UIScrollView 嵌套是指一個 UIScrollView ,它有另一個 UIScrollView 作為子控件,并且它們的滾動方向一致。你可以用相同方向的嵌套來實現這樣的效果,比如在 UIScrollView 中添加多組要區分開的數據,你還可以通過它來實現兩個 UIScrollView 同時滾動時的視差效果。在我們的示例中,我們將兩個相同方向的 UIScrollView 設置不同的滾動速度,從而實現滾動時的視差效果。
打開 NestedScrollViews 項目中的 storyboard 文件,你將看到兩個 UIScrollView ,分別叫做foreground和background。background里面添加了一個 UIImageView ,并將圖片設置為 image.png ,foreground里面添加了一些標簽和一個作為容器用的 UIVIew ,這些標簽只是為了方便我們觀看視圖的滾動,容器視圖我們將在下一節內容中才用到。
我們的界面這樣就算搭建完成了,現在運行程序的話,你會發現只有foreground視圖在滾動,而background視圖保持不動。接下來我們將要實現background的滾動,并且實現滾動的視差效果。
首先將foreground和background兩個 UIScrollView 連線到控制器,之后代碼會如下所示:
@IBOutlet weak var background: UIScrollView!
@IBOutlet weak var foreground: UIScrollView!
我們需要知道foreground視圖滾動了多長的距離,用來計算background視圖需要滾動多長的距離。所以我們需要為foreground視圖設置一個代理,用來監聽它的滾動。
class ViewController: UIViewController, UIScrollViewDelegate {
在 viewDidLoad() 方法中設置foreground視圖的代理。
foreground.delegate = self
然后實現如下代理方法。
func scrollViewDidScroll(scrollView: UIScrollView) {
let foregroundHeight = foreground.contentSize.height - CGRectGetHeight(foreground.bounds)
let percentageScroll = foreground.contentOffset.y / foregroundHeight
let backgroundHeight = background.contentSize.height - CGRectGetHeight(background.bounds)
background.contentOffset = CGPoint(x: 0, y: backgroundHeight * percentageScroll)
}
在上面的代碼中,我們獲取了foreground視圖可以滾動的最大高度,然后用當前滾動的距離除以它以獲取滾動的比例,然后獲取background視圖可以滾動的最大高度,將其乘以滾動比例,就可以得到background應該滾動的距離。運行你的程序,在進行滾動時你會發現foreground和background兩個視圖都在滾動,并且background視圖滾動的更快,從而有一種視差效果。
交叉方向的UIScrollView嵌套
交叉方向的 UIScrollView 嵌套是指一個 UIScrollView ,它有另一個 UIScrollView 作為子控件,并且它們的滾動方向正好相差90°,接下來我們就演示一下這種情況。
在 NestedScrollViews 項目中,你會發現在foreground里面有一個 Container View ,我們將用它來設置我們水平滾動的 UIScrollView 。
在 storyboard 中新拖入一個控制器,按住Control鍵從 Container View 拖到新的控制器,選擇 embed 方式。然后選中這個控制器,將它的Size選項設為 Freeform 并將其高度設為128,因為 Container View 的高度就是128。
往新控制器中拖入一個 UIScrollView ,設置其邊緣始終粘著父控件。然后在 UIScrollView 中拖入一個70×70的 UIView ,將其背景色設為灰色方便我們觀看,然后復制多個,從左到右依次擺放在 UIScrollView 中。你不需要精確地去設置每一個 UIView 的位置,接下來我會教你們怎么去做。現在我們的控制器界面應該是這個樣子。
選擇最左邊的 UIView ,添加它上邊和左邊的約束,再添加寬度和高度約束。
再選擇最右邊的 UIView ,添加它的上邊、右邊、寬度和高度約束。
接下來,選中我們的 UIScrollView ,然后點擊上面菜單欄中的 Editor > Resolve Auto Layout Issues > All Views > Add Missing Constraints 。這樣我們所有的 UIView 就都添加好了約束。運行你的程序,豎直滾動到底部,你會看見我們的 Container View ,你可以水平滾動它里面的內容。下圖中,我將控制器自身視圖的背景色設置為透明,所以你看到的效果就是這樣的。
我們的教程這就結束了,并沒有包含 UIScrollView 所有的方方面面,但我希望通過這篇教程可以讓你對 UIScrollView 有初步的了解,更多 UIScrollView 的知識
來自:http://wxgbridgeq.github.io/blog/2015/06/19/uiscrollview-tutorial/