2016-02-05 106 views
11

當我試圖做一個UIScrollView只允許在水平方向上放大,使變焦的UIScrollView只在一個方向。滾動視圖使用純自動佈局方法進行設置。通常的方法是如在數堆棧溢出的答案(例如this one)和other資源建議。在自動佈局存在之前,我已經在應用程序中成功使用了它。該方法如下:在滾動視圖的內容視圖,我重寫setTransform(),並修改傳入變換到上規模在x方向:如何使用自動佈局

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     var t = newValue 
     t.d = 1.0 
     super.transform = t 
    } 
} 

我還重置滾動視圖的內容偏移,使得它不不使用自動佈局時

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
} 

這工作非常漂亮:「T變焦時垂直滾動

Zooming without autolayout

然而,在使用自動佈局時,內容偏移變焦過程中結束了錯。而在水平方向上的內容視圖只,它垂直移動:

Zooming with autolayout

我已經把一個例子項目on GitHub(用來在這個問題上的視頻)。它包含兩個故事板,Main.storyboard和NoAutolayout.storyboard,這是除了Main.storyboard相同的自動版式已開啓,同時NoAutolayout沒有。通過更改主界面項目設置在它們之間切換,您可以在兩種情況下看到行爲。

我真的寧願不關閉自動佈局,因爲它解決了以比手動佈局所需更好的方式實現我的用戶界面的許多問題。有沒有辦法在使用自動佈局進行縮放時保持垂直內容偏移正確(即爲零)?

編輯:我已經添加了第三個故事板AutolayoutVariableColumns.storyboard,到示例項目。這會添加一個文本字段來更改GridView中的列數,這會更改其內在內容大小。這更接近地顯示了我在真實應用程序中需要的行爲,這引發了這個問題。

回答

8

想我想通了。您需要在變換中應用翻譯來抵消UIScrollView在縮放時嘗試執行的居中操作。

添加到您的GridView:

var unzoomedViewHeight: CGFloat? 
override func layoutSubviews() { 
    super.layoutSubviews() 
    unzoomedViewHeight = frame.size.height 
} 

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     if let unzoomedViewHeight = unzoomedViewHeight { 
      var t = newValue 
      t.d = 1.0 
      t.ty = (1.0 - t.a) * unzoomedViewHeight/2 
      super.transform = t 
     } 
    } 
} 

要計算的變換,我們需要知道視圖的未縮放高度。在這裏,我只是在layoutSubviews()期間抓住了框架的大小,並假設它包含了unzoomed的高度。可能有些邊緣案例不正確;可能在視圖更新週期中更好地計算高度,但這是基本思路。

+0

謝謝安娜,這是一個有趣的方法,似乎它可以運作良好!我會給它一個機會,並會回報。 –

+0

一個快速測試似乎表明,這將工作。我會在明天嘗試將它整合到我的實際項目中。再次感謝! –

+0

當然!讓我知道你是否調整了它 - 我試圖解決同樣的問題。 (並確保你接受我的答案;-) –

0

我不確定這是否滿足您的100%自動佈局要求,但這裏有一個解決方案,其中UIScrollView設置了自動佈局,gridView作爲子視圖添加到它。

ViewController.swift應該是這樣的:

// Add an outlet for the scrollView in interface builder. 
@IBOutlet weak var scrollView: UIScrollView! 

// Remove the outlet and view for gridView. 
//@IBOutlet var gridView: GridView! 

// Create the gridView here: 
var gridView: GridView! 

// Setup some views 
override func viewDidLoad() { 
    super.viewDidLoad() 
    // The width for the gridView is set to 1000 here, change if needed. 
    gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height)) 
    scrollView.addSubview(gridView) 
    scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height) 
    gridView.contentMode = .ScaleAspectFill 
} 

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { 
    return gridView 
} 

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
} 
+0

謝謝你的好回答。不幸的是,我想使用autolayout的全部原因是「GridView」(實際上並不像我的示例項目中的GridView那麼簡單)的大小根據其內容而變化。 Autolayout對'intrinsicContentSize()'的支持對此非常完美。沒有它,視圖必須手動調整大小,因爲它們的內容更改非常痛苦。我並不介意爲_scroll view_關閉自動佈局,只要它的內容視圖可以通過其固有內容大小調整大小即可。 –

