2013-05-29 151 views
36

我遇到了使用自動佈局約束的UIScrollView的麻煩。 我有以下視圖層次結構,通過設置IB約束:自動佈局UIScrollView與動態高度的子視圖

- ScrollView (leading, trailing, bottom and top spaces to superview) 
-- ContainerView (leading, trailing, bottom and top spaces to superview) 
--- ViewA (full width, top of superview) 
--- ViewB (full width, below ViewA) 
--- Button (full width, below ViewB) 

的ViewA和ViewB有200點的初始高度,但它可以通過點擊它被垂直地花費在400分的高度。 ViewA和ViewB通過更新高度約束(從200到400)進行擴展。下面是對應的摘錄:

if(self.contentVisible) { 
    heightConstraint.constant -= ContentHeight; 
    // + additional View's internal constraints update to hide additional content 
    self.contentVisible = NO; 
} else { 
    heightConstraint.constant += ContentHeight; 
    // + additional View's internal constraints update to show additional content 
    self.contentVisible = YES; 
} 

[self.view setNeedsUpdateConstraints]; 
[UIView animateWithDuration:.25f animations:^{ 
    [self.view layoutIfNeeded]; 
}]; 

我的問題是,如果這兩種觀點展開,我需要能夠滾動看到全部內容,而現在的滾動不工作。我如何設法使用約束來更新滾動視圖以反映ViewA和ViewB高度的變化?

到目前爲止,我唯一能想到的解決方案是在動畫之後手動設置ContainerView的高度,這將是ViewA + ViewB + Button高度的總和。但我相信有更好的解決方案?

由於

+0

http://stackoverflow.com/questions/13499467/uiscrollview-doesnt-use-autolayout-constraints也非常有幫助 –

回答

57

我使用像下列

-view 
    -scrollView 
    -view A 
    -view B 
    -Button 

純結構確保按鈕(THE LAST視圖)具有(從其底部至上海華,這是滾動型垂直間距)的約束,在這種情況下,無論視圖A和視圖B會發生什麼變化,scrollView的高度都會相應地改變。

我參考了這個偉大的在線book site

只需閱讀「創建滾動視圖」部分,您應該有一個想法。

我有類似的問題,我正在創建一個詳細視圖,並使用界面生成器與自動佈局是非常適合任務!

祝你好運!

(其他資源:。對auto layout for scroll view

堆棧溢出討論

iOS 6中有一個Release Notes在談論UIScrollView的自動佈局支持

免費在線iOS book explanation關於滾動視圖這實際上幫助我很多!

+0

感謝您的資源!計劃的小改動,我的客戶想要iOS 5的支持,所以我不得不關閉自動佈局,但我會在稍後回來。 – lukas

+2

夢幻般的答案。 – Jesse

+1

「確保按鈕(最後一個視圖)有一個約束(垂直間距從底部到超視圖,這是滾動視圖)」< - 我喜歡這部分! – FDIM

7

這是我如何用容器視圖佈局一個純自動佈局UIScrollView的一個例子。我已經評論過讓它成爲c learer:

容器是標準的UIView和身體是一個UITextView

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    //add scrollview 
    [self.view addSubview:self.scrollView]; 

    //add container view 
    [self.scrollView addSubview:self.container]; 

    //body as subview of container (body size is undetermined) 
    [self.container addSubview:self.body]; 

    NSDictionary *views = @{@"scrollView" : self.scrollView, @"container" : self.container, @"body" : self.body}; 
    NSDictionary *metrics = @{@"margin" : @(100)}; 

    //constrain scrollview to superview, pin all edges 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:kNilOptions metrics:metrics views:views]]; 

    //pin all edges of the container view to the scrollview (i've given it a horizonal margin as well for my purposes) 
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[container]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[container]-margin-|" options:kNilOptions metrics:metrics views:views]]; 

    //the container view must have a defined width OR height, here i am constraining it to the frame size of the scrollview, not its bounds 
    //the calculation for constant is so that it's the width of the scrollview minus the margin * 2 
    [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:-([metrics[@"margin"] floatValue] * 2)]]; 

    //now as the body grows vertically it will force the container to grow because it's trailing edge is pinned to the container's bottom edge 
    //it won't grow the width because the container's width is constrained to the scrollview's frame width 
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[body]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[body]|" options:kNilOptions metrics:metrics views:views]]; 
} 

在我的例子「身體」是一個UITextView,但它可能是別的。如果你碰巧在使用UITextView,那麼請注意,爲了使它垂直增長,它必須有一個高度約束,它在viewDidLayoutSubviews中設置。因此,添加以下約束在viewDidLoad中,並保持對它的引用:

self.bodyHeightConstraint = [NSLayoutConstraint constraintWithItem:self.body attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:nil multiplier:1.0f constant:100.0f]; 
[self.container addConstraint:self.bodyHeightConstraint]; 

