我正在处理裁剪视图...我有一个问题..
我向您展示的前三张照片代表了
cropView
的初始状态(所以不像这样使用偏移来移动照片)
如您所见,如果我们将女孩的鞋子作为参考点,我们会注意到它始终位于圆形蒙版区域之外,即使我对图像的 2/3、1/2、1/3 使用分割视图完美契合
但是,如果我们查看我使用拖动手势的其他 3 张照片,始终采用参考点,即女孩的鞋子,我们会注意到,每次更改分割视图时,它并不总是保持在相同的位置。这肯定会因图像测量而发生变化,但我只是找不到一种方法来理解为什么会发生这种情况。
如果我不使用拖动手势,则前三张照片中的蒙版和照片都是完美的,但是一旦偏移值随着拖动手势发生变化,我就会在最后三张照片中看到这种差异
你能帮我理解吗?我希望我能够正确地解释自己
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)
}
}
示例原始图片
解释为什么当视图大小改变时图像会移动不同的量,因为拖动偏移量被存储为绝对大小(= 绝对点数)。该大小与执行拖动时图像的大小有关。当图像显示在较小的屏幕上时,相同的偏移量将导致图像发生较大的移动,反之亦然。
要修复此问题,需要对拖动偏移进行“归一化”。然后,当将偏移量应用于图像时,需要根据当前图像尺寸对归一化偏移量进行缩放。 可以对
CropView
进行以下更改以使其以这种方式工作:
添加标准化参考尺寸let refSizeForOffset: CGFloat = 1000
.onChanged
.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
}
private var scaledOffset: CGSize {
CGSize(
width: offset.width * photoSize.width / refSizeForOffset,
height: offset.height * photoSize.height / refSizeForOffset
)
}
Image(uiImage: selectedPhoto)
// ... other modifiers as before
.offset(scaledOffset) // 👈 scaledOffset replaces offset from before
.mask {
// ...
}