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

Использование AutoLayout для стека в двух столбцах разных высот

Targeting iOS 8.1

Я использую AutoLayout, чтобы выложить несколько ярлыков в TableCell. Некоторые из этих ярлыков являются необязательными, а некоторые могут обернуть их текст. Они разделены на два столбца "Столбцы", эти столбцы представляют собой два UIView в TableCell ContentView. Мои ограничения применяются программно.

ВТОРОЕ ОБНОВЛЕНИЕ

Без Ответ SwiftArchitect ниже Я бы не решил этого и принял его ответ. Однако, поскольку мой все в коде, в пользовательской таблице, я также добавил отдельный ответ ниже

UPDATE

В попытке остановить ярлыки от растяжения до размера, большего, чем нужно, я ранее устанавливал значения SetContentHuggingPriority и SetContentCompressionResistancePriority в 1000, поскольку я полагал, что это было эквивалентно: "Я хочу, чтобы метка была обнимайте его содержимое до его точной высоты, и я не хочу, чтобы он когда-либо был сжат по вертикали" Этот запрос явно не соответствовал AutoLayout, как вы можете видеть в примерах Red и Pink ниже.

this.notesLabel.SetContentHuggingPriority(1000, UILayoutConstraintAxis.Vertical);
this.notesLabel.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);

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

  • Почему удаление приоритетов Hugging and Compression устраняет мои вопрос?
  • Как я могу получить текст в красном поле (красное поле, не входящее в ячейку, добавленное позже), чтобы не расширяться, не возвращаясь к моей предыдущей проблеме?

введите описание изображения здесь

Вот несколько скриншотов того, как это выглядело, когда приоритеты сжатия и обнимания устанавливаются. Цвета фона предназначены для отладки

введите описание изображения здесь

Общая проблема заключалась в том, что Содержащий вид (цветной фиолетовый и красный) подбирал себя к меньшему из двух. Как вы можете видеть в верхней части, "Приоритет 3" сокращается, потому что левый контейнер столбца не должен быть выше.

В следующем примере нет метки приоритета, но EventDate сжимается.

введите описание изображения здесь

4b9b3361

Ответ 1

Следующий ответ был написан и протестирован. Он работает правильно на iPhone и iPad, портрет и пейзаж. Побеждает самая высокая колонна, а другая просто занимает необходимое пространство. При необходимости он может быть изменен для вертикально центрирования объектов. Он затрагивает проблемы, связанные с вертикальной обрезкой, а также динамическое масштабирование.

введите описание изображения здесь

Предварительный совет

  • Используйте Storyboard, если можете. Вы можете проверить все свои ограничения визуально с помощью современного графического интерфейса.
  • UILabel height: пусть каждая метка занимает пространство, которое требуется вертикально, и добавляет только верхние и боковые привязки
  • Используйте дополнительные представления в качестве контейнеров для определения ширины каждого столбца. Используйте multiplier, чтобы получить, скажем 2/3 и 1 треть.
  • Пусть эти представления вычисляют их идеальную высоту, добавляя единственное ограничение высоты к нижней части нижней метки (leftColumn.bottom Equal lowerLeftLabel.bottom)
  • Не добавлять и не удалять представления динамически; скорее, скрыть их, чтобы они сохраняли связанные ограничения.

Описание решения

Для простоты я создал 2 подпункта, 1 для каждого столбца и расположил их бок о бок. Они привязаны сверху/слева и сверху/вправо, их ширина вычисляется, а их высота получается из их соответствующего содержимого (*).

В левом и правом подзонах есть 1/2 multiplier, к которому я добавил a constant из 2 пикселей для поля. Метки внутри этих двух столбцов привязаны слева и справа (leading space к контейнеру и trailing space к контейнеру) с краем 8 пикселей. Это гарантирует, что ни одна этикетка никогда не истечет за ее колонку.

  • Учтите, что высота вашего UITableViewCell является самой большой из двух внутренних столбцов. Другими словами, containerView.height >= left.height и контейнерView.height >= right.height.
  • Убедитесь, что вы не удаляете какие-либо невидимые ярлыки. view.hidden не нарушит ваши ограничения, и это то, что вы хотите.
  • Прикрепите каждый UILabel слева и справа к контейнеру, самый верхний к контейнеру, но каждый последующий label.top должен быть привязан к .bottom одного над ним. Таким образом, ваш контент будет течь. Вы можете добавить поля, если хотите.

(*) Конечный ключ - связать высоту каждого столбца с ограничением на столбец, чтобы он равнялся .bottom нижней метки для этого столбца. В приведенном выше примере вы можете четко видеть, что правый столбец с синим фоном короче левого.

