2016-12-31 4 views
4

У меня есть UIViewController по имени LoginViewController. Я хочу построить представление об этом LoginViewControllerполностью программно в обычном UIView классе под названием LoginView вместо того, чтобы строить все элементы в моем LoginViewController. Таким образом я предотвращаю код «View» в классе Controller (MVC).Установить представление UIViewController в пользовательский класс UIView без раскадровки/наконечника

В коде ниже я устанавливаю вид моей LoginViewController к моему LoginView, который только для простоты содержит 2 UILabels

class LoginViewController: UIViewController { 

override func loadView() { 
    super.loadView() 

    self.view = LoginView(frame: CGRect.zero) 
} 

Класс LoginView инициализирует обе этикетки и должны установить некоторые ограничения.

class LoginView: UIView { 

var usernameLabel: UILabel! 
var passwordLabel: UILabel! 


override init (frame : CGRect) { 
    super.init(frame : frame) 

    setupLabels() 
} 

convenience init() { 
    self.init(frame:CGRect.zero) 
} 

required init?(coder aDecoder: NSCoder) { 
    super.init(coder: aDecoder) 
} 

private func setupLabels(){ 
    //Init labels and set a simple text 
    self.usernameLabel = UILabel() 
    self.usernameLabel.text = "Username" 
    self.passwordLabel = UILabel() 
    self.passwordLabel.text = "Password" 

    //Set constraints which aren't possible since there is no contentView, perhaps using the frame? 
    } 
} 

Это не работает, так как ограничивающая зрения являются 0. Однако я не мог найти какой-либо ресурс, который дает представление в ли это возможно, поэтому я попытался мой подход, который не работал.

Как вы устанавливаете представление UIViewController на пользовательский UIView, который делается программно? Или приведенный выше фрагмент рекомендуется?

Это рабочий раствор на основе ответа Jadar в:

class LoginViewController: UIViewController { 

    override func loadView() { 
     view = LoginView() 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad()   
    // Do any additional setup after loading the view. 

    } 
} 

class LoginView: UIView { 

var usernameLabel: UILabel! 
var passwordLabel: UILabel! 

override init(frame: CGRect) { 
    super.init(frame: frame) 

    self.usernameLabel = UILabel() 
    self.usernameLabel.text = "Username" 
    self.passwordLabel = UILabel() 
    self.passwordLabel.text = "Password" 

    addSubview(usernameLabel) 
    addSubview(passwordLabel) 

    if let superview = usernameLabel.superview{ 
     //Setting AutoLayout using SnapKit framework 
     usernameLabel.snp.makeConstraints { (make) in 
      make.center.equalTo(superview) 
     } 
    } 
} 

Результат:

Result

+1

Код в обновленной вопрос не является правильным для контроллера представления. Не устанавливайте self.view в viewDidLoad. Сделайте это в loadView, как и в оригинале. – rmaddy

+0

Благодарим вас за отзыв! @rmaddy – Hapeki

ответ

3

Это выглядит на самом деле есть два вопроса. Один из них - лучший способ программной настройки ViewController. Другой, как настроить программный вид.

Во-первых, лучший способ иметь ViewController с использованием другого подкласса UIView - инициализировать и назначить его в методе loadView. Per Apple's docs:

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

Это будет выглядеть примерно так:

class LoginViewController: UIViewController {  
    override func loadView() { 
     // Do not call super! 
     view = LoginView() 
    } 
} 

Таким образом, вы не должны иметь дело с просеиванием их, как View Controller сам должен заботиться о нем (как это происходит с его собственной UIView).

Помните, что не звоните super.loadView() или контроллер будет смущен. Кроме того, в первый раз, когда я попробовал это, у меня появился черный экран, потому что я забыл позвонить window.makeKeyAndVisible() в своем App Delegate. В этом случае представление никогда не было добавлено в иерархию окон. Вы всегда можете использовать кнопку интроспектора просмотра в Xcode, чтобы узнать, что происходит.

Во-вторых, вам нужно будет позвонить self.addSubview(_:) в ваш подкласс UIView, чтобы они отображались. Когда вы добавите их в виде подзонов, вы можете добавить ограничения с помощью NSLayoutConstraint.

private func setupLabels(){ 
    // Initialize labels and set their text 
    usernameLabel = UILabel() 
    usernameLabel.text = "Username" 
    usernameLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB 
    addSubview(usernameLabel) 

    passwordLabel = UILabel() 
    passwordLabel.text = "Password" 
    passwordLabel.translatesAutoresizingMaskIntoConstraints = false // Necessary because this view wasn't instantiated by IB 
    addSubview(passwordLabel) 

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel])) 
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel])) 

    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]", options: [], metrics: nil, views: ["view":usernameLabel])) 
    NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[view]", options: [], metrics: nil, views: ["view":passwordLabel])) 
} 

Для получения дополнительной информации о визуальном языке формат, используемый для создания ограничений см VFL Guide

+0

Это правильный ответ. – matt

+0

Это вообще не отвечает на вопрос. ОП изначально делал это. Это не проблема. Проблема заключается в реализации пользовательского представления, а не в том, как создается представление в контроллере представления. – rmaddy

+0

На самом деле, я думаю, мы оба неправильно поняли вопрос ОП. Похоже, что реальная проблема заключается в том, что его ярлыки не отображаются, что он приписывает своим фреймам zero'd. Но это потому, что 1, он никогда не добавляет их как подпункты и 2, он никогда не меняет их и не позиционирует. Я обновил свой ответ, чтобы отразить как вопросы контроллера, так и макеты. – Jadar

2

Override метод layoutSubviews обновить кадры из подвидов внутри ваш пользовательский вид.

И никогда не звоните super.loadView(). Это документировано для метода loadView.

+0

Благодарим вас за ответ. Как обновить фреймы с помощью AutoLayout (SnapKit) в layoutSubviews, если мой кадр - CGRect.zero? Я не могу найти способ сделать центр passwordLabel своим супервизом .. так как его нет? – Hapeki

+0

Если вы хотите использовать автоматическую компоновку, установите исходный кадр на ненулевой фрейм и настройте автоматическую компоновку, когда вы создадите подпрограммы initiallly. Тогда вам не нужно реализовывать 'layoutSubviews'. – rmaddy

+0

Этот ответ не дает адекватной информации, чтобы помочь ОП исправить его/ее проблему. – Jadar

2

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

override func viewDidLoad() { 
     super.viewDidLoad() 
     let newView = LoginView(frame: view.bounds) 
     view.addSubview(newView) 
    } 
+0

Благодарим вас за ответ. Я отредактировал свой пост с потенциальным решением, основанным на вашем ответе. Если это решение будет принято сообществом в качестве действительного решения, я помечаю этот ответ как принятый. Я не уверен, что вызов viewDidLoad для настройки представления - это хорошо, поскольку Apple заявляет: Внедрить loadView для создания иерархии представлений программно, без использования ниба. Внедрите viewDidLoad, чтобы выполнить дополнительную настройку после загрузки представления, как правило, из наконечника. – Hapeki

+2

Это одно из возможных решений, но исходное решение для использования loadView - это подходящее место для установки вида контроллера представления на пользовательское представление. – rmaddy

+0

Если вы хотите использовать loadView, вам нужно инициализировать свое пользовательское представление с помощью фрейма, отличного от текущего контроллера представления, потому что это еще не загружено, вы можете установить 'UIScreen.main.bounds' в кадр LoginView. Мне нужен пример, просто дайте мне знать. – DariusV

Смежные вопросы