Ограничения кнопок в StackView (Swift программно) - PullRequest
0 голосов
/ 04 февраля 2019

Я новичок в настройке StackViews и кнопок программно.У меня странное поведение с моими ограничениями. Я не могу понять, что я делаю неправильно.Такое ощущение, что я упускаю что-то простое.Любая помощь очень ценится!

Я пытаюсь добавить две кнопки в StackView для создания пользовательской панели вкладок.Однако, когда я добавляю ограничения к кнопкам, они отображаются за пределами нижней части StackView.Как будто верхнее ограничение изображения Земли не работает.Есть идеи?См. Изображение и код ниже.

enter image description here

// View to put in the StackView
class ProfileBottomTabBarView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.translatesAutoresizingMaskIntoConstraints = false
        self.backgroundColor = .blue
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

// Calculate the screen height
public var screenHeight: CGFloat {
    return UIScreen.main.bounds.height
}

// StackView height set to a proporation of screen height
let stackViewHeight = screenHeight * 0.07

// Views to put in the StackView
let profileIconView = ProfileBottomTabBarView()
let actIconView = ActBottomTabBarView()
let achieveIconView = AchieveBottomTabBarView()
let growIconView = GrowBottomTabBarView()

// Buttons to put in the Views
let profileButton = UIButton(type: .system)
let actButton = UIButton(type: .system)
let achieveButton = UIButton(type: .system)
let growButton = UIButton(type: .system)

let profileButtonText = UIButton(type: .system)
let actButtonText = UIButton(type: .system)
let achieveButtonText = UIButton(type: .system)
let growButtonText = UIButton(type: .system)

// Stackview setup
lazy var stackView: UIStackView = {

    let stackV = UIStackView(arrangedSubviews: [profileIconView, actIconView, achieveIconView, growIconView])

    stackV.translatesAutoresizingMaskIntoConstraints = false
    stackV.axis = .horizontal
    stackV.spacing = 20
    stackV.distribution = .fillEqually

    return stackV
}()


override func viewDidLoad() {
    super.viewDidLoad()

view.backgroundColor = .black

    // Add StackView
    view.addSubview(stackView)

    stackView.bottomAnchor.constraint(equalTo: view.safeBottomAnchor).isActive = true
    stackView.leadingAnchor.constraint(equalTo: view.safeLeadingAnchor).isActive = true
    stackView.trailingAnchor.constraint(equalTo: view.safeTrailingAnchor).isActive = true

    // Set height of the bottom tab bar as a proportion of the screen height.
    stackView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true

    profileIconView.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
    profileIconView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
    profileIconView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true


    // Add Buttons to the View
    profileIconView.addSubview(profileButton)
    profileIconView.addSubview(profileButtonText)

    profileButton.translatesAutoresizingMaskIntoConstraints = false
    profileButtonText.translatesAutoresizingMaskIntoConstraints = false


    // Profile Button with Earth Image Setup
    profileButton.setImage(UIImage(named: "earthIcon"), for: .normal)
    profileButton.imageView?.contentMode = .scaleAspectFit

    profileButton.topAnchor.constraint(equalTo: profileIconView.topAnchor).isActive = true
    profileButton.bottomAnchor.constraint(equalTo: profileButtonText.topAnchor).isActive = true
    profileButton.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor).isActive = true

    //Set height of icon to a proportion of the stackview height
    let profileButtonHeight = stackViewHeight * 0.8
    profileButton.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, constant: profileButtonHeight).isActive = true

    profileButton.widthAnchor.constraint(equalToConstant: profileButtonHeight).isActive = true
    profileButton.imageView?.widthAnchor.constraint(equalToConstant: profileButtonHeight)
    profileButton.imageView?.heightAnchor.constraint(equalToConstant: profileButtonHeight)


    // Profile Text Button Setup

    profileButtonText.setTitle("Profile", for: .normal)
    profileButtonText.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
    profileButtonText.setTitleColor(.white, for: .normal)

    profileButtonText.topAnchor.constraint(equalTo: profileButton.bottomAnchor).isActive = true
    profileButtonText.bottomAnchor.constraint(equalTo: profileIconView.bottomAnchor).isActive = true
    profileButtonText.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor).isActive = true

    //Set height of icon to a proportion of the stackview height
    let profileButtonTextHeight = stackViewHeight * 0.2
    profileButton.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, constant: profileButtonTextHeight).isActive = true
    profileButtonText.widthAnchor.constraint(equalToConstant: 40).isActive = true

}