+0

啊,知道了。如果我想出一個在'gridView'上使用autolayout的方法,我會告訴你! – xoudini

3

嘗試設置translatesAutoresizingMaskIntoConstraints

func scrollViewDidZoom(scrollView: UIScrollView) { 
    gridView.translatesAutoresizingMaskIntoConstraints = true 
} 

在它工作的示例項目。如果這還不夠,嘗試在scrollViewDidEndZooming:編程方式創建新的限制可能會有所幫助。

此外,如果這沒有幫助,請更新示例項目,所以我們可以重現具有可變intrinsicContentSize()

問題本文由奧萊Begemann幫了我很多
How I Learned to Stop Worrying and Love Cocoa Auto Layout

WWDC 2015年視頻
Mysteries of Auto Layout, Part 1
Mysteries of Auto Layout, Part 2

幸運的是,這是一個標誌。它被稱爲translatesAutoResizingMask IntoConstraints [沒有空格]。

這是一個有點口,但它幾乎做它說。它使視圖按照傳統佈局系統下的方式運行,但是在自動佈局世界中。

+0

感謝您的回答。我不完全明白爲什麼這會起作用,但我認爲它本質上是一種脆弱的黑客攻擊。如果你在'viewDidLoad()'中開啓了'translatesAutoresizingMaskIntoConstraints',你會立即得到一個衝突的約束異常。在任何情況下,'translatesAutoresizingMaskIntoConstraints'並不意味着用於直接參與自動佈局的視圖,因爲GridView在這裏。我將用變量'intrinsicContentSize()'更新示例項目。 –

+0

我用故事板更新了示例項目,它允許更改網格視圖中的列數,這會影響其內在內容大小。 (我也把這個項目移到了GitHub上)。請注意,通過此更改,您的解決方案會崩潰。如果縮放,然後更改列數,則從autoresize掩碼轉換的寬度約束會導致GridView無法調整大小以適應新的列數。 –

+0

@ Anna的答案比嘗試在傳統和自動佈局系統之間切換要好得多。 –

1

雖然@安娜的解決方案有效,的UIScrollView提供自動佈局的一種工作方式。但由於滾動視圖的作用有點不同於其他視圖,約束不同的解釋太:在邊緣/利潤率滾動視圖的及其內容重視滾動視圖的內容區域之間

  • 約束。
  • 高度寬度,或中心之間約束附着到滾動視圖
  • 滾動視圖和滾動視圖外的視圖之間的約束與普通視圖一樣工作。

所以,當你添加一個子視圖釘在其邊緣/利潤率滾動視圖,即子視圖成爲滾動視圖的內容區域或內容視圖

蘋果提出了以下的方法在Working with Scroll Views

  1. 添加滾動視圖到現場。
  2. 正常情況下,繪製約束來定義滾動視圖的大小和位置。
  3. 將視圖添加到滾動視圖。將視圖的Xcode特定標籤設置爲內容視圖。
  4. 將內容視圖的頂部,底部,前端和後端固定到滾動視圖的相應邊緣。內容視圖現在定義滾動視圖的內容區域 。
  5. (...)
  6. (...)
  7. 鋪陳滾動視圖的內容視圖裏面的內容。正常情況下,使用約束將內容定位到內容視圖中。

在你的情況下,GridView控件必須內容視圖內:

Interface Builder

你可以保持您一直在使用,但現在網格視圖的限制,附加到內容視圖。而對於僅水平縮放,請將代碼保持原樣。你的transform重寫處理得很好。

+0

我明白這一切。問題是關於僅水平_縮放_這是一個完全不同於野獸的動作,而不是僅水平滾動。在啓發這個問題的真實應用中,_scrolling_應該在兩個方向上工作,並且內容視圖的高度是可變的。這只是縮小應該被限制在水平方向。 –

+0

沒問題。水平滾動只是一個猜測。關於水平縮放,你已經解決了它。剩下的就是用自動佈局來解決水平縮放問題,如果沒有安娜的解決方案,你可以做到這一點。我測試過,它工作正常。如果有一點不清楚,請讓我知道,我將編輯帖子。 –

+0

安娜的解決方案非常適合採用純自動佈局方法進行水平縮放。如果你有不同的方法,我會很樂意聽到它。這裏的答案不提縮放。 –

相關問題