Swift: UIButton не активируется, проблемы NSLayoutConstraint heightAnchor - PullRequest
0 голосов
/ 11 июля 2020

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

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

У меня есть попытался установить якоря высоты NSLayoutConstraint, и он sh выведет UIButtons из их соответствующих представлений.

class ViewController: UIViewController {

// letterGuess
// usedLetters
// score/lives

var scoreLabel: UILabel!
var answerLabel: UILabel!
var characterButtons = [UIButton]()

var score = 0 {
    didSet {
        scoreLabel.text = "Score: \(score)"
    }
}
override func loadView() {
    view = UIView()
    view.backgroundColor = .white
    
    scoreLabel = UILabel()
    scoreLabel.translatesAutoresizingMaskIntoConstraints = false
    scoreLabel.textAlignment = .right
    scoreLabel.font = UIFont.systemFont(ofSize: 24)
    scoreLabel.text = "Score: 0"
    view.addSubview(scoreLabel)
    
    answerLabel = UILabel()
    answerLabel.translatesAutoresizingMaskIntoConstraints = false
    answerLabel.font = UIFont.systemFont(ofSize: 24)
    answerLabel.text = "ANSWER"
    answerLabel.numberOfLines = 1
    answerLabel.textAlignment = .center
    answerLabel.setContentHuggingPriority(UILayoutPriority(1), for: .vertical)
    view.addSubview(answerLabel)
    
    let buttonsView = UIView()
    buttonsView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(buttonsView)
    
    let row1View = UIView()
    row1View.translatesAutoresizingMaskIntoConstraints = false
    buttonsView.addSubview(row1View)
    
    let row2View = UIView()
    row2View.translatesAutoresizingMaskIntoConstraints = false
    row2View.setContentHuggingPriority(.defaultLow, for: .vertical)
    buttonsView.addSubview(row2View)
    
    let row3View = UIView()
    row3View.translatesAutoresizingMaskIntoConstraints = false
    buttonsView.addSubview(row3View)

    
    NSLayoutConstraint.activate([
        scoreLabel.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
        scoreLabel.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: 0),
        
        answerLabel.topAnchor.constraint(equalTo: scoreLabel.bottomAnchor, constant: 25),
        answerLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        
        buttonsView.widthAnchor.constraint(equalToConstant: 1000),
        buttonsView.heightAnchor.constraint(equalToConstant: 300),
        buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        buttonsView.topAnchor.constraint(equalTo: answerLabel.bottomAnchor, constant: 20),
        buttonsView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -20),
        
        row1View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
        row1View.topAnchor.constraint(equalTo: buttonsView.topAnchor),
        row1View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
        //row1View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
        //row1View.heightAnchor.constraint(equalToConstant: 100),
        
        
        row2View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
        row2View.topAnchor.constraint(equalTo: row1View.bottomAnchor),
        row2View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
        //row2View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
        //row2View.heightAnchor.constraint(equalToConstant: 100),
        
        row3View.leftAnchor.constraint(equalTo: buttonsView.leftAnchor),
        row3View.topAnchor.constraint(equalTo: row2View.bottomAnchor),
        row3View.widthAnchor.constraint(equalTo: buttonsView.widthAnchor),
        //row3View.heightAnchor.constraint(equalTo: buttonsView.heightAnchor, multiplier: 0.333, constant: 0),
        //row3View.heightAnchor.constraint(equalToConstant: 100),
       
        
        
    
    ])
    
    let width = 100
    let height = 100
    var i = 10
    
    for row in 0..<3 {
        print(row)
        switch row {
        case 0:
            i = 10
        case 1:
            i = 9
        case 2:
            i = 7
        default:
            return
        }
        for col in 0..<i {
            let characterButton = UIButton(type: .system)
            characterButton.titleLabel?.font = UIFont.systemFont(ofSize: 36)
            
            characterButton.layer.borderWidth = 1
            characterButton.layer.borderColor = UIColor.lightGray.cgColor
            characterButton.layer.backgroundColor = UIColor.white.cgColor
            characterButton.setTitle("#", for: .normal)

            let frame = CGRect(x: col * width, y: row * height, width: width, height: height)
            characterButton.frame = frame
            
            switch row {
            case 0:
                print(row)
                print("row 1")
                row1View.addSubview(characterButton)
            case 1:
                print(row)
                print("row 2")
                row2View.addSubview(characterButton)
            case 2:
                print(row)
                print("row 3")
                row3View.addSubview(characterButton)
            default:
                print("defualt")
                return
            }
            
            characterButtons.append(characterButton)
            
            
            characterButton.addTarget(self, action: #selector(characterTapped), for: .touchUpInside)
        }
    }
    
   buttonsView.backgroundColor = .purple
    row1View.backgroundColor = .red
    row2View.backgroundColor = .yellow
    row3View.backgroundColor = .green

}

