iOS UIkit ViewDidLayout 调用句柄拖动

iOS UIkit ViewDidLayout Calls On Handle Drag

提问人:mkemalgokce 提问时间:8/10/2023 最后编辑:pkambmkemalgokce 更新时间:8/11/2023 访问量:38

问:

应用程序有 2 个不同的部分。矩形视图和按钮。矩形视图可以使用平移触摸手势进行拖动。按钮可以通过点击手势更改自己的图像。当用户点击该按钮时,它会更改自己的图像。但是当我点击按钮时,矩形视图移动到起始位置。(查看负载位置)。

App recording

import UIKit

final class SampleViewController:    UIViewController {
    private var isTapped: Bool = false
    
    let button: UIButton = {
        let button = UIButton()
        let image = UIImageView(image: UIImage(systemName: "play"))
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = .yellow
        return button
    }()
    
    let littleView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.layer.borderColor = UIColor.red.cgColor
        view.layer.borderWidth = 4
        view.isUserInteractionEnabled = true
        view.backgroundColor = .red
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(button)
        self.view.addSubview(littleView)
        
        littleView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))))
        
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        
        NSLayoutConstraint.activate([
            button.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
            button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            littleView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1/5),
            littleView.widthAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1/3),
        
        ])
    }

    override func viewDidLayoutSubviews() {
        //littleView.frame = CGRect(x: 0, y: 0, width: littleView.frame.width, height: littleView.frame.height)
    }
    
    override func viewWillLayoutSubviews() {
        //littleView.frame = CGRect(x: 0, y: 0, width: littleView.frame.width, height: littleView.frame.height)
    }

    @objc private func buttonTapped() {
        button.setImage(UIImage(systemName: isTapped ? "play.slash" : "play"), for: .normal)
        self.view.layoutIfNeeded()
        isTapped.toggle()
        print("Button tapped")
    }
    
    @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
        guard let viewToMove = gesture.view else { return }
        if gesture.state == .began || gesture.state == .changed {
            let translation = gesture.translation(in: view)
            
            let newX = max(0, min(view.frame.width - viewToMove.frame.width, viewToMove.frame.origin.x + translation.x))
            let newY = max(0, min((view.frame.height - viewToMove.frame.height), viewToMove.frame.origin.y + translation.y))
            
            viewToMove.frame.origin = CGPoint(x: newX, y: newY)
            gesture.setTranslation(CGPoint.zero, in: view)
            
        }
    }
}

我检查了日志,当我点击按钮时,我看到 viewDidLayout 方法在调用。我该如何处理这个问题,有人帮助我吗?多谢。

iOS Swift UIKIT 自动布局

评论

0赞 HangarRash 8/11/2023
viewDidLayoutSubviews当您点击按钮时被调用,因为您正在调用您的方法。self.view.layoutIfNeeded()buttonTapped
0赞 mkemalgokce 8/11/2023
感谢您的回答,但我删除了这一行,没有任何变化。

答:

0赞 DonMag 8/11/2023 #1

不能将约束与显式框架设置“混合搭配”。

两个选项...

1 - 不要对以下方面使用任何约束:littleView

littleView.translatesAutoresizingMaskIntoConstraints = true
littleView.frame = .init(x: 0.0, y: 0.0, width: 160.0, height: 120.0)

2 - 使用约束,但在拖动时跟踪顶部和前导约束并更新其常量:

// add these as class properties
var lvTop: NSLayoutConstraint!
var lvLeading: NSLayoutConstraint!

在 中设置 widthAnchor 和 heightAnchor 后,添加以下行:viewDidLoad()

lvTop = littleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0)
lvLeading = littleView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0)
lvTop.isActive = true
lvLeading.isActive = true

将您的更改为:handlePan

@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
    guard let viewToMove = gesture.view else { return }
    if gesture.state == .began || gesture.state == .changed {
        let translation = gesture.translation(in: view)

        lvTop.constant += translation.y
        lvLeading.constant += translation.x
        
        gesture.setTranslation(CGPoint.zero, in: view)
        
    }
}