Ширина и высота равны его 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()

при создании пользовательского 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)]];

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]))
    }

}

Если вы изменили код только так, чтобы он соответствовал 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),
            ])
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.