Ответы [ 2 ]

0 голосов
/ 13 июля 2020

Использование UIStackView s дает множество преимуществ ... прежде всего тот факт, что они могут использоваться для автоматического упорядочивания и изменения размера подвидов, что упрощает адаптацию макета к различным устройствам и разным размерам экрана.

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

class CharacterButton: UIButton {
    
    // this will automatically set the font size for the button
    // if the button width >= 100, font size will be maxSize
    // if it's less than 100, font size will be proportional
    // with a minimum font size of 20
    
    // these are declared as "var" so they can be changed at run-time if desired
    var maxSize: CGFloat = 36
    var minSize: CGFloat = 20
    var forWidth: CGFloat = 100
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    func commonInit() -> Void {
        
        // set title colors
        setTitleColor(.blue, for: .normal)
        setTitleColor(.lightGray, for: .highlighted)
        
        // maybe change title color when disabled?
        //setTitleColor(.darkGray, for: .disabled)
        
        // give it a border
        layer.borderWidth = 1
        layer.borderColor = UIColor.lightGray.cgColor
        layer.backgroundColor = UIColor.white.cgColor
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        let fSize = min(max(minSize, bounds.size.width / forWidth * maxSize), maxSize)
        titleLabel?.font = UIFont.systemFont(ofSize: fSize)
    }
    
}

class HangManViewController: UIViewController {
    
    // letterGuess
    // usedLetters
    // score/lives
    
    var scoreLabel: UILabel!
    var answerLabel: UILabel!
    var characterButtons = [UIButton]()
    
