简体   繁体   中英

Swift: UIStackView of UIControls with selector method that doesn't fire

Introduction

I'm creating an app which uses a custom view in which I have a UIStackView to sort out 5 UIControls. When a user taps one of the UIControls an underscore line gets animated, sliding under the tapped UIControl.

However, for some reason the method/selector for these UIControls no longer gets called. I believe this has to do with that I updated my Mac to the macOS (and Xcode) update released this week (wk.44). (updated from swift 4.2 to swift 4.2.1). Before the updated this animation and selector worked perfectly. But I'm not sure. And I'm now completely stuck on what I'm doing wrong.

Context

  • I created a playground and scaled down everything as much as I could and the issue persists.

  • I have tried to define the UIStackView in the global scope of my SetupView class but it doesn't change anything. So I believe it is not an issue of the stackView or its subviews being deallocated?

  • Below I've provided my UIControl subclass and my SetupView (UIView subclass) that I use. I've created a playground so you may copy paste in Xcode playground to test if you want.

Question

  • Why doesn't the method goalViewControlTapped(_ sender: SetupViewControl) get called?

Code

import UIKit
import PlaygroundSupport

class SetupViewControl: UIControl {

    let titleLabel : UILabel = {
        let lbl = UILabel()
        lbl.font = UIFont(name: "Futura", size: 14)
        lbl.textColor = .white
        lbl.backgroundColor = .clear
        lbl.textAlignment = .center
        lbl.translatesAutoresizingMaskIntoConstraints = false
        return lbl
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLabel()
        layer.cornerRadius = 5
    }

    fileprivate func setupLabel() {
        addSubview(titleLabel)
        titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5).isActive = true
        titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    }

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

    override var isHighlighted: Bool {
        didSet {
            UIView.animate(withDuration: 0.12) {
                self.backgroundColor = self.isHighlighted ? UIColor.lightGray : UIColor.clear
            }
        }
    }
}

class SetupView: UIView {

    let dataModel : [String] = ["2 weeks", "1 month", "2 months", "6 months", "1 year"]
    var selectionLineCenterX : NSLayoutConstraint!

    let selectionLine = UIView()
    let labelZero = SetupViewControl()
    let labelOne = SetupViewControl()
    let labelTwo = SetupViewControl()
    let labelThree = SetupViewControl()
    let labelFour = SetupViewControl()
    let labelFive = SetupViewControl()

    lazy var controlArray = [self.labelZero, self.labelOne, self.labelTwo, self.labelThree, self.labelFour, self.labelFive]

    init(frame: CGRect, color: UIColor) {
        super.init(frame: frame)
        self.backgroundColor = color
        setupView()
    }

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

    fileprivate func setupView() {
        layer.cornerRadius = 0
        layer.borderColor = UIColor.black.cgColor
        layer.borderWidth = 1
        setupLabelText()
        setupControlsInStackView()
    }

    fileprivate func setupLabelText() {
        for num in 0...(dataModel.count - 1) {
            controlArray[num].titleLabel.text = dataModel[num]
        }
    }

    // let stackView = UIStackView(frame: .zero) I have tried to declare the stackView here but it doesn't fix my issue.
    func setupControlsInStackView() {
        var stackViewArray = [SetupViewControl]()
        for num in 0...(dataModel.count - 1) {
            controlArray[num].isUserInteractionEnabled = true
            controlArray[num].addTarget(self, action: #selector(goalViewControlTapped(_:)), for: .touchUpInside)
            stackViewArray.append(controlArray[num])
        }
        let stackView = UIStackView(arrangedSubviews: stackViewArray)
        stackView.alignment = .fill
        stackView.distribution = .fillEqually
        stackView.axis = .horizontal
        stackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(stackView)
        stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8).isActive = true
        stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8).isActive = true
        stackView.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true

        addSubview(selectionLine)
        selectionLine.backgroundColor = .white
        selectionLine.translatesAutoresizingMaskIntoConstraints = false
        selectionLine.heightAnchor.constraint(equalToConstant: 1).isActive = true
        selectionLine.topAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
        selectionLine.widthAnchor.constraint(equalToConstant: 50).isActive = true
        selectionLineCenterX = selectionLine.centerXAnchor.constraint(equalTo: leadingAnchor, constant: -100)
        selectionLineCenterX.isActive = true
    }

    @objc fileprivate func goalViewControlTapped(_ sender: SetupViewControl) {
        print("This is not getting printed!!!")
        selectionLineCenterX.isActive = false
        selectionLineCenterX = selectionLine.centerXAnchor.constraint(equalTo: sender.centerXAnchor)
        selectionLineCenterX.isActive = true
        UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5, options: .curveEaseIn, animations: {
            self.layoutIfNeeded()
        }, completion: nil)
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        let testView = SetupView(frame: .zero, color: UIColor.blue)
        view.addSubview(testView)
        testView.translatesAutoresizingMaskIntoConstraints = false
        testView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        testView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        testView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        testView.widthAnchor.constraint(equalToConstant: 365).isActive = true
    }
}
// For live view in playground
let vc = ViewController()
vc.preferredContentSize = CGSize(width: 375, height: 812)
PlaygroundPage.current.liveView = vc

Thanks for reading my question.

Does your UIStackView show as having an ambiguous layout when you open the view debugger? If so, that may be causing the internal views to not receive the touch events.

You can provide UIStackView with either:

x and y constraints only

or

x , y , width and height .

In the above case the height constraint is missing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM