Правильное использование intrinsicContentSize и sizeThatFits: в подклассе UIView с autolayout
Я задаю этот (как-то) простой вопрос просто для того, чтобы быть привередливым, потому что иногда я волнуюсь из-за неправильного использования многих API-интерфейсов UIView, особенно когда речь идет об автоматической разметке.
Чтобы сделать это очень просто, я приведу пример, давайте предположим, что мне нужен подкласс UIView, который имеет значок изображения и многострочную метку; я хочу, чтобы высота моего вида изменялась с высотой надписи (чтобы уместился текст внутри), также я выкладываю ее с помощью Интерфейсного конструктора, поэтому у меня есть что-то вроде этого:
с некоторыми ограничениями, которые задают фиксированную ширину и высоту для представления изображения, а также фиксированную ширину и положение (относительно представления изображения) для метки:
Теперь, если я назначу некоторый текст метке, я хочу, чтобы вид был изменен по высоте, чтобы соответствовать ему должным образом, или остался с той же высотой, что и в xib. До автоматического размещения я бы всегда делал что-то вроде этого:
В файле подкласса CustoView я бы переопределил sizeThatFits:
вот так:
- (CGSize) sizeThatFits:(CGSize)size{
//this stands for whichever method I would have used
//to calculate the height needed to display the text based on the font
CGSize labelSize = [self.titleLabel intrinsicContentSize];
//check if we're bigger than what's in ib, otherwise resize
CGFloat newHeight = (labelSize.height <= 21) ? 51: labelSize.height+20;
size.height = newHeight;
return size;
}
И чем бы я назвал что-то вроде:
myView.titleLabel.text = @"a big text to display that should be more than a line";
[myView sizeToFit];
Теперь, думая об ограничениях, я знаю, что intrinsicContentSize
на элементах дерева представления, чтобы знать, каков их размер и сделать его вычисления, поэтому я должен переопределить intrinsicContentSize
в моем подпредставлении, чтобы вернуть те же самые вещи, которые он возвращает в sizeThatFits:
ранее показанный метод, за исключением того, что ранее при вызове sizeToFit
У меня было правильно изменено изображение, но теперь с autolayout в сочетании с xib этого не произойдет.
Конечно, я мог бы звонить sizeToFit
каждый раз, когда я редактирую текст в моем подклассе вместе с переопределенным intrinsicContentSize
который возвращает точно такой же размер sizeThatFits:
, но почему-то я не думаю, что это правильный способ сделать это.
Я думал о переопределении needsUpdateConstraints
а также updateConstraints
, но все же не имеет особого смысла, так как ширина и высота моего вида определяются и переводятся из маски авторазмера из xib.
Итак, как вы думаете, что является самым чистым и правильным способом сделать именно то, что я здесь показываю, и полностью поддерживать автопоставку?
1 ответ
Я не думаю, что вам нужно определить intrinsicContentSize.
Вот две причины думать, что:
Когда документация по Auto Layout обсуждается
intrinsicContentSize
, он относится к нему как к "листовым представлениям", таким как кнопки или метки, где размер может быть вычислен исключительно на основе их содержимого. Идея состоит в том, что они являются листами в дереве иерархии представлений, а не ветвями, поскольку они не состоят из других представлений.IntrinsicContentSize на самом деле не является "фундаментальной" концепцией в Auto Layout. Фундаментальные понятия - это просто ограничения и атрибуты, связанные ограничениями. IntrinsicContentSize, приоритеты объятия контента и приоритеты сопротивления сжатию - это на самом деле просто удобство, используемое для генерации внутренних ограничений, касающихся размера. Окончательный размер является просто результатом того, что эти ограничения взаимодействуют со всеми другими ограничениями обычным способом.
И что? Поэтому, если ваше "пользовательское представление" на самом деле является просто сборкой пары других представлений, вам не нужно определять intrinsicContentSize. Вы можете просто определить ограничения, которые создают желаемый макет, и эти ограничения также приведут к желаемому размеру.
В конкретном случае, который вы описываете, я бы установил ограничение>> 0 нижнего пространства от метки до суперпредставления, еще одно от изображения до суперпредставления, а затем также ограничение с низким приоритетом высоты ноль для вида как все. Ограничение с низким приоритетом будет пытаться сжать сборку, в то время как другие ограничения не позволят ей сжиматься до такой степени, что она обрезает свои подпредставления.
Если вы никогда не определяете intrinsicContentSize явно, как вы видите размер, вытекающий из этих ограничений? Одним из способов является принудительное размещение макета, а затем наблюдать результаты.
Другой способ заключается в использовании systemLayoutSizeFittingSize:
(а в iOS8 малообещающие systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
). Это кузен ближе sizeThatFits:
чем это intrinsicContentSize
, Это то, что система будет использовать для расчета подходящего размера вашего представления, принимая во внимание все ограничения, которые он содержит, включая ограничения внутреннего размера контента, а также все остальные.
К сожалению, если у вас есть многострочная метка, вам, вероятно, также потребуется настроить preferredMaxLayoutWidth
чтобы получить хороший результат, но это другая история...