Ширина и высота равны его superView, используя программно автоматическое размещение?

83

Я искал много фрагментов в сети и до сих пор не могу найти ответ на свою проблему. Мой вопрос в том, что у меня есть scrollView (SV), и я хочу программно добавить кнопку внутри scrollView (SV) с той же шириной и высотой своего супервизора, который является scrollView (SV), чтобы при повороте пользователя кнопка устройства имела тот же фрейм scrollView (SV). как сделать NSLayout / NSLayoutConstraint? благодаря

Бордз
источник

Ответы:

125

Если кто-то ищет решение Swift - я бы создал расширениеUIView Swift, которое будет помогать вам каждый раз, когда вы хотите привязать фрейм subviews к его границам superviews:

Swift 2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Swift 3:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Swift 4.2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
        self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
        self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
        self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true

    }
}

Тогда просто назовите это так :

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindFrameToSuperviewBounds()
MadNik
источник
при создании пользовательского UIView с использованием .xib bindFrameToSuperviewBounds следует вызывать в рамках 'required init? (coder aDecoder)' сразу после self.addSubview (self.view)
user1603721 05
Стоит отметить, что решения, использующие визуальный формат, небезопасны. Если, например, вы вызываете это в представлении, которое находится внутри контроллера навигации, показывающем панели навигации и панели инструментов, ваше представление будет находиться под панелью навигации и под панелью инструментов, если оно заходит так далеко вниз.
Энди
Это также работает как решение для Swift 5. Я не мог адаптировать свой пользовательский subView к размеру parentView с помощью AutoLayout. Использование этого после добавления подпредставления работало как шарм.
toni_piu
Решение Swift 4.2 работает хорошо. Вы даже можете сделать его немного короче, удалив constant: 0деталь.
Zyphrax
69

Я не уверен, что это самый эффективный способ сделать это, но он работает ..

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
// initialize


[coverForScrolView addSubview:button];

NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:button
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:coverForScrolView
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:button
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:coverForScrolView
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:button
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:coverForScrolView
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
                                       constraintWithItem:button
                                       attribute:NSLayoutAttributeLeading
                                       relatedBy:NSLayoutRelationEqual
                                       toItem:coverForScrolView
                                       attribute:NSLayoutAttributeLeading
                                       multiplier:1.0f
                                       constant:0.f];
[coverForScrolView addConstraint:width];
[coverForScrolView addConstraint:height];
[coverForScrolView addConstraint:top];
[coverForScrolView addConstraint:leading];
Бордз
источник
4
Было бы намного эффективнее использоватьNSLayoutConstraint.activateConstraints([width, height, top, leading])
Берик
Вы можете использовать[coverForScrolView addConstraints:@[width, height, top, leading]];
Islam Q.
1
стоит отметить (спустя годы) этот код чрезвычайно устарел . теперь намного проще добавлять ограничения - см. любой ответ 2017 года ниже
Fattie
49

Эта ссылка может вам помочь, следуйте инструкциям: http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

РЕДАКТИРОВАТЬ :

используйте следующий фрагмент кода, где subview - это ваш subivew.

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
Uniruddh
источник
4
В этом случае визуальный формат также может быть V:|[subview]|иH:|[subview]|
Густаво Барбоса
4
стоит отметить (спустя годы) этот код чрезвычайно устарел . теперь намного проще добавлять ограничения - см. любой ответ 2017 года ниже
Fattie
19

addConstraintи removeConstraintметоды для UIView будут устаревшими, поэтому стоит использовать «удобства создания ограничений»:

view.topAnchor.constraint(equalTo: superView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0).isActive = true
бериллий
источник
Это хорошо работает. Вы даже можете сделать его немного короче, удалив constant: 0деталь.
Zyphrax
8

Подход №1: через расширение UIView

Вот более функциональный подход в Swift 3+ с предварительным условием вместо print(который может легко исчезнуть в консоли). Этот сообщит об ошибках программиста как о неудачных сборках.

Добавьте это расширение в свой проект:

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

Теперь просто назовите это так:

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindEdgesToSuperview()

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


Подход # 2: использование фреймворка

Если вы много работаете с программными ограничениями в своем проекте, я рекомендую вам проверить SnapKit . Это делает работу с ограничениями намного проще и менее подверженной ошибкам .

Следуйте инструкциям по установке в документации, чтобы включить SnapKit в свой проект. Затем импортируйте его в верхнюю часть файла Swift:

import SnapKit

Теперь вы можете добиться того же с помощью всего этого:

subview.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}
Джихут
источник
6

Swift 3:

import UIKit

extension UIView {

    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}
