Как программно свернуть UIViews?

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

Теперь в состоянии по умолчанию ошибок автоматического раскладки не возникает. Но как только все начинает двигаться, все разваливается. Некоторые из проблем связаны с сохранением высоты изображений, в то время как верхняя константа их представления установлена ​​на 0. Теперь у меня включен .scaleToFill.

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

Теперь мои вопросы: как мне динамически и программно свернуть UIView и UIImageviews?

Теперь я не против ввести множество ограничений вручную, если это работает.

Вот ограничения рассматриваемых представлений (есть и другие)

func setUpLayout() {
    // SuggestionCloud
    suggestionCloud.setConstraints(
        topAnchor: textView.bottomAnchor, topConstant: 0,
        bottomAnchor: bottomMenu.topAnchor, bottomConstant: 0,
        trailingAnchor: view.trailingAnchor, trailingConstant: -10,
        leadingAnchor: view.leadingAnchor, leadingConstant: 10)
        print("Suggestion View frame        :\(suggestionCloud.frame)")

    //WEIGHT_IMAGE_VIEW
    weigtImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
    weigtImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
    weigtImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
    weigtImageView.heightAnchor.constraint(equalToConstant: 150).isActive = true
    weigtImageView.addSubview(weightLabel);
    print("Weight Image View \(weigtImageView.frame)")

    //WEIGHT_LABEL
    weightLabel.trailingAnchor.constraint(equalTo: weigtImageView.trailingAnchor, constant: -30).isActive = true;
    weightLabel.leadingAnchor.constraint(equalTo: weigtImageView.leadingAnchor, constant: 25).isActive = true;
    weightLabel.heightAnchor.constraint(equalTo: weigtImageView.heightAnchor, multiplier: 1).isActive = true;

    //TEXT_VIEW
    textView.topAnchor.constraint(equalTo: weigtImageView.bottomAnchor).isActive = true;
    textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
    textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true;
    textView.heightAnchor.constraint(equalToConstant: 100).isActive = true;
    textView.addSubview(nameTextField)
    textView.addSubview(tagTextField)
    textView.addSubview(setButtonView)

   //TAG_CONTROLLER
    tagController.heightAnchor.constraint(equalToConstant: 110).isActive = true;
    tagController.topAnchor.constraint(equalTo: self.weigtImageView.bottomAnchor).isActive = true;
    tagController.leadingAnchor.constraint(equalTo:  self.view.leadingAnchor, constant : 10).isActive = true
    tagController.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10).isActive = true

    //SET_BUTTON_VIEW
    setButtonView.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true;
    setButtonView.bottomAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true;
    setButtonView.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true;
    setButtonView.widthAnchor.constraint(equalToConstant: 110).isActive = true;


    //NAME_TEXT_FIELD
    nameTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
    nameTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
    nameTextField.topAnchor.constraint(equalTo: textView.topAnchor, constant: 13).isActive = true
    nameTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
    nameTextField.layer.cornerRadius = 8
    nameTextField.backgroundColor = .white;


    //TAG_TEXT_FIELD
    tagTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
    tagTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
    tagTextField.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: -13).isActive = true
    tagTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
    tagTextField.layer.cornerRadius = 8
    tagTextField.backgroundColor = .white

