Создание программных ограничений компоновки

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

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

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 748)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:0 metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Он не удовлетворяет некоторым ограничениям. Я не вижу, что не так. Кроме того, почему я не могу использовать свойство типа self.myView вместо локальной переменной типа myView?

Ответ 1

При использовании автоматической компоновки в коде установка кадра ничего не делает. Поэтому тот факт, что вы указали ширину 200 в приведенном выше представлении, ничего не значит, когда вы устанавливаете для нее ограничения. Для того чтобы ограничение на представление было однозначным, ему нужны четыре вещи: x-позиция, y-позиция, ширина и высота для любого заданного состояния.

В приведенном выше коде у вас есть только две (высота, относительно супервизора и y-позиция, относительно супервизора). В дополнение к этому у вас есть два требуемых ограничения, которые могут конфликтовать в зависимости от того, как устанавливаются ограничения просмотра. Если у супервизора было требуемое ограничение, определяющее его высоту, то какое-то значение меньше 748, вы получите исключение "неудовлетворительных ограничений".

Тот факт, что вы задали ширину представления перед установкой ограничений, ничего не значит. Он даже не учитывает старый фрейм и рассчитает новый фрейм, основанный на всех ограничениях, которые он указал для этих представлений. Когда вы работаете с автозапуском в коде, я обычно просто создаю новое представление с помощью initWithFrame:CGRectZero или просто init.

Чтобы создать набор ограничений, необходимый для макета, который вы устно сформулировали в своем вопросе, вам нужно будет добавить некоторые горизонтальные ограничения для привязки ширины и x-позиции, чтобы дать полностью определенный макет:

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:[myView(==200)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Условное описание этого макета читается следующим образом, начиная с вертикального ограничения:

myView заполнит его высоту надписью верхним и нижним отступом равное стандартному пространству. myView viewview имеет минимальную высоту из 748 пунктов. ширина myView равна 200pts и имеет правильное дополнение, равное стандартное пространство против его наблюдения.

Если вы просто хотите, чтобы представление заполнило всю высоту супервизора без ограничения высоты супервизора, вы просто опустите параметр (>=748) в текст визуального формата. Если вы считаете, что параметр (>=748) необходим для того, чтобы дать ему высоту - в этом случае вы не должны: привязать представление к краям супервизора с помощью панели (|) или строки с пробелом (|-, -|), вы даете вашему представлению y-позицию (фиксируя представление на одном краю) и y-позицию с высотой (привязывая представление к обоим краям), тем самым удовлетворяя вашему ограничению, установленному для представления.

В отношении вашего второго вопроса:

Используя NSDictionaryOfVariableBindings(self.myView) (если у вас была настройка свойств для myView) и загрузите его в свой VFL, чтобы использовать self.myView в тексте VFL, вы, вероятно, получите исключение, когда autolayout попытается проанализировать ваш текст VFL. Это связано с точечным обозначением в словарных клавишах и системой, пытающейся использовать valueForKeyPath:. См. здесь похожие вопросы и ответы.

Ответ 2

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

myView.translatesAutoresizingMaskIntoConstraints = NO;

чтобы этот пример работал. Спасибо Userxxx, Rob M. и особенно larsacus за объяснение и код здесь, это было бесценно.

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

UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
myView.translatesAutoresizingMaskIntoConstraints = NO;  //This part hung me up 
[self.view addSubview:myView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[myView(>=200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:[myView(==200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

Ответ 3

Быстрая версия

Обновлен для Swift 3

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

Ширина и высота

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

Центр контейнера

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

Код котла

override func viewDidLoad() {
    super.viewDidLoad()

    // set up the view
    let myView = UIView()
    myView.backgroundColor = UIColor.blue
    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView)

    // Add constraints code here (choose one of the methods below)
    // ...
}

Способ 1: Стиль привязки

// width and height
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true

// center in container
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

Способ 2: стиль NSLayoutConstraint

// width and height
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true

// center in container
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true

Примечания

  • Якорный стиль является предпочтительным методом поверх NSLayoutConstraint Style, однако он доступен только для iOS 9, поэтому, если вы поддерживаете iOS 8, вы должны использовать NSLayoutConstraint Style.
  • См. также документацию Programmatically Creating Constraints.
  • См. этот ответ для аналогичного примера добавления ограничения пиннинга.

Ответ 4

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

Ответ 5

Почему я не могу использовать свойство типа self.myView вместо локальной переменной, например myView?

попробуйте использовать:

NSDictionaryOfVariableBindings(_view)

вместо self.view

Ответ 6

Также обратите внимание, что из iOS9 мы можем программно "ограничить" программными "более краткими и более легкими для чтения" с использованием подклассов нового вспомогательного класса NSLayoutAnchor.

Пример из документа:

[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;