введите описание изображения здесь

Пока я вижу, что вам нужно решение в коде, я создал свой пример с помощью Storyboard менее чем за 15 минут. Это не просто концепция, это реальная реализация. Он имеет ровно 0 строк кода и работает на всех устройствах iOS. Кстати, он также имеет 0 ошибок.

Список всех ограничений

Обратите внимание на то, что >= посыпано здесь и там. Они являются ключом к тому, чтобы ваши колонки были независимо высокими.

NSLayoutContraint практически идентичны для L и R.

введите описание изображения здесь

введите описание изображения здесь

Получите раскадровку здесь и подробную статью там.

Ответ 2

Я принял ответ SwiftArchitect, но, увидев, как я был после подхода на основе кода для TableCell, я добавлю отдельный ответ. Без его помощи я не смог бы зайти так далеко.

Я использую MVVMCross и Xamarin iOS, и мой TableCell наследует от MvxTableViewCell

Создание подпрограмм

Из ctor ячейки я создаю все необходимые UILabels и отключу AutoResizingMasks, установив view.TranslatesAutoresizingMaskIntoConstraints = false

В то же время я создаю два UIViews leftColumnContainer и rightColumnContainer. Они снова TranslatesAutoresizingMaskIntoConstraints установлены в false.

Соответствующие метки добавляются как подпункты к leftColumnContainer и rightColumnContainer UIViews. Затем два контейнера добавляются в качестве подматрицы в TableCell ContentView

this.ContentView.AddSubviews(this.leftColumnContainer, this.rightColumnContainer);
this.ContentView.TranslatesAutoresizingMaskIntoConstraints = true;

UILabels все данные связаны через вызов MVVMCross DelayBind

Настройка ограничений компоновки (UpdateConstraints())

Макет TableCell зависит от данных для ячейки, где 5 из 8 меток являются необязательными, и 4 из 8, нуждающихся в поддержке обертывания текста

Первое, что я делаю, - это привязать верхнюю и левую части leftColumnContainer к TableCell.ContentView. Затем верхний и правый из "rightColumnContainer" в TableCell.ContentView. Конструкция требует, чтобы правый столбец был меньше левого, поэтому это делается с помощью масштабирования. Я использую FluentLayout для применения этих ограничений

this.ContentView.AddConstraints(
                this.leftColumnContainer.AtTopOf(this.ContentView),
                this.leftColumnContainer.AtLeftOf(this.ContentView, 3.0f),
                this.leftColumnContainer.ToLeftOf(this.rightColumnContainer),
                this.rightColumnContainer.AtTopOf(this.ContentView),
                this.rightColumnContainer.ToRightOf(this.leftColumnContainer),
                this.rightColumnContainer.AtRightOf(this.ContentView),
                this.rightColumnContainer.WithRelativeWidth(this.ContentView, 0.35f));

Вызовы ToLeftOf и ToRight of устанавливают правый край левого столбца и левый край правого столбца рядом друг с другом.

Ключевым элементом решения, которое пришло от SwiftArchitect, было установить высоту TableCell ContentView нa >= на высоту leftColumnContainer и rightColumnContainer. Было не так очевидно, как это сделать с FluentLayout, чтобы они были "длинными руками"

this.ContentView.AddConstraint(
              NSLayoutConstraint.Create(this.ContentView, NSLayoutAttribute.Height, NSLayoutRelation.GreaterThanOrEqual, this.leftColumnContainer, NSLayoutAttribute.Height, 1.0f, 5.0f));

            this.ContentView.AddConstraint(
              NSLayoutConstraint.Create(this.ContentView, NSLayoutAttribute.Height, NSLayoutRelation.GreaterThanOrEqual, this.rightColumnContainer, NSLayoutAttribute.Height, 1.0f, 5.0f));

Затем я ограничиваю верхнюю, левую и правую стороны первой метки в каждом столбце контейнером столбца. Ниже приведен пример первой метки в левом столбце

this.leftColumnContainer.AddConstraints(
                this.categoryLabel.AtTopOf(this.leftColumnContainer, CellPadding),
                this.categoryLabel.AtRightOf(this.leftColumnContainer, CellPadding),
                this.categoryLabel.AtLeftOf(this.leftColumnContainer, CellPadding));

Для каждой из ярлыков, которые являются необязательными, я сначала проверяю MVVMCross DataContext, чтобы увидеть, видны ли они. Если они видны, то аналогичные ограничения для Left, Top и Right применяются, когда Top ограничивается нижним знаком метки выше. Если они не видны, они удаляются из представления так

