Copy of UiView and all subviews..Copied UIButton c

2019-07-25 13:33发布

问题:

Essentially I am using this code extension below to copy a view and all its subviews. The copy is successful and I am able to view the copied view. However, the button on each copied view cannot be pressed. The button can only be pressed within the first original (not copied) view. How to get all copied buttons to be active? Is this even possible?

I have already tried .isUserInteractionEnabled on the button and its parent view.

override func viewDidLoad(){
    super.viewDidLoad()
    view.isUserInteractionEnabled = true
    view.addSubview(containerScrollView)
    containerScrollView.addSubview(contentView)
    contentView.addSubview(stackMainView)
    let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
    let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
    let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
    partLabel1.text = "This should sit within part use :)"
    partLabel1.textColor = .white
    partLabel2.text = "This should also sit within part use :)"
    partLabel2.textColor = .white
    contentView.addSubview(button)
    contentView.addSubview(partLabel1)
    contentView.addSubview(partLabel2)
    part.addSubview(button)
    part.addSubview(partLabel1)
    part.addSubview(partLabel2)
    part.bringSubviewToFront(button)
    part.bringSubviewToFront(partUse3Label1)
    part.layer.zPosition = -1
    button.setTitle("Issue", for: .normal)
    button.backgroundColor = .orange
    button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
    button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
    button.heightAnchor.constraint(equalToConstant: 40).isActive = true
    button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
    button.isUserInteractionEnabled = true
    part.bringSubviewToFront(button)
    partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    part.layoutIfNeeded()

    let copiedView = self.part.copyView()
    stackMainView.addArrangedSubview(part)
    stackMainView.addArrangedSubview(copiedView)
    containerScrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
    containerScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
    containerScrollView.trailingAnchor.constraint(equalTo:self.view.trailingAnchor, constant: 0).isActive = true
    containerScrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
    contentView.topAnchor.constraint(equalTo: self.containerScrollView.topAnchor, constant: 0).isActive = true
    contentView.leadingAnchor.constraint(equalTo: self.containerScrollView.leadingAnchor, constant: 0).isActive = true
    contentView.trailingAnchor.constraint(equalTo:self.containerScrollView.trailingAnchor, constant: 0).isActive = true
    contentView.bottomAnchor.constraint(equalTo: self.containerScrollView.bottomAnchor, constant: 0).isActive = true
    contentView.widthAnchor.constraint(equalTo:self.view.widthAnchor, constant: 0).isActive = true

    stackMainView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 50).isActive = true
    stackMainView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8).isActive = true
    stackMainView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -8).isActive = true
    stackMainView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -30).isActive = true

}



@objc func buttonAction(sender: UIButton!) {
    print("Button tapped")
}

var containerScrollView: UIScrollView = {
    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints  = false
    scrollView.backgroundColor = .white
    scrollView.isScrollEnabled = true
    return scrollView
}()

var contentView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints  = false
    view.backgroundColor = UIColor.white
    return view
}()

let stackMainView: UIStackView = {
    let stackView = UIStackView()
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.backgroundColor = .random()
    return stackView

}()



let part: UIView  = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints  = false
    view.layer.cornerRadius = 4
    view.layer.masksToBounds = true
    view.backgroundColor = .random()

    return view
}()

Extension to copy the view.

extension UIView {
    func copyView<T: UIView>() -> T {
        return NSKeyedUnarchiver.unarchiveObject(with: 
               NSKeyedArchiver.archivedData(withRootObject: self)) as! T
    }
}

I expect the output to print in the console "Button Tapped" This happens only when I press the button on the non copied view.

回答1:

Since this is all done in viewDidLoad, I assume that the view you want to copy is the same every time.

Your code does not work likely because NSKeyedArchiver does not archive the button's target and selector pairs.

You can create a method that gives a new UIView instead:

func createPart() -> UIView {
    let part = UIView()
    part.translatesAutoresizingMaskIntoConstraints  = false
    part.layer.cornerRadius = 4
    part.layer.masksToBounds = true
    part.backgroundColor = .random()

    // The part below is copied from your viewDidLoad method
    // Include only those lines that create the part view.
    // I might have put more than you need. Check twice
    let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
    let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
    let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
    partLabel1.text = "This should sit within part use :)"
    partLabel1.textColor = .white
    partLabel2.text = "This should also sit within part use :)"
    partLabel2.textColor = .white
    part.addSubview(button)
    part.addSubview(partLabel1)
    part.addSubview(partLabel2)
    part.bringSubviewToFront(button)
    part.bringSubviewToFront(partUse3Label1)
    part.layer.zPosition = -1
    button.setTitle("Issue", for: .normal)
    button.backgroundColor = .orange
    button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
    button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
    button.heightAnchor.constraint(equalToConstant: 40).isActive = true
    button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
    button.isUserInteractionEnabled = true
    part.bringSubviewToFront(button)
    partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
    partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
    part.layoutIfNeeded()

    return part
}

And then in viewDidLoad, you should remove the lines of code that help create the part view, leaving only the code that creates the stack view and main content view. You should then call createPart twice, and there you have 2 copies!

let part = createPart()
let copyOfPart = createPart()