iOS UIkit ViewDidLayout 调用在处理拖动时

huangapple go评论113阅读模式
英文:

iOS UIkit ViewDidLayout Calls On Handle Drag

问题

应用程序有两个不同的部分。一个是矩形视图(Rectangle view),另一个是按钮(Button)。矩形视图可以通过拖动手势(pan touch gesture)进行拖动。按钮可以通过轻击手势(tap gestures)更改自己的图像。当用户轻击按钮时,它会更改自己的图像。但是,当我轻击按钮时,矩形视图会移动到初始位置(即视图加载时的位置)。

iOS UIkit ViewDidLayout 调用在处理拖动时

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方法。如何处理此问题?有人可以帮助我吗?非常感谢。

英文:

App has 2 different parts. Rectangle view and button. Rectangle view can drag with pan touch gesture. Button can change own image with tap gestures. When user taps the button, it changes own image. But when I tap the button, rectangle view moves to the at the beginning position.(view did load position).

iOS UIkit ViewDidLayout 调用在处理拖动时

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)
            
        }
    }
}

I checked logs and I see viewDidLayout methods calling when I tap the button. How can I handle this problem, is there anyone help me ? Thanks a lot.

答案1

得分: 0

你不能在明确的框架设置中“混合匹配”约束条件。

两个选项...

1 - 在 littleView不使用任何约束条件:

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

2 - 使用约束条件,但跟踪顶部和前导约束条件,并在拖动时更新它们的常量值:

// 将这些添加为类属性
var lvTop: NSLayoutConstraint!
var lvLeading: NSLayoutConstraint!

// 在 `viewDidLoad()` 中设置 widthAnchor 和 heightAnchor 后,添加以下代码:

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)

    }
}
英文:

You cannot "mix-and-match" constraints with explicit frame settings.

Two options...

1 - don't use any constraints on littleView:

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

2 - use constraints, but track the top and leading constraints and update their constants when dragging:

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

after setting widthAnchor and heightAnchor in viewDidLoad(), add these lines:

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

change your handlePan to this:

@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)
}
}

huangapple
  • 本文由 发表于 2023年8月10日 22:47:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76876855.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定