it-swarm.com.ru

Автоматическая разметка UIScrollView с подпредставлениями с динамической высотой

У меня проблемы с 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. Но я верю, что есть лучшее решение?

Спасибо

38
lukas

Я использую чистую структуру, как показано ниже

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

Убедитесь, что кнопка (THE LAST view) имеет ограничение (вертикальный интервал от ее низа до суперпредставления, который является просмотром прокрутки), в этом случае, независимо от того, какие будут изменения для вашего вида A и вида B, будет высота scrollView. будут изменены соответственно.

Я имею в виду этот великий онлайн книжный сайт .

Просто прочитайте раздел «Создание прокрутки», у вас должна быть идея. 

У меня была похожая проблема, когда я создавал подробный вид, и использование Interface Builder с автоматической разметкой отлично подходит для этой задачи!

Удачи!

(Дополнительные ресурсы:

Обсуждение переполнения стека о автоматическом макете для просмотра с прокруткой .

в iOS 6 есть Release Notes , в котором говорится о поддержке Auto Layout для UIScrollView.

Бесплатный онлайн объяснение книги iOS о просмотре прокрутки. Это на самом деле мне очень помогло!

58
lorixx

Допустим, у нас есть такая иерархия (Label1 - это подпредставление ContentView; ContentView - это подпредставление ScrollView, ScrollView - это подпредставление представления viewcontroller):

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView ограничен autolayout обычным способом к представлению viewcontroller.

ContentView закреплен сверху/слева/справа/снизу для просмотра прокрутки. Это означает, что у вас есть ограничения, которые ограничивают верхние/нижние/передние/задние края ContentView так, чтобы они равнялись одним и тем же краям в ScrollView. Вот ключ: эти ограничения относятся к contentSize объекта ScrollView, а не к его размеру кадра, как показано в представлении viewcontroller. Таким образом, это не говорит о том, что ContentView имеет тот же размер фрейма, что и отображаемый кадр ScrollView, а скорее говорит Scrollview, что ContentView является его содержимым, и поэтому, если contentview больше, чем кадр ScrollView, вы получаете прокрутку, как при установке scrollView.contentSize больше. чем scrollView.frame делает прокручиваемый контент.

Вот еще один ключ: теперь у вас должно быть достаточно ограничений между ContentView, Label1-3 и всем остальным, кроме Scrollview, чтобы ContentView мог определить его ширину и высоту по этим ограничениям.

Так, например, если вы хотите вертикально прокрутить набор меток, вы устанавливаете ограничение, чтобы сделать ширину ContentView равной ширине ViewController View, которая заботится о ширине. Чтобы позаботиться о высоте, прикрепите верхнюю часть Label1 к верхней части ContentView, верхнюю часть Label2 к нижней части Label1, верхнюю часть Label3 к нижней части Label2 и, наконец (и, что немаловажно), прикрепите нижнюю часть Label3 к нижней части ContentView. Теперь у него достаточно информации для расчета высоты ContentView.

Я надеюсь, что это даст кому-то подсказку, так как я прочитал вышеупомянутые посты и до сих пор не могу понять, как правильно установить ограничения ширины и высоты в ContentView. Чего мне не хватало, так это закрепления нижней части Label3 к нижней части ContentView, иначе как ContentView мог бы узнать, насколько он высок (поскольку Label3 просто будет плавать, и не будет никаких ограничений, чтобы сообщать ContentView, где находится его нижняя позиция y).

25
Doug Voss

Это пример того, как я выложил чистый UIScrollView с автопоставкой и представлением контейнера. Я прокомментировал, чтобы прояснить это:

контейнер является стандартным 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]];
}

В моем примере 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.

7
jwswart

Используйте этот код. ScrollView setContentSize должен вызываться как асинхронный в главном потоке.

Swift:

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;
                       });
}
1
Karen Hovhannisyan

Мой вариант для просмотра с прокруткой с! 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, прокрутка будет видна. И все работает.

Работает как шарм для меня. Любые вопросы - добро пожаловать в комментарии.

0
Vlad Pulichev

В любой момент представление прокрутки должно знать размер своего содержимого. Размер контента определяется из подпредставлений прокрутки. Очень удобно сопоставить свойства контроллера с ограничениями в файле xib, описывающими высоты подпредставлений. Затем в коде (блоке анимации) вы можете просто изменить константы этих свойств ограничения. Если вам нужно изменить все ограничение, сохраните ссылку на него, чтобы позже вы могли обновить его в родительском контейнере. 

0
Vladimír Slavík