    var score = 0 {
        didSet {
            scoreLabel.text = "Score: \(score)"
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        
        // create and add the Score label
        scoreLabel = UILabel()
        scoreLabel.translatesAutoresizingMaskIntoConstraints = false
        scoreLabel.textAlignment = .right
        scoreLabel.font = UIFont.systemFont(ofSize: 24)
        scoreLabel.text = "Score: 0"
        view.addSubview(scoreLabel)
        
        // create and add the Answer label
        answerLabel = UILabel()
        answerLabel.translatesAutoresizingMaskIntoConstraints = false
        answerLabel.font = UIFont.systemFont(ofSize: 24)
        answerLabel.text = "ANSWER"
        answerLabel.numberOfLines = 1
        answerLabel.textAlignment = .center
        answerLabel.setContentHuggingPriority(UILayoutPriority(1), for: .vertical)
        view.addSubview(answerLabel)
        
        // create a view to hold the buttons
        let buttonsView = UIView()
        buttonsView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(buttonsView)
        
        // create a vertical "outer" stack view
        let outerStack = UIStackView()
        outerStack.axis = .vertical
        outerStack.distribution = .fillEqually

        // add it to the buttons holder view
        outerStack.translatesAutoresizingMaskIntoConstraints = false
        buttonsView.addSubview(outerStack)
        
        // create three "row" stack views
        let row1Stack = UIStackView()
        row1Stack.axis = .horizontal
        row1Stack.distribution = .fillEqually
        
        let row2Stack = UIStackView()
        row2Stack.axis = .horizontal
        row2Stack.distribution = .fillEqually
        
        let row3Stack = UIStackView()
        row3Stack.axis = .horizontal
        row3Stack.distribution = .fillEqually
        
        // add the 3 "row" stack views to the "outer" stack view
        [row1Stack, row2Stack, row3Stack].forEach {
            outerStack.addArrangedSubview($0)
        }
        
        let g = view.layoutMarginsGuide
        
        NSLayoutConstraint.activate([
            
            // constrain Score label to top-right
            scoreLabel.topAnchor.constraint(equalTo: g.topAnchor),
            scoreLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0),
            
            // constrain Answer label centered horizontally
            answerLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            // and 4-pts above the grid of buttons
            answerLabel.bottomAnchor.constraint(equalTo: buttonsView.topAnchor, constant: -4),

            // constrain buttons holder view Leading / Trailing
            buttonsView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            buttonsView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),

            // constrain buttons holder view Bottom with 20-pts "padding"
            buttonsView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20),
            
            // constrain all 4 sides of "outer" stack view to buttons holder view
            outerStack.topAnchor.constraint(equalTo: buttonsView.topAnchor),
            outerStack.leadingAnchor.constraint(equalTo: buttonsView.leadingAnchor),
            outerStack.trailingAnchor.constraint(equalTo: buttonsView.trailingAnchor),
            outerStack.bottomAnchor.constraint(equalTo: buttonsView.bottomAnchor),
            
        ])
        
        let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map { String($0) }

        var j = 0
        
        // loop through the 3 "rows" adding 10, 9 and 7 buttons
        
        for (thisRow, numButtonsInThisRow) in zip([row1Stack, row2Stack, row3Stack], [10, 9, 7]) {

            for i in 0..<10 {
                
                if i < numButtonsInThisRow {
                    
                    // create a button
                    let characterButton = CharacterButton()
                    
                    // set its title
                    characterButton.setTitle(letters[j], for: .normal)

                    // maybe set button title to "#" when disabled?
                    //characterButton.setTitle("#", for: .disabled)

                    // give button a touchUp target
                    characterButton.addTarget(self, action: #selector(self.characterTapped(_:)), for: .touchUpInside)
                    
                    // add button to current row stack view
                    thisRow.addArrangedSubview(characterButton)
                    
                    // add button to characterButtons Array
                    characterButtons.append(characterButton)

                    // increment j
                    j += 1
                    
                } else {
                    
                    // we're past the number of character buttons that should be on this row
                    // so "fill it out" with bordered views
                    let v = UIView()
                    v.layer.borderWidth = 1
                    v.layer.borderColor = UIColor.lightGray.cgColor
                    v.layer.backgroundColor = UIColor.white.cgColor
                    thisRow.addArrangedSubview(v)
                    
                }
            }

        }
        
        // we want square buttons, so
        //  we only need to set the first button to have a 1:1 height:width ratio
        //  the stack views' fillEqually distribution will handle the rest
        if let v = characterButtons.first {
            v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
        }

        // just so we can see the frame of the answer label
        answerLabel.backgroundColor = .green
        
    }
    
    @objc func characterTapped(_ sender: UIButton) {
        // character button tapped
        
        // get its title
        let s = sender.currentTitle ?? "no title"
        
        // do we want to disable it?
        //sender.isEnabled = false

        // for now, print the Letter to the debug console
        print("Button \(s) was tapped!")

    }
    
}

Результаты:

enter image description here

enter image description here

введите описание изображения здесь

0 голосов
/ 11 июля 2020

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

// your code
let frame = CGRect(x: col * width, y: row * height, width: width, height: height)

Вам не нужно менять y положение кнопки. Здесь это может быть просто 0 , поскольку каждая строка находится в своем собственном представлении.

// corrected code
let frame = CGRect(x: col * width, y: 0, width: width, height: height)

Вы также должны установить ограничение высоты для каждой имеющейся строки. Все добавленные кнопки находились за пределами родительского представления. Это становится видимым при установке rowView.clipsToBounds = true. Вот почему ваши кнопки не работали.

Я считаю, что в l oop есть проблема, ведь он работает больше, чем нужно, но я не проверял.

Решение вашей проблемы : Я попытался исправить ваш пример кода, и он работает. Отметьте здесь

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

...