UIScrollView PagingEnabled的轉屏問題

weixin_33866037發表於2016-10-16

應用支援使用者轉動手機後,UI會跟隨轉動,此時就需要對我們的頁面設定轉屏支援;如果介面中包含UIScrollView(此時要求UIScrollView的width和height是不相等的,看完本文就明白),並且UIScrollView isPagingEnable為true時,在轉屏後,UIScrollView的page滑動就會出現問題(無法滑動到正確的頁,一般情況下會滑走多個頁),如何解決,請繼續看。

1. iOS的轉屏回撥

iOS在UIController中提供了屏轉的回撥,在iOS 8.0開始使用新的版本,回撥方法的宣告如下:

// iOS 8.0 以前
func willRotateToInterfaceOrientation(_ toInterfaceOrientation: UIInterfaceOrientation, duration duration: NSTimeInterval)
func didRotateFromInterfaceOrientation(_ fromInterfaceOrientation: UIInterfaceOrientation)

// iOS 8.0 及 更高版本
func viewWillTransitionToSize(_ size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

經過測試發現,在iOS 8.0及更高版本里面,系統會嘗試先回撥viewWillTransitionToSize,如果沒有重寫,則會嘗試回撥willRotateToInterfaceOrientation

另外,我們還可以監聽如下通知獲得轉屏感知:

UIApplicationWillChangeStatusBarOrientationNotification
UIApplicationDidChangeStatusBarOrientationNotification

2. UIScrollView的轉屏設定

UIScrollView開啟pagingEnabled,scrollView每次滑動的寬度是scrollView的width的長度,轉屏之後,scrollView滑動的寬度變化了,但是scrollView的subviews的frame和scrollView的contentSize並沒有變化,這就是出現問題的原因。

解決問題的方法很簡單,只需要在轉屏時,把scrollView的subviews的frame和scrollView的contentSize設定正確就ok了,參考程式碼如下(<i>剛開始在公開場合使用Swift,不地道的地方請各位指出~~</i>):

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    
    var count = 0
    for view in scrollView.subviews {
        
        var frame = view.frame
        
        // subview的frame修正成轉屏後,一屏的大小
        frame.origin.x = CGFloat.init(integerLiteral: count) * size.width
        frame.size.width = size.width
        frame.size.height = size.height
        
        view.frame = frame
        
        count += 1
    }
    
    scrollView.contentSize = CGSize.init(width: CGFloat.init(integerLiteral: count) * size.width, height: size.height)
    
}

此時,再對UIScrollView進行轉屏,已經不會出現滑動錯頁的問題;回到本文開始的一個問題,如果scrollView的width和height是相等的,那麼,轉屏前後,scrollView的contentSize和subviews的frame實際不會發生變化,所以這種情況下轉屏是不會有問題的。

我們會發現,轉屏後有可能顯示的不再是之前展示的那頁,所以我們還需要使用setContentOffset來讓正確的頁顯示出來,程式碼如下:

// 滑動到當前page的顯示位置
var offset = scrollView.contentOffset
offset.x = CGFloat.init(integerLiteral: (Int)(offset.x / scrollView.frame.width)) * size.width
scrollView.setContentOffset(offset, animated: false)

3. UIScrollView的其它問題

如果UIScrollView顯示了scrollIndicator,則scrollView的subviews會多出1個或者2個UIImageView物件,這2個物件就是scrollIndicator。

可以使用下面方法,隱藏scrollIndicator

scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false

或者,在遍歷subviews時排除掉

for view in scrollView.subviews {
    
    if !view.isKind(of: YourClass.self) {
        continue
    }
    
    // 修正subview的frame
    
}

排除的方法還有一種,可以在新增subview的時候為view設定tag,在for中就可以根據tag來判斷該view是否為我們新增的view了。


相關文章