代码之家  ›  专栏  ›  技术社区  ›  Perry Wang

UIImageView收缩手势在SwiftUI中包装时不起作用

  •  1
  • Perry Wang  · 技术社区  · 1 年前

    我正在尝试使用 UIImageView 在我的SwiftUI项目中。但捏手势不起作用。我尝试了几种解决方案,这些解决方案应该在UIKit中运行良好。

    struct UIImageViewRepresentable: UIViewRepresentable {
        let image: UIImage?
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
    
        class Coordinator: NSObject {
            var parent: UIImageViewRepresentable
    
            init(_ parent: UIImageViewRepresentable) {
                self.parent = parent
            }
    
            @objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
                guard let view = sender.view else { return }
    
                    UIView.animate(withDuration: 0.3) {
                        let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
                        guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
                        sender.view?.transform = scale
                        sender.scale = 1
                    }
            }
        }
    
        func makeUIView(context: Context) -> UIImageView {
            let imageView = UIImageView()
            imageView.image = image
            imageView.isUserInteractionEnabled = true
            imageView.addGestureRecognizer(UIPinchGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handlePinchGesture(_:))))
            return imageView
        }
    
        func updateUIView(_ uiView: UIImageView, context: Context) {
            uiView.image = image
        }
    }
    struct ContentView: View {
        var body: some View {
            UIImageViewRepresentable(image: UIImage(named: "default"))
                .frame(width: 300, height: 300)
                .background(Color.gray)
                .padding()
        }
    }
    

    如果没有动画,图像根本无法缩放。添加动画后,图像会缩放,但会立即返回到初始大小。为什么会发生这种情况?

    1 回复  |  直到 1 年前
        1
  •  1
  •   Sweeper    1 年前

    看起来SwiftUI正在获取/设置 frame 在图像视图的 transform 不是身份转换。从…起 frame ,

    如果 使改变 属性不是身份转换,此属性的值未定义,因此应忽略。

    对此属性的更改可以设置动画。但是,如果 使改变 属性包含非标识转换,frame属性的值未定义,不应修改。

    当然,SwiftUI并不关心视图是否已转换,而是设置 框架 不管怎样,可能是为了根据SwiftUI自己的布局规则更新视图的框架。

    包装 UIImageView 内部的另一个视图解决了这个问题。

    class Wrapper: UIView {
        let imageView: UIImageView
        
        var image: UIImage? {
            get { imageView.image }
            set { imageView.image = newValue }
        }
        
        override init(frame: CGRect) {
            imageView = UIImageView()
            super.init(frame: frame)
            addSubview(imageView)
            imageView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                imageView.topAnchor.constraint(equalTo: topAnchor),
                imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
                imageView.leftAnchor.constraint(equalTo: leftAnchor),
                imageView.rightAnchor.constraint(equalTo: rightAnchor),
            ])
        }
        
        required init?(coder: NSCoder) {
            fatalError()
        }
    }
    
    struct UIImageViewRepresentable: UIViewRepresentable {
        let image: UIImage?
    
        func makeCoordinator() -> Coordinator {
            Coordinator()
        }
    
        @MainActor
        class Coordinator: NSObject {
    
            @objc func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
                guard let view = sender.view else { return }
                let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
                guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
                sender.view?.transform = scale
                sender.scale = 1
            }
        }
    
        func makeUIView(context: Context) -> Wrapper {
            let wrapper = Wrapper()
            wrapper.image = image
            wrapper.imageView.isUserInteractionEnabled = true
            wrapper.imageView.addGestureRecognizer(UIPinchGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handlePinchGesture(_:))))
            return wrapper
        }
    
        func updateUIView(_ uiView: Wrapper, context: Context) {
            uiView.image = image
        }
    }