CGAffineTransform: одновременное применение перевода и масштабирования

У меня есть два UILabels. Больше в левой части экрана и меньше в правой части.

Я пытаюсь использовать CGAffineTransform для анимации перемещения меньшей метки на место большей и масштабирования ее до того же размера, а также перемещения большей метки за пределы экрана.

На самом деле я не перемещаю метки, после завершения анимации я изменяю текстовое свойство меток и устанавливаю их преобразования на identity.

Моя проблема в том, что я не знаю, как вычислить точные значения x и y, которые мне нужно перевести на мою меньшую метку. Я думаю, что имеющиеся у меня значения неточны, потому что я масштабирую метку одновременно с переводом, а значения tx и ty вычисляются с немасштабируемым размером меньшей метки.

Чем я занимаюсь сейчас: tx: ширина большей метки + расстояние между ней и меньшей меткой, ty: расстояние между центрами двух меток по оси y


person chnski    schedule 28.02.2021    source источник


Ответы (1)


Есть разные способы сделать это - вот один ...

Начните с вычисления значений перевода и значений шкалы, а затем объедините их:

    let translation = CGAffineTransform(translationX: xMove, y: yMove)
    let scaling = CGAffineTransform(scaleX: xScale, y: yScale)
    
    let fullTransform = scaling.concatenating(translation)
    

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

class ViewController: UIViewController {

    let labelA = UILabel()
    let labelB = UILabel()
    
    var aTop: NSLayoutConstraint!
    var aLeading: NSLayoutConstraint!

    var bTop: NSLayoutConstraint!
    var bLeading: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        [labelA, labelB].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        labelA.text = "Label A"
        labelB.text = "Label B"
        
        labelA.backgroundColor = .green
        labelB.backgroundColor = .cyan
        
        labelA.font = .systemFont(ofSize: 40.0)
        labelB.font = .systemFont(ofSize: 20.0)

        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        aTop = labelA.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0)
        aLeading = labelA.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0)
        
        bTop = labelB.topAnchor.constraint(equalTo: g.topAnchor, constant: 300.0)
        bLeading = labelB.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 240.0)
        
        NSLayoutConstraint.activate([
            aTop, aLeading,
            bTop, bLeading,
        ])
        
        let t = UITapGestureRecognizer(target: self, action: #selector(self.doAnim(_:)))
        view.addGestureRecognizer(t)
        
    }
    
    @objc func doAnim(_ g: UITapGestureRecognizer?) -> Void {
        
        let targetPoint = labelA.center
        let originPoint = labelB.center
        
        let xMove = targetPoint.x - originPoint.x
        let yMove = targetPoint.y - originPoint.y

        let xScale = labelA.frame.width / labelB.frame.width
        let yScale = labelA.frame.height / labelB.frame.height
        
        let translation = CGAffineTransform(translationX: xMove, y: yMove)
        let scaling = CGAffineTransform(scaleX: xScale, y: yScale)
        
        let fullTransform = scaling.concatenating(translation)
        
        UIView.animate(withDuration: 1.0, animations: {
            self.labelB.transform = fullTransform
        }) { [weak self] b in
            guard let self = self else { return }
            self.labelB.transform = .identity
            self.labelB.font = self.labelA.font
            self.bTop.constant = self.aTop.constant
            self.bLeading.constant = self.aLeading.constant
        }

    }
    
}
person DonMag    schedule 28.02.2021