this.bodyText.RemoveFromSuperview();

Если вам интересно, как эти ячейки будут работать с iOSs Cell Reuse, я расскажу об этом далее.

Если метка будет последней меткой в ​​столбце (это зависит от данных), я применяю другое ключевое обучение из ответа SwiftArcthiect

Пусть [столбцы] вычисляют их идеальную высоту, добавляя один ограничение высоты до нижней части нижней метки (leftColumn.bottom Равный lowLeftLabel.bottom)

Работа с повторным использованием ячеек

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

TableSource наследует MvxTableViewSource. В переопределенном GetOrCreateCellFor я проверяю конкретный идентификатор повторного использования (нормальное использование), и если это так вызовите DequeueReusableCell, однако в этом случае я откладываю на процедуру, инкапсулированную в пользовательский класс ячеек, который знает, как построить идентификатор данных

protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
        {
            MvxTableViewCell cell;

            if (this.reuseIdentifier != null)
            {
                cell = (MvxTableViewCell)tableView.DequeueReusableCell(this.reuseIdentifier);    
            }
            else
            {
                // No single reuse identifier, defer to the cell for the identifer
                string identifier = this.itemCell.GetCellIdentifier(item);

                if (this.reuseIdentifiers.Contains(identifier) == false)
                {
                    tableView.RegisterClassForCellReuse(this.tableCellType, identifier);
                    this.reuseIdentifiers.Add(identifier);
                }

                cell = (MvxTableViewCell)tableView.DequeueReusableCell(identifier);    
            }

            return cell;
        }

и вызов для создания id

public string GetCellIdentifier(object item)
        {
            StringBuilder cellIdentifier = new StringBuilder();

            var entry = item as EntryItemViewModelBase;

            cellIdentifier.AppendFormat("notes{0}", entry.Notes.HasValue() ? "yes" : "no");
            cellIdentifier.AppendFormat("_body{0}", !entry.Body.Any() ? "no" : "yes");
            cellIdentifier.AppendFormat("_priority{0}", entry.Priority.HasValue() ? "yes" : "no");
            cellIdentifier.AppendFormat("_prop1{0}", entry.Prop1.HasValue() ? "yes" : "no");
            cellIdentifier.AppendFormat("_prop2{0}", entry.Prop2.HasValue() ? "yes" : "no");
            cellIdentifier.AppendFormat("_warningq{0}", !entry.IsWarningQualifier ? "no" : "yes");
            cellIdentifier.Append("_MEIC");

            return cellIdentifier.ToString();
        }

Ответ 3

Прежде всего, мы не должны играть вокруг остроконечного контента - приоритет обмана или сжатия, если не существует ситуации, когда вам нужно изменить без останова. Значение по умолчанию 250: 750, заданное яблоком, будет соответствовать 90% сценария, если настройка автоматически настроена. Только в редких случаях, когда существует конфликт для механизма изменения размера представления на основе выполненных ограничений, мы должны изменить приоритеты обмана/сжатия.

Ваша оригинальная проблема - ваши ярлыки раздавлены. Пометки по умолчанию позволяют использовать собственный размер содержимого системы, где двигатель будет определять ширину и высоту метки по умолчанию в зависимости от содержимого ярлыков и размера текста. Поэтому, если вы решите, что ваш ярлык не расширяет представление, тогда вы должны установить ограничение на конец с правой стороны ярлыка на представление. В общем случае он будет установлен в свойство "Equals", которое не будет соответствовать нашему требованию, потому что наш ярлык передается по внутреннему свойству, и мы не должны предоставлять стандартную ширину метки. Следовательно, вместо "Равенство должно быть" больше или равно значению для конечного свойства.

В вашем сценарии вы не должны устанавливать ограничение высоты для меток и активировать функцию переноса слов вдоль 'с количеством свойств линии для любой требуемой метки двух строк.

вы знаете, что вам всегда нужно отображать "Желтая метка", "зеленая метка" и "фиолетовая метка" в правом боковом представлении, независимо от того, что слева отображается одна или две строки на "красной этикетке".

Так исправить статическую высоту для ячейки. С правой стороны, Исправьте верхнее ограничение для "оранжевой" метки и нижнее ограничение для "желтой метки". Таким образом, красная метка центра получит ясную высоту, которая может вместить одну/две строки в соответствии с требованием. И приложите достаточные ограничения для правильной стороны, чтобы выполнить ваши требования.

Если это не решит вас, вы не опубликуете или не обсудите мое решение, напишите ниже.