1 Ответ

0 голосов
/ 04 февраля 2019

Несколько вещей не так с вашими ограничениями ...

Вы вычисляете высоту / ширину и используете их в качестве констант, но эти значения могут (почти наверняка) изменяться в зависимости от жизненного цикла представления.

Лучше использовать только связанные ограничения.Например:

        // constrain profile image button top, centerX and width relative to the iconView
        profileButton.topAnchor.constraint(equalTo: profileIconView.topAnchor),
        profileButton.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor),
        profileButton.widthAnchor.constraint(equalTo: profileIconView.widthAnchor, multiplier: 1.0),

        // constrain profile text button bottom, centerX and width relative to the iconView
        profileButtonText.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor),
        profileButtonText.widthAnchor.constraint(equalTo: profileIconView.widthAnchor, multiplier: 1.0),
        profileButtonText.bottomAnchor.constraint(equalTo: profileIconView.bottomAnchor),

        // constrain bottom of image button to top of text button (with a padding of 4-pts, change to suit)
        profileButton.bottomAnchor.constraint(equalTo: profileButtonText.topAnchor, constant: -4.0),

        // constrain height of text button to 20% of height of iconView
        profileButtonText.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, multiplier: 0.2),

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

class BottomTabBarView: UIView {

    var theImageButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.imageView?.contentMode = .scaleAspectFit
        return v
    }()

    var theTextButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
        v.setTitleColor(.white, for: .normal)
        return v
    }()

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

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

    convenience init(withImageName imageName: String, labelText: String, bkgColor: UIColor) {

        self.init()
        self.commonInit()
        theImageButton.setImage(UIImage(named: imageName), for: .normal)
        theTextButton.setTitle(labelText, for: .normal)
        backgroundColor = bkgColor

    }

    func commonInit() -> Void {
        self.translatesAutoresizingMaskIntoConstraints = false

        addSubview(theImageButton)
        addSubview(theTextButton)

        NSLayoutConstraint.activate([

            // constrain profile image button top, centerX and width of the iconView
            theImageButton.topAnchor.constraint(equalTo: topAnchor),
            theImageButton.centerXAnchor.constraint(equalTo: centerXAnchor),
            theImageButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0),

            // constrain profile text button bottom, centerX and width of the iconView
            theTextButton.centerXAnchor.constraint(equalTo: centerXAnchor),
            theTextButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0),
            theTextButton.bottomAnchor.constraint(equalTo: bottomAnchor),

            // constrain bottom of image button to top of text button
            theImageButton.bottomAnchor.constraint(equalTo: theTextButton.topAnchor, constant: -4.0),

            // set text button height to 20% of view height (instead of using intrinsic height)
            theTextButton.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.2),

            ])

    }

}

Теперь вы можете создавать каждыйпредставление одной строкой, например:

    profileIconView = BottomTabBarView(withImageName: "earthIcon", labelText: "Profile", bkgColor: .blue)

И ваш класс контроллера представления становится намного проще / чище:

class BenViewController: UIViewController {

    // Views to put in the StackView
    var profileIconView = BottomTabBarView()
    var actIconView = BottomTabBarView()
    var achieveIconView = BottomTabBarView()
    var growIconView = BottomTabBarView()

    // Stackview setup
    lazy var stackView: UIStackView = {
        let stackV = UIStackView(arrangedSubviews: [profileIconView, actIconView, achieveIconView, growIconView])

        stackV.translatesAutoresizingMaskIntoConstraints = false
        stackV.axis = .horizontal
        stackV.spacing = 20
        stackV.distribution = .fillEqually

        return stackV
    }()


    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .black

        profileIconView = BottomTabBarView(withImageName: "earthIcon", labelText: "Profile", bkgColor: .blue)
        actIconView = BottomTabBarView(withImageName: "actIcon", labelText: "Action", bkgColor: .brown)
        achieveIconView = BottomTabBarView(withImageName: "achieveIcon", labelText: "Achieve", bkgColor: .red)
        growIconView = BottomTabBarView(withImageName: "growIcon", labelText: "Grow", bkgColor: .purple)

        // Add StackView
        view.addSubview(stackView)

        NSLayoutConstraint.activate([

            // constrain stackView to bottom, leading and trailing (to safeArea)
            stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),

            // Set height of the stackView (the bottom tab bar) as a proportion of the view height (7%).
            stackView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.07),

            ])

    }
}
...