PLJNS
источник
Если вы изменили код только так, чтобы он соответствовал Swift 3, вам лучше отредактировать исходный ответ плаката, чем публиковать новый ответ (так как это не изменение первоначального намерения плаката). Если у вас недостаточно очков для редактирования, прокомментируйте исходный пост с подсказкой об изменениях, необходимых для соответствия Swift 3. Исходный постер (или кто-то другой, увидев ваш комментарий), вероятно, обновит ответ. Таким образом мы очищаем поток от повторяющихся ответов и устаревшего кода.
Jeehut,
Привет, @Dschee - я полностью с тобой согласен, но мы «ошибаемся». "консенсус" на сайте, к лучшему или худшему, противоположен тому, что вы здесь выражаете. meta.stackoverflow.com/questions/339024/… (Я постоянно игнорирую консенсусное решение, делаю то, что разумно, а потом получаю проблемы из-за модов :))
Fattie
2

Swift 4, используя NSLayoutConstraint:

footerBoardImageView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint  = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 0)
superview.addConstraints([widthConstraint, heightConstraint])
Масих
источник
1

В качестве дополнительного ответа и для тех, кто не возражает против включения сторонних библиотек, библиотека PureLayout предоставляет способ сделать именно это. После установки библиотеки это так же просто, как

myView.autoPinEdgesToSuperviewEdges()

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

Мэтт Пинкстон
источник
1

Я выбрал лучшие элементы из других ответов:

extension UIView {
  /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
  /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
  func bindFrameToSuperviewBounds() {
    guard let superview = self.superview else {
      print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
      return
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
      self.topAnchor.constraint(equalTo: superview.topAnchor),
      self.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
      self.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
      self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
    ])
  }
}

Вы можете использовать его так, например, в своем пользовательском UIView:

let myView = UIView()
myView.backgroundColor = UIColor.red

self.addSubview(myView)
myView.bindFrameToSuperviewBounds()
Зифракс
источник
0

Как продолжение решения @Dschee, вот синтаксис swift 3.0: (Обратите внимание: это не мое решение , я только что исправил его для Swift 3.0)

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}
Джеймс Ларкомб
источник
1
Если вы изменили код только для того, чтобы сделать его совместимым с Swift 3, вам лучше отредактировать исходный ответ плаката, чем публиковать новый ответ (поскольку это не изменение первоначального намерения плаката). Если у вас недостаточно очков для редактирования, прокомментируйте исходный пост с подсказкой об изменениях, необходимых для соответствия Swift 3. Исходный постер (или кто-то другой, увидев ваш комментарий), вероятно, обновит ответ. Таким образом мы очищаем поток от повторяющихся ответов и устаревшего кода.
Jeehut,
Я полностью согласен с вами, @Dschee, но есть (для меня абсурдный) комментарий к Meta о том, что «мы не делаем этого на SO» ... meta.stackoverflow.com/questions/339024/…
Fattie
@JoeBlow Прочитав обсуждение вашей ссылки, я действительно думаю, что это тоже имеет смысл. Я согласен с комментарием Патрика Хо (по вопросу), хотя следует дать новый ответ в сочетании с комментарием к исходному ответу. Затем исходный постер должен обновить свой ответ (чтобы получить будущие положительные голоса) или нет. Спасибо за ссылку!
Jeehut
Да ладно, это большая часть того, почему я оставался верным хитом и бегуном в прошлом. Я беру свой ответ, конвертирую его в текущий синтаксис и продолжаю кодировать. Основная причина, по которой я разместил такой способ, заключается в том, что, когда я учил Swift, меня часто спрашивали, как найти решение в любой текущей версии swift, потому что у новых кодировщиков возникли проблемы с обновлением объявлений своих функций. Это был один из главных источников разочарования, но также и возможность противопоставить два стиля кода. В результате студент смог предсказать аналогичные изменения в других фрагментах кода.
Джеймс Ларкомб,
0

Мне нужно было полностью покрыть супервизор. Другие не стали бы этого делать при смене ориентации. Итак, я написал новый, который работает - с произвольным множителем размера 20. Не стесняйтесь изменять в соответствии с вашими потребностями. Также обратите внимание, что это фактически делает подпредставление намного больше супервизора, которое может отличаться от требований.

extension UIView {
    func coverSuperview() {
        guard let superview = self.superview else {
            assert(false, "Error! `superview` was nil – call `addSubview(_ view: UIView)` before calling `\(#function)` to fix this.")
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        let multiplier = CGFloat(20.0)
        NSLayoutConstraint.activate([
            self.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: multiplier),
            self.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: multiplier),
            self.centerXAnchor.constraint(equalTo: superview.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: superview.centerYAnchor),
            ])
    }
}
Джонни
источник