2013-05-30 1 views
39

Следующий код (называемый viewDidLoad) приводит к полностью красному экрану. Я ожидаю, что это будет полностью зеленый экран. Почему он красный? И как я могу сделать все зеленым?iOS Autolayout с UIScrollview: почему просмотр содержимого прокрутки не заполняет прокрутку?

UIScrollView* scrollView = [UIScrollView new]; 
scrollView.translatesAutoresizingMaskIntoConstraints = NO; 
scrollView.backgroundColor = [UIColor redColor]; 
[self.view addSubview:scrollView]; 

UIView* contentView = [UIView new]; 
contentView.translatesAutoresizingMaskIntoConstraints = NO; 
contentView.backgroundColor = [UIColor greenColor]; 
[scrollView addSubview:contentView]; 

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView,contentView); 

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]]; 
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]]; 

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]]; 
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]]; 

ответ

66

Ограничения с просмотром прокрутки работают несколько иначе, чем с другими видами. Ограничения между contentView и его superview (scrollView) относятся к scrollViewcontentSize, а не к его frame. Это может показаться запутанным, но на самом деле это очень полезно, а это значит, что вам никогда не придется настраивать contentSize, но contentSize будет автоматически настраиваться в соответствии с вашим контентом. Такое поведение описано в Technical Note TN2154.

Если вы хотите определить размер contentView на экране или что-то в этом роде, вам нужно будет добавить ограничение между contentView и основным видом, например. Это, по общему признанию, противоречиво для размещения контента в scrollview, поэтому я, вероятно, не советую этого, но это можно сделать.


Чтобы проиллюстрировать эту концепцию, что размер contentView будет зависеть от его содержания, а не по bounds из scrollView, добавить ярлык к вашему contentView:

UIScrollView* scrollView = [UIScrollView new]; 
scrollView.translatesAutoresizingMaskIntoConstraints = NO; 
scrollView.backgroundColor = [UIColor redColor]; 
[self.view addSubview:scrollView]; 

UIView* contentView = [UIView new]; 
contentView.translatesAutoresizingMaskIntoConstraints = NO; 
contentView.backgroundColor = [UIColor greenColor]; 
[scrollView addSubview:contentView]; 

UILabel *randomLabel = [[UILabel alloc] init]; 
randomLabel.text = @"this is a test"; 
randomLabel.translatesAutoresizingMaskIntoConstraints = NO; 
randomLabel.backgroundColor = [UIColor clearColor]; 
[contentView addSubview:randomLabel]; 

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel); 

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]]; 
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]]; 

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]]; 
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]]; 

[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]]; 
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]]; 

Теперь вы будете см., что contentView (и, следовательно, contentSize of scrollView) отрегулированы так, чтобы соответствовать этикетке со стандартными полями. И поскольку я не указывал ширину/высоту метки, которая будет корректироваться на основе текста, который вы помещаете в этот ярлык.


Если вы хотите, чтобы contentView также приспособиться к ширине основного зрения, вы могли бы сделать заново свой viewDict, как это так, а затем добавить эти дополнительные ограничения (в дополнение ко всем остальным, выше):

UIView *mainView = self.view; 

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel, mainView); 

[mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[contentView(==mainView)]" options:0 metrics:0 views:viewDict]]; 

Существует known issue (bug?) с многострочными этикетками в scrollviews, что если вы хотите, чтобы изменить размер в зависимости от количества текста, вы должны сделать некоторую ловкость рук, такими как:

dispatch_async(dispatch_get_main_queue(), ^{ 
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width; 
}); 
+1

Я не могу показаться, чтобы получить ярлык расширить вертикально, если текст слишком длинный, чтобы поместиться на одна линия. Я установил количество строк в 0, а lineBreakMode - NSLineBreakByWordWrapping. Я что-то упускаю? – rdelmar

+0

Кроме того, я добавил последнюю строку в вашем ответе, чтобы навести ширину contentView на ширину экрана (в противном случае ширина contentView будет достаточно широкой, чтобы разместить длинный текст). Если я явно устанавливаю высоту метки с ограничением высоты, то она работает. – rdelmar

+0

Да, Мэтт обнаружил [пару обходов] (http://stackoverflow.com/questions/13149733/ios-autolayout-issue-with-uilabels-in-a-resizing-parent-view) для этой проблемы/ошибка многострочных меток. Я добавил один из них до конца моего ответа. Совершенно неудовлетворительное решение. Похоже, вам нужно выбирать между значениями жесткого кодирования или обходным путем. – Rob

1

Я попробовал ответ Роба в порядке. НО!, если вы также включили масштабирование, этот код автоотключения будет мешать. Сохраняет изменение размера contentView при масштабировании. То есть, если я увеличил масштаб (zoomScale> 1), я не смогу прокрутить детали за пределами экрана.

После нескольких дней борьбы с Autolayout, к сожалению, я не смог найти решение. В конце концов, это даже не имеет значения (wat? Ok, извините). В конце концов, Я просто удаляю Autolayout на contentView (используйте фиксированный размер contentView), а затем в layoutSubviews, я настраиваюself.scrollView.contentInset. (Я использую Xcode5, iOS 7)

Я знаю, что это не является прямым решением этого вопроса. Но я просто хочу указать на простой способ обхода проблемы.Он отлично работает для центрирования содержимого contentView с фиксированным размером в scrollView с двумя строками кода! Надеюсь, что это может помочь кому-то,)

- (void)layoutSubviews { 
    [super layoutSubviews]; 

    // move contentView to center of scrollView by changing contentInset, 
    // because I CANNOT set this with AUTOLAYOUT!!! 
    CGFloat topInset = (self.frame.size.height - self.contentView.frame.size.height)/2; 
    self.scrollView.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0); 
} 
-1

В общем, Auto Layout рассматривает сверху, слева, снизу, и правые края тем, чтобы быть видимыми краями. То есть, если вы привязываете представление к левому краю своего супервизора, вы действительно привязываете его к минимальной x-значению границ супервизора. Изменение начального значения границ надзора не меняет положение представления.

Класс UIScrollView прокручивает его содержимое, изменяя начало его границ. Чтобы сделать эту работу с помощью автоматического макета, верхний, левый, нижний и правый края в представлении прокрутки теперь означают края его содержимого.

Ограничения на вид в представлении прокрутки должны приводить к заполнению размера, которое затем интерпретируется как размер содержимого прокрутки. (Это не следует путать с методом intrinsicContentSize, используемым для автоматической компоновки.) Чтобы размер кадра представления прокрутки с помощью Auto Layout, ограничения должны быть либо явными относительно ширины и высоты прокрутки, либо края прокрутки должны быть привязан к представлениям вне своего поддерева.

Обратите внимание, что вы можете сделать представление подпрограммы вида прокрутки, чтобы плавать (а не прокручивать) по другому прокручиваемому контенту, создавая ограничения между представлением и представлением вне поддерева вида прокрутки, например надвью просмотра прокрутки.

Вот два примера того, как настроить вид прокрутки, первый смешанный подход, а затем чистый подход

+0

Добро пожаловать в переполнение стека. Пожалуйста, просмотрите [Как написать хороший ответ] (https://meta.stackexchange.com/questions/7656/how-do-i-write-a-good-answer-to-a-question). – Rick