簡體   English   中英

自動布局UIScrollView以及具有動態高度的子視圖

[英]Auto layout UIScrollView with subviews with dynamic heights

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

- 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高度的總和。 但我相信還有更好的解決方案嗎?

謝謝

我使用如下的純結構

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

確保Button( 最后一個視圖 )有一個約束(從底部到superview的垂直間距,即scrollview),在這種情況下,無論你的視圖A和視圖B有什么變化,scrollView的高度都會相應地改變。

我參考了這個偉大的在線圖書網站

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

我有類似的問題,我正在創建一個詳細信息視圖和使用Interface Builder與Auto布局是一個非常適合任務!

祝好運!

(其他資源:

關於滾動視圖自動布局的堆棧溢出討論。

iOS 6有一個發行說明,討論了UIScrollView的Auto Layout支持。

關於滾動視圖的免費在線iOS書籍說明 這實際上對我幫助很大!

假設我們有這樣的層次結構(Label1是ContentView的子視圖; ContentView是ScrollView的子視圖,ScrollView是viewcontroller視圖的子視圖):

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView以與viewcontroller視圖正常的方式使用autolayout進行約束。

ContentView被固定在scrollview的頂部/左側/右側/底部。 這意味着你有一些約束使ContentView的上/下/前/后邊緣被約束為等於ScrollView上的相同邊。 這是一個關鍵:這些約束是針對ScrollView的contentSize,而不是它的框架大小,如viewcontroller的視圖所示。 因此,它不會告訴ContentView與顯示的ScrollView框架具有相同的框架大小,而是告訴Scrollview ContentView是其內容,因此如果contentview大於ScrollView框架,那么您可以滾動,就像設置scrollView.contentSize更大而scrollView.frame使內容可滾動。

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

因此,例如,如果您需要垂直滾動的標簽集,則設置約束以使ContentView寬度等於ViewController視圖的寬度,該寬度負責寬度。 為了處理高度,將Label1頂部固定到ContentView頂部,將Label2頂部固定到Label1底部,將Label3頂部固定到Label2底部,最后(並且重要的是)將Label3的底部固定到ContentView的底部。 現在它有足夠的信息來計算ContentView的高度。

我希望這給某人一個線索,因為我閱讀了上面的帖子,仍然無法弄清楚如何正確地使ContentView的寬度和高度限制。 我缺少的是將Label3的底部固定在ContentView的底部,否則ContentView如何知道它有多高(因為Label3將會浮動,並且沒有約束告訴ContentView它的底部y位置在哪里)。

這是我如何使用容器視圖布局純自動布局UIScrollView的示例。 我評論說要更清楚:

容器是標准的UIView,而body是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]];
}

在我的例子中,'body'是一個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的大小。

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

迅速:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        var contentRect = CGRect.zero

        for view in self.scrollView.subviews {
           contentRect = contentRect.union(view.frame)
        }

        self.scrollView.contentSize = contentRect.size
    }
}

目標C:

 - (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];

     dispatch_async(dispatch_get_main_queue(), ^ {
                        CGRect contentRect = CGRectZero;

                        for(UIView *view in scrollView.subviews)
                           contentRect = CGRectUnion(contentRect,view.frame);

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

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

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

1)將scroll view添加到您的UIView 固定所有(頂部,底部,引導,軌跡)約束。

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

3)控制從Content view拖動到Scroll view - Equal width

4)向UIView添加內容。 設置所需的約束。 和! 在較低的項目添加底部約束 Greater or equal>= )(像大多數人談話) 但是 Equal 例如,將其設置為20

在我的情況下,我在內容中有UIImageView 我把它的height連接到代碼。 如果我將其更改為1000 ,則滾動顯示。 一切正常。

對我來說就像一個魅力。 有任何問題 - 歡迎提出意見。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM