Подтвердить что ты не робот

Автоматическая компоновка 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 internal constraints update to hide additional content 
    self.contentVisible = NO;
} else {
    heightConstraint.constant += ContentHeight;
    // + additional View 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. Но я считаю, что есть лучшее решение?

Спасибо

4b9b3361

Ответ 1

Я использую чистую структуру следующим образом

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

Убедитесь, что кнопка (ПОСЛЕДНЕЕ представление) имеет ограничение (вертикальное расстояние от нижнего до супервизора, которое является прокруткой), в этом случае независимо от того, какие изменения для вашего вида A и вида B будет, scrollView будет соответствующим образом изменен.

Я ссылаюсь на этот отличный онлайн-сайт .

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

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

Удачи!

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

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

iOS 6 имеет Замечания к выпуску, в котором рассказывается об автоматической поддержке макета для UIScrollView.

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

Ответ 2

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

ViewController View ScrollView ContentView Label1 Label2 Label3

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

ContentView привязан вверх/влево/вправо/снизу к scrollview. Это означает, что у вас есть ограничения, которые ограничивают верхний/нижний/передний/конечный края 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).

Ответ 3

Это пример того, как я выложил чистый автозапуск 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 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 trailing edge is pinned to the container bottom edge
    //it won't grow the width because the container width is constrained to the scrollview 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.

Ответ 4

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

Ответ 5

Используйте этот код. 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;
                   });
}

Ответ 6

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

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