处理蒙版和图像的最佳方式

问题描述 投票:0回答:1

我正在处理裁剪视图...我有一个问题..

我向您展示的前三张照片代表了

cropView
初始状态(所以不像这样使用偏移来移动照片)

enter image description hereenter image description hereenter image description here

如您所见,如果我们将女孩的鞋子作为参考点,我们会注意到它始终位于圆形蒙版区域之外,即使我对图像的 2/3、1/2、1/3 使用分割视图完美契合

但是,如果我们查看我使用拖动手势的其他 3 张照片,始终采用参考点,即女孩的鞋子,我们会注意到,每次更改分割视图时,它并不总是保持在相同的位置。这肯定会因图像测量而发生变化,但我只是找不到一种方法来理解为什么会发生这种情况。

enter image description hereenter image description hereenter image description here

如果我不使用拖动手势,则前三张照片中的蒙版和照片都是完美的,但是一旦偏移值随着拖动手势发生变化,我就会在最后三张照片中看到这种差异

你能帮我理解吗?我希望我能够正确地解释自己

public struct CropView: PlatformView {
   
    var selectedPhoto: UIImage
    let prefersShape: Mask
    
    public init(selectedPhoto: UIImage, prefersShape: Mask = .circle) {
        self.selectedPhoto = selectedPhoto
        self.prefersShape = prefersShape
    }
    
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @Environment(\.verticalSizeClass) var verticalSizeClass
    
    @State private var viewSize: CGSize = .zero
    @State private var offset: CGSize = .zero
    @State private var lastOffset: CGSize = .zero
    
    private var maskScale: CGFloat {
        isCompact ? 0.6 : 0.65
    }
    
    @State private var photoSize: CGSize = .zero
    
    private var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                let currentOffset = lastOffset + value.translation
                offset = currentOffset
               // updateOffsetLimits(currentOffset: currentOffset)
            }
            .onEnded { _ in lastOffset = offset }
    }
    
    public var body: some View {
                        
        ZStack {
            Image(uiImage: selectedPhoto)
                .resizable()
                .aspectRatio( contentMode: .fill)
                .frame(photoSize)
                .offset(offset)
                .mask {
                    Rectangle().opacity(0.5)
                        .overlay {
                            prefersShape.shape
                                .blendMode(.destinationOver)
                        }
                }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .gesture(dragGesture)
        .onGeometryChange(for: CGSize.self) { proxy in
            proxy.size
        } action: { newValue in
            viewSize = newValue
            
            let isLandscape = newValue.width > newValue.height
            let reference = isLandscape ? newValue.height : newValue
                .width
            photoSize = prefersShape.size(relativeTo: .init(width: reference, height: reference)).scaledBy(maskScale)

        }
    }
}


extension View {
    func frame(_ size: CGSize) -> some View {
        frame(width: size.width, height: size.height)
    }
}


@MainActor
protocol PlatformView: View {
    var horizontalSizeClass: UserInterfaceSizeClass? { get }
    var verticalSizeClass: UserInterfaceSizeClass? { get }
    var isCompact: Bool { get }
    var isRegular: Bool { get }
}

extension PlatformView {
    var isCompact: Bool { horizontalSizeClass == .compact || verticalSizeClass == .compact }
    var isRegular: Bool { horizontalSizeClass == .regular && verticalSizeClass == .regular }
}

掩码枚举和扩展的更新

public enum Mask {
    case circle, square, 
    
    var shape: AnyShape {
        switch self {
        case .circle: return AnyShape(.circle)
        case .square, .rectangle : return AnyShape(.rect)
        }
    }

  func size(relativeTo size: CGSize) -> CGSize {
    let isLandscape = size.width > size.height
    let reference = isLandscape ? size.height : size
        .width

    switch self {
    case .circle, .square :
        return .init(width: size.width, height: size.width)
    }
}

extension CGSize {
    static func + (lhs: CGSize, rhs: CGSize) -> CGSize {
        .init(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
    }
    
    func scaledBy(_ scale: CGFloat) -> CGSize {
        .init(width: self.width * scale, height: self.height * scale)
    }
}

示例原始图片

enter image description here

ios swift swiftui
1个回答
0
投票

解释为什么当视图大小改变时图像会移动不同的量,因为拖动偏移量被存储为绝对大小(= 绝对点数)。该大小与执行拖动时图像的大小有关。当图像显示在较小的屏幕上时,相同的偏移量将导致图像发生较大的移动,反之亦然。

要修复此问题,需要对拖动偏移进行“归一化”。然后,当将偏移量应用于图像时,需要根据当前图像尺寸对归一化偏移量进行缩放。 可以对

CropView

进行以下更改以使其以这种方式工作:


添加标准化参考尺寸
  1. let refSizeForOffset: CGFloat = 1000
    .onChanged
  1. 闭包中,计算并保存归一化偏移量
    
    
  2. .onChanged { value in let normalizedOffset = CGSize( width: value.translation.width * refSizeForOffset / photoSize.width, height: value.translation.height * refSizeForOffset / photoSize.height ) let currentOffset = lastOffset + normalizedOffset offset = currentOffset }
添加计算属性来计算缩放偏移量
  1. private var scaledOffset: CGSize { CGSize( width: offset.width * photoSize.width / refSizeForOffset, height: offset.height * photoSize.height / refSizeForOffset ) }
将缩放后的偏移应用于图像
  1. Image(uiImage: selectedPhoto) // ... other modifiers as before .offset(scaledOffset) // 👈 scaledOffset replaces offset from before .mask { // ... }
	
© www.soinside.com 2019 - 2024. All rights reserved.