вот настройка viewcontrollers:

   class UIScaleControllerVew: UIViewController, UITextFieldDelegate, SuggenstionCloudDelegate {

let weigtImageView : UIImageView = {
    var imageView = UIImageView(image: UIImage(named: "scaleVisorShadow"));
    imageView.contentMode = .scaleToFill
    imageView.translatesAutoresizingMaskIntoConstraints = false;
    return imageView
}()

let weightLabel : UILabel = {
    let label = UILabel()
    label.text = "135 gr"
    label.font = UIFont(name: "Avenir-Light", size: 50.0)
    label.textAlignment = .right
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}();

let textView : UIView = {
    var view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false;
    return view;
}();
let setButtonView : UIImageView = {
    var imageView = UIImageView(image: UIImage(named: "setButton"))
    imageView.translatesAutoresizingMaskIntoConstraints = false;
    return imageView;
}();



let nameTextField : UITextField = {
    var textField = UITextField();
    textField.tag = 2;
    textField.translatesAutoresizingMaskIntoConstraints = false;
    textField.addTarget(self, action: #selector(nameFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
    return textField;
}();

let tagTextField : UITextField = {
    var textField = UITextField();
    textField.tag = 1;
    textField.translatesAutoresizingMaskIntoConstraints = false;
    textField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
    return textField;
}();

let bottomMenu : UIView = {
    var view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false;
    return view;
}();

let saveButton : UIButton = {
    let button = UIButton()
    button.setImage(UIImage(named: "save"), for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false;
    return button
}();

let microPhoneButton : UIButton = {
    let button = UIButton()
    button.setImage(UIImage(named: "microPhone"), for: .normal)
    button.translatesAutoresizingMaskIntoConstraints = false;
    return button;
}();

let suggestionCloud : SuggenstionCloud = {        
    let cloud =  SuggenstionCloud(image: UIImage(named: "suggestionCloud.png"))
    cloud.translatesAutoresizingMaskIntoConstraints = false;
    return cloud;
}();
let tagController : TagController = {
    let tagController = TagController()
    tagController.translatesAutoresizingMaskIntoConstraints = false
    return tagController;
}()

let scaleModel = ScaleModel.init()

override func viewDidLoad() {
    super.viewDidLoad()
    print("UIScaleController_DidLoad")
    tagTextField.delegate = self
    nameTextField.delegate = self;
    suggestionCloud.delegate = self;
    view.backgroundColor = UIColor(hexString: "8ED7F5")
    view.addSubview(weigtImageView)
    view.addSubview(textView)
    view.addSubview(bottomMenu);
    view.addSubview(suggestionCloud)
    view.addSubview(tagController)
    tagController.isHidden = true;


    setUpLayout()

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}

var didSetUpSuggestionCloud = false
var didSetUpTagController = false
override func viewDidLayoutSubviews() {
    guard !self.didSetUpTagController else {
        return
    }
    guard !self.didSetUpSuggestionCloud else {
        return
    }
    self.didSetUpSuggestionCloud = true
    self.didSetUpTagController = true
};

и вот проблемный код:

@objc func keyboardWillShowNotification(notification: Notification ) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {

        // collapse and hide bottom view
        bottomMenu.contentMode = .scaleToFill;
        bottomMenu.heightAnchor.constraint(equalToConstant: 0).isActive = true;
        bottomMenu.isHidden = true

        // collapse and hide top view
        weigtImageView.contentMode = .scaleToFill;
        weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true;
        weigtImageView.isHidden = true;



        // spawn my tag view
        tagController.topAnchor.constraint(equalTo: self.textView.bottomAnchor).isActive = true;
        tagController.bottomAnchor.constraint(equalTo: suggestionCloud.topAnchor).isActive = true
        tagController.isHidden = false;

        // set textviews new constraints
        textView.bottomAnchor.constraint(equalTo: tagController.topAnchor).isActive = true;
        // set middleView's new constraints
        suggestionCloud.topAnchor.constraint(equalTo: tagController.bottomAnchor).isActive = true;
        suggestionCloud.bottomAnchor.constraint(equalTo: bottomMenu.topAnchor, constant: -keyboardSize.height).isActive = true

        self.view.layoutIfNeeded()
    }
}

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

Вот несколько фотографий того, что происходит на данный момент:

Итак, когда клавиатура поднята: WeightView свернут: облако предложений и текст перемещаются вверх. Если добавлен тег, новое представление с именем tagController должно быть размещено между texView и SuggesitonCloud. Ластил, клавиатуру нужно снова свернуть.

Я добавлю несколько скриншотов Как программно свернуть UIViews?

Как программно свернуть UIViews?

Как программно свернуть UIViews?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
100
3

Ответы 3

Если у вас нет четкой ссылки на ваши представления, которые будут выпущены, достаточно выполнить следующее:

if view2Breleased.superview != nil {
    view2Breleased.removeFromSuperview()
}

Вид исчезнет и выйдет из памяти.

Если вы пока не знаете, что такое сильная ссылка, просто попробуйте код, который я написал. Просмотры все равно исчезнут.

(Сильная ссылка означает, что вы назначили представление переменной, которая переживает выполнение кода view2Breleased.removeFromSuperview() и выход из вызова функции, где находится код view2Breleased.removeFromSuperview().)

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

Arne Oldenhave 04.01.2019 12:08

Спасибо за обновления

Arne Oldenhave 04.01.2019 13:12

Одна вещь, на которую вы могли бы обратить внимание, - это повторяющиеся ограничения.

Каждый раз, когда вы вызываете weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true, вы создаете новое ограничение. Это не заменяет автоматически любые предыдущие активные ограничения высоты.

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

Представления стека
Представления стека могут быть полезны в вашей ситуации, потому что они автоматически сворачивают представления, для которых для isHidden установлено значение true. Я думаю, что до тех пор, пока прямые подпредставления StackView имеют внутренний размер контента (например, правильные внутренние ограничения), они должны быть правильно размещены StackView.

Спасибо! Тогда я попробую sackViews!

Arne Oldenhave 04.01.2019 12:35

Вы можете изменить константу ограничения heightAnchor следующим образом:

import Foundation
import UIKit

class TestController : UIViewController {

var myViewHeightConstraint : NSLayoutConstraint!

let myView : UIControl = {
    let newView = UIControl()
    newView.translatesAutoresizingMaskIntoConstraints = false
    newView.backgroundColor = .red
    return newView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .white


    self.myView.addTarget(self, action: #selector(viewClicked), for: .touchUpInside)
    self.myViewHeightConstraint = myView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)

    setup()
}

func setup(){

    view.addSubview(myView)

    myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    myView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    self.myViewHeightConstraint.isActive = true

}

@objc func viewClicked() {
    self.myViewHeightConstraint.constant = -self.myView.frame.size.height
}

}

В моем примере я установил константу на минус высоту высоты кадра представления, что фактически сжимает ее.

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

Arne Oldenhave 04.01.2019 12:53

@ArneOldenhave Я не совсем уверен, как выглядит ваше мнение. Если бы вы могли набросать что-нибудь, это могло бы помочь. Из вашего комментария я понял, что у вас много разных представлений, которые содержат много подвидов? Если это так, то, вероятно, было бы чище создать подкласс UIView и установить все это там.

nicocappa 04.01.2019 12:59

Спасибо, я обновлю свой вопрос несколькими скринами

Arne Oldenhave 04.01.2019 13:07

Другие вопросы по теме