然後在viewDidLayoutSubviews計算高度和更新約束的常數:

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    [self.bodyHeightConstraint setConstant:[self.body sizeThatFits:CGSizeMake(self.container.width, CGFLOAT_MAX)].height]; 

    [self.view layoutIfNeeded]; 
} 

需要第二佈局傳遞到調整的UITextView 。

0

在任何時候滾動視圖應該知道它的內容大小。內容大小是從滾動視圖的子視圖中推斷出來的。將控制器屬性映射到描述子視圖高度的xib文件中的約束是非常方便的。然後在代碼中(一個動畫塊),你可以改變這些約束屬性的常量。如果您需要更改整個約束,請保留對其的引用,以便稍後在父容器中對其進行更新。

+0

附上一些例子或代碼。用言語表達自己的理解真的很容易。 – Developer

23

比方說,我們有這樣的層次結構(Label1的是內容查看的子視圖;內容查看是滾動型的子視圖,滾動型是視圖控制器的視圖的subiview):

ViewController's View ScrollView ContentView Label1 Label2 Label3

滾動型約束與以正常方式自動佈局到視圖控制器的視圖。

ContentView被固定在頂部/左/右/底部以滾動查看。這意味着您有限制,使ContentView的頂部/底部/前導/尾部邊緣限制爲與ScrollView上的邊緣相同。這裏有一個關鍵:這些約束是針對ScrollView的contentSize,而不是它在視圖控制器視圖中顯示的幀大小。所以它不會告訴ContentView與顯示的ScrollView框架的框架大小相同,它只是告訴Scrollview ContentView是它的內容,所以如果contentview大於ScrollView框架,那麼您會滾動,就像設置scrollView.contentSize更大比scrollView.frame使內容可滾動。

下面是另一個關鍵:現在你必須在ContentView,Label1-3和除了Scrollview以外的其他內容之間有足夠的約束,以便ContentView能夠從這些約束中找出它的寬度和高度。

因此,舉例來說,如果你想有一個垂直滾動組標籤,你設定一個約束,使內容查看寬度等於視圖控制器視圖的寬度,即採用寬度的照顧。爲了照顧高度,將Label1頂部固定到ContentView頂部,將Label2頂部固定到Label1底部,將Label3頂部固定到Label2底部,最後(最重要的是)將Label3的底部固定到ContentView的底部。現在它有足夠的信息來計算ContentView的高度。

我希望這給別人一個線索,當我在上面的帖子閱讀,仍然無法弄清楚如何使內容查看的寬度和高度的限制正常。我錯過的是將Label3的底部固定到ContentView的底部,否則ContentView知道它有多高(因爲Label3會浮動,並且不會有任何限制來告訴ContentView它位於底部y位置)。

+0

這部分對我來說很清楚「因此它不會告訴ContentView與顯示的ScrollView框架具有相同的框架大小,而是告訴Scrollview ContentView是其內容」。 –

+0

到目前爲止,最澄清的答案!謝謝!豎起大拇指! –

+0

謝謝道格。你的「如果你想要一個垂直滾動的[] ....你設置一個約束,使ContentView寬度等於ViewController視圖的寬度,這將照顧寬度」爲我解決了很多頭痛的問題。我將ContentView的寬度和高度都設置爲等於ViewController的視圖。我刪除了相同的高度,並修復了很多約束問題。謝謝! – Pangu

0

使用此代碼。 ScrollView的setContentSize應該在主線程中調用async。

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    dispatch_async(dispatch_get_main_queue(),^
        { 
         CGRect contentRect = CGRectZero; 
         for (UIView *view in scrollView.subviews) 
          if (!view.hidden) 
           contentRect = CGRectUnion(contentRect, view.frame); 

         contentRect.size.height=contentRect.size.height; 
         scrollView.contentSize = contentRect.size; 
        }); 
} 
0

我的滾動視圖變體! Dynamic!高度:

1)添加scroll viewUIView。固定所有(頂部,底部,引導,尾跡)約束。

2)將UIView加到Scroll View。固定所有(頂部,底部,引導,尾跡)約束。這將是你的Content view。您也可以重命名它。

3)控制從拖Content viewScroll view - Equal width

4)將內容添加到您的UIView。設置所需的約束。和!在較低處增加底部約束不是Greater or equal>=)(與大多數人談話)但是Equal!例如,將其設置爲20

在我的情況下,我有UIImageView的內容。我已將它連接到代碼height。如果我將其更改爲1000,則可以看到滾動。和所有的作品。

工程就像我的魅力。任何問題 - 歡迎評論。

+0

如果底部商品也在動態增長? – jshapy8

+0

@ jshapy8我不確定,但我認爲它會在滾動視圖中調整UIView大小。和scrollview將適應其高度。你可以自己嘗試。我不能做它atm –

+0

是的,我發現它調整大小。我認爲手動重繪動態視圖的框架和它下面的視圖是一個棘手的部分,不僅僅是約束 – jshapy8

相關問題