2016-12-01 18 views
6

我有一個應用程序,它有一個動態生成的視圖,每次視圖加載時都可能會有所不同。主視圖包含一個設置爲主視圖邊界的ScrollView。然後子視圖會動態添加到ScrollView中,但這些視圖的高度不會相同,並且可以隨時更改高度(例如,用戶單擊視圖內容並且它會更改)。我想使用佈局約束來確保每個視圖都保持與其上面的視圖保持一致,並使用一些任意的填充。見下圖:使用NSLayoutConstraints在SwiftView中垂直對齊動態視圖使用Swift

needed layout

現在所有的填充值設置爲10(上,左,右)。我使用框架手動設置這些子視圖的位置,但如果視圖更改大小,這不起作用,所以我想將其更改爲使用NSLayoutConstraints,但我遇到了一些問題。

作爲一個測試,我設置的子視圖的框架再像以前那樣,但後來我加了約束:

// newView is created and its frame is initialized 
self.scrlView?.addSubview(newView) 
let constr = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.previousView, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10) 
NSLayoutConstraints.activateConstraints([constr]) 
newView.translatesAutoResizingMaskIntoConstraints = false 
self.previousView = newView 

但是意見是無處可看。我究竟做錯了什麼?所有需要的是確保每個視圖的頂部與先前視圖對齊,並且無論視圖高度如何,它們都保持這種方式。

此外,由於這些視圖都添加到滾動視圖,使用上面的佈局約束如何設置滾動視圖的正確內容大小?

+0

我可以看到頂部約束,但newView的前導和尾隨(a.k.a左和右)約束在哪裏? – gwinyai

+0

@gwinyai他們需要嗎?真的,我所關心的是頂級路線。 –

+0

我現在的假設是滾動視圖的內容視圖不會調整以適合您的子視圖。你可以1.手動調整內容大小,如下所示self.scrlView?.contentSize = CGSize(width:view.frame.width,height:view.frame.height)或者2.給子視圖至少設置一個頂部和左邊的約束。 – gwinyai

回答

5

所以,你想是這樣的:

demo

設置與自動版式滾動視圖中Technical Note TN2154: UIScrollView And Autolayout解釋的內容大小。總而言之,自動佈局基於滾動視圖和其後代視圖之間的約束來設置滾動視圖的contentSize。這意味着:

  • 您需要滾動視圖和它的後代意見,使滾動視圖的contentSize將正確設置之間創建約束。

  • 您需要在圖塊(圖片中的彩色視圖)和主視圖(它是滾動視圖的超級視圖)之間創建約束以正確設置圖塊的寬度。從瓦片到其包含的滾動視圖的約束不能設置瓦片的大小 - 僅滾動視圖的contentSize

對於在代碼中創建,如果你打算使用約束來控制其大小或位置的任何觀點,您還需要設置translatesAutoresizingMaskIntoConstraints = false。否則,自動調整掩碼將會干擾你顯式約束的行爲。

下面是我做演示的過程。我用UIButton子類,輕敲後,切換60個120點之間自身的高度:

class HeightTogglingButton: UIButton { 

    override init(frame: CGRect) { 
     super.init(frame: frame) 

     translatesAutoresizingMaskIntoConstraints = false 
     heightConstraint = heightAnchor.constraint(equalToConstant: 60) 
     heightConstraint.isActive = true 
     addTarget(self, action: #selector(HeightTogglingButton.toggle(_:)), for: .touchUpInside) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    @IBAction func toggle(_ sender: Any) { 
     UIView.animate(withDuration: 0.3) { 
      self.heightConstraint.constant = 180 - self.heightConstraint.constant 
      self.window?.layoutIfNeeded() 
     } 
    } 

    private(set) var heightConstraint: NSLayoutConstraint! 
} 

然後我佈置這些按鈕10在滾動視圖。我設置了約束來控制每個按鈕的寬度,以及每個按鈕相對於其他按鈕的佈局。頂部和底部按鈕限制在滾動視圖的頂部和底部,因此它們控制着scrollView.contentSize.height。所有按鈕都限制爲滾動視圖的前端和後端,因此它們全部共同控制scrollView.contentSize.width

class ViewController: UIViewController { 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     let scrollView = UIScrollView() 
     scrollView.translatesAutoresizingMaskIntoConstraints = false 
     view.addSubview(scrollView) 
     NSLayoutConstraint.activate([ 
      scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 
      scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 
      scrollView.topAnchor.constraint(equalTo: view.topAnchor), 
      scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)]) 

     let margin: CGFloat = 10 

     var priorAnchor = scrollView.topAnchor 
     for i in 0 ..< 10 { 
      let button = HeightTogglingButton() 
      button.backgroundColor = UIColor(hue: CGFloat(i)/10, saturation: 0.8, brightness: 0.3, alpha: 1) 
      button.setTitle("Button \(i)", for: .normal) 
      scrollView.addSubview(button) 
      NSLayoutConstraint.activate([ 
       button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -2 * margin), 
       button.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: margin), 
       button.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -margin), 
       button.topAnchor.constraint(equalTo: priorAnchor, constant: margin)]) 
      priorAnchor = button.bottomAnchor 
     } 

     scrollView.bottomAnchor.constraint(equalTo: priorAnchor, constant: margin).isActive = true 
    } 

} 
+0

優秀的答案。在我嘗試實現這一點之前,我清楚地知道,在scollview中還有其他子視圖,我沒有爲其分配約束,這就是爲什麼我分配約束的視圖不會顯示在視圖上?此外,我添加的子視圖是也包含子視圖的自定義視圖,這些還需要約束,還是我可以離開那些以前放置過的位置?謝謝! –

+1

如果您想通過設置其框架直接定位子視圖,那沒關係。只要避免在其上創建任何明確的約束。 –

+0

這似乎適用於不調整大小的項目,但只要視圖調整大小,它就不會調整其他視圖,並且它們保持在最後位置。我只是將你建議的約束添加到子視圖中(沒有子視圖的子視圖被改變,這些都是用框架調整的)。我使用的是Swift 2,所以我不得不對代碼做一些修改,但是我不明白爲什麼這會是一個問題。 (例如使用activateConstraints()和constraintEqualToAnchor())。對於調整大小的示例,我在設置新框架高度後使用self.heightAnchor.constraintEqualToConstant(self.frame.height).active = true –