gpt4 book ai didi

ios - 使用 SwiftUI 和 contentMode = .aspectFit 动态裁剪图像

转载 作者:行者123 更新时间:2023-11-29 13:52:01 28 4
gpt4 key购买 nike

我想要一个向用户显示图像的 View 。通过拖动角点,我希望他能够选择裁剪矩形。

因为输入图像的尺寸可能比屏幕大,所以我想使用纵横比适合作为图像的内容模式。

我遇到的问题是,在确定裁剪矩形相对于图像原始尺寸的尺寸时,我不知道如何考虑内容模式引起的位移。

这在视频中可能更容易解释:

enter image description here

如您所见,圆圈位置的上下文是整个 View 。我想改用调整后图像的坐标系。对于此转换,我需要外部 View 的大小与调整后的图像之间的差异。

所以问题是:如何根据调整大小的图像获得用户选择的矩形的正确测量值?

import SwiftUI

struct CropImageViewTest: View {
var currentImage: Image

@State private var currentPositionTopLeft: CGPoint = .zero
@State private var newPositionTopLeft: CGPoint = .zero

@State private var currentPositionTopRight: CGPoint = .zero
@State private var newPositionTopRight: CGPoint = .zero

@State private var currentPositionBottomLeft: CGPoint = .zero
@State private var newPositionBottomLeft: CGPoint = .zero

@State private var currentPositionBottomRight: CGPoint = .zero
@State private var newPositionBottomRight: CGPoint = .zero

var body: some View {
ZStack {
VStack {
Text("Top left: \(currentPositionTopLeft.x) | \(currentPositionTopLeft.y)")
Text("Top right: \(currentPositionTopRight.x) | \(currentPositionTopRight.y)")
Text("Bottom left: \(currentPositionBottomLeft.x) | \(currentPositionBottomLeft.y)")
Text("Bottom right: \(currentPositionBottomRight.x) | \(currentPositionBottomRight.y)")
Spacer()
currentImage
.resizable()
.aspectRatio(1 , contentMode: .fit)
.background(Color.red)
Spacer()
Group {
Button(action: {
// TODO: Crop it
}) {
Image(systemName: "checkmark").resizable().frame(width: 24, height: 24)
.padding(20)
.background(Color(Colors.getColor(Colors.colorSboBlue)))
.foregroundColor(Color.white)
}.clipShape(Circle())
.shadow(radius: 4)
}
}
getCorners()
}
}

private func getCorners() -> some View{

return
HStack {
VStack {
ZStack {
GeometryReader { geometry in
Path { path in
path.move(to: self.currentPositionTopLeft)
path.addLine(
to: .init(
x: self.currentPositionTopRight.x + geometry.size.width,
y: self.currentPositionTopRight.y
)
)
path.addLine(
to: .init(
x: self.currentPositionBottomRight.x + geometry.size.width,
y: self.currentPositionBottomRight.y + geometry.size.height
)
)
path.addLine(
to: .init(
x: self.currentPositionBottomLeft.x,
y: self.currentPositionBottomLeft.y + geometry.size.height
)
)
path.addLine(
to: .init(
x: self.currentPositionTopLeft.x,
y: self.currentPositionTopLeft.y
)
)
}
.stroke(Color.blue, lineWidth: CGFloat(1))
}

Circle().foregroundColor(Color.blue).frame(width: 24, height: 24)
.offset(x: self.currentPositionTopLeft.x, y: self.currentPositionTopLeft.y)
.gesture(DragGesture()
.onChanged { value in
self.currentPositionTopLeft = CGPoint(x: value.translation.width + self.newPositionTopLeft.x, y: value.translation.height + self.newPositionTopLeft.y)
}
.onEnded { value in
self.currentPositionTopLeft = CGPoint(x: value.translation.width + self.newPositionTopLeft.x, y: value.translation.height + self.newPositionTopLeft.y)
self.newPositionTopLeft = self.currentPositionTopLeft
print(self.currentPositionTopLeft)
print(self.newPositionTopLeft)
}
)
.opacity(0.5)
.position(CGPoint(x: 0, y: 0))

GeometryReader { geometry in
Circle().foregroundColor(Color.blue).frame(width: 24, height: 24)
.offset(x: self.currentPositionTopRight.x, y: self.currentPositionTopRight.y)
.gesture(DragGesture()
.onChanged { value in
self.currentPositionTopRight = CGPoint(x: value.translation.width + self.newPositionTopRight.x, y: value.translation.height + self.newPositionTopRight.y)
}
.onEnded { value in
self.currentPositionTopRight = CGPoint(x: value.translation.width + self.newPositionTopRight.x, y: value.translation.height + self.newPositionTopRight.y)
self.newPositionTopRight = self.currentPositionTopRight
print(self.currentPositionTopRight)
print(self.newPositionTopRight)
}
)
.opacity(0.5)
.position(CGPoint(x: geometry.size.width, y: 0))
}

GeometryReader { geometry in
Circle().foregroundColor(Color.blue).frame(width: 24, height: 24)
.offset(x: self.currentPositionBottomLeft.x, y: self.currentPositionBottomLeft.y)
.gesture(DragGesture()
.onChanged { value in
self.currentPositionBottomLeft = CGPoint(x: value.translation.width + self.newPositionBottomLeft.x, y: value.translation.height + self.newPositionBottomLeft.y)
}
.onEnded { value in
self.currentPositionBottomLeft = CGPoint(x: value.translation.width + self.newPositionBottomLeft.x, y: value.translation.height + self.newPositionBottomLeft.y)
self.newPositionBottomLeft = self.currentPositionBottomLeft
print(self.currentPositionBottomLeft)
print(self.newPositionBottomLeft)
}
)
.opacity(0.5)
.position(CGPoint(x: 0, y: geometry.size.height))
}

GeometryReader { geometry in
Circle().foregroundColor(Color.blue).frame(width: 24, height: 24)
.offset(x: self.currentPositionBottomRight.x, y: self.currentPositionBottomRight.y)
.gesture(DragGesture()
.onChanged { value in
self.currentPositionBottomRight = CGPoint(x: value.translation.width + self.newPositionBottomRight.x, y: value.translation.height + self.newPositionBottomRight.y)
}
.onEnded { value in
self.currentPositionBottomRight = CGPoint(x: value.translation.width + self.newPositionBottomRight.x, y: value.translation.height + self.newPositionBottomRight.y)
self.newPositionBottomRight = self.currentPositionBottomRight
print(self.currentPositionBottomRight)
print(self.newPositionBottomRight)
}
)
.opacity(0.5)
.position(CGPoint(x: geometry.size.width, y: geometry.size.height))
}
}

Spacer()
}
Spacer()
}
}
}

如果您没有示例图片,您可以这样调用 View :

CropImageViewTest(currentImage: Image(systemName: "camera.fill"))

我添加了红色背景,以便您可以看到图像的限制。

如果当前方法不是“最快捷”的方法,我也愿意接受完全不同的方法。

提前致谢!

编辑:

我有可用的 UIImage(SwiftUI)图像源自。如果这有助于确定正确的测量值。

更新:

如果我像这样使用裁剪矩形作为图像的叠加层:

currentImage
.resizable()
.aspectRatio(1 , contentMode: .fit)
.overlay(getCorners())

它确实有效。尽管如此,仍然存在每个角定义其起始位置为 (0|0) 的问题。我想相对于图像的左上角定义位置。

最佳答案

好的,终于解决了。

1.) 我使用带有矩形和可拖动角的 View 作为图像的覆盖。这样,矩形和角的原点就是图像,而不是周围的 View 。从这里得到灵感:https://swiftui-lab.com/geometryreader-to-the-rescue/

2.) 仍然存在每个角都将其原点 (0|0) 定义为其初始位置的问题。我通过使用

解决了这个问题
.position(CGPoint(x: 0, y: 0))

并使用onAppear放置坐标。

这会导致应用程序正确计算相对于调整大小的图像的坐标:

enter image description here

我还在自定义 View 中封装了矩形和角,从而产生了这段代码:

Root View :

import SwiftUI

struct CropImageViewTest: View {
var currentImage: Image

@State private var currentPositionTopLeft: CGPoint = .zero
@State private var newPositionTopLeft: CGPoint = .zero

@State private var currentPositionTopRight: CGPoint = .zero
@State private var newPositionTopRight: CGPoint = .zero

@State private var currentPositionBottomLeft: CGPoint = .zero
@State private var newPositionBottomLeft: CGPoint = .zero

@State private var currentPositionBottomRight: CGPoint = .zero
@State private var newPositionBottomRight: CGPoint = .zero

var body: some View {
ZStack {
VStack {
Text("Top left: \(currentPositionTopLeft.x) | \(currentPositionTopLeft.y)")
Text("Top right: \(currentPositionTopRight.x) | \(currentPositionTopRight.y)")
Text("Bottom left: \(currentPositionBottomLeft.x) | \(currentPositionBottomLeft.y)")
Text("Bottom right: \(currentPositionBottomRight.x) | \(currentPositionBottomRight.y)")
Spacer()
currentImage
.resizable()
.aspectRatio(1 , contentMode: .fit)
.overlay(getCorners())
Spacer()
Group {
Button(action: {
// TODO: Crop it
}) {
Image(systemName: "checkmark").resizable().frame(width: 24, height: 24)
.padding(20)
.background(Color(Colors.getColor(Colors.colorSboBlue)))
.foregroundColor(Color.white)
}.clipShape(Circle())
.shadow(radius: 4)
}
}
}
}

private func getCorners() -> some View{

return
HStack {
VStack {
ZStack {
CropImageViewRectangle(
currentPositionTopLeft: self.$currentPositionTopLeft,
currentPositionTopRight: self.$currentPositionTopRight,
currentPositionBottomLeft: self.$currentPositionBottomLeft,
currentPositionBottomRight: self.$currentPositionBottomRight
)

GeometryReader { geometry in
CropImageViewRectangleCorner(
currentPosition: self.$currentPositionTopLeft,
newPosition: self.$newPositionTopLeft,
displacementX: 0,
displacementY: 0
)

CropImageViewRectangleCorner(
currentPosition: self.$currentPositionTopRight,
newPosition: self.$newPositionTopRight,
displacementX: geometry.size.width,
displacementY: 0
)

CropImageViewRectangleCorner(
currentPosition: self.$currentPositionBottomLeft,
newPosition: self.$newPositionBottomLeft,
displacementX: 0,
displacementY: geometry.size.height
)

CropImageViewRectangleCorner(
currentPosition: self.$currentPositionBottomRight,
newPosition: self.$newPositionBottomRight,
displacementX: geometry.size.width,
displacementY: geometry.size.height
)
}
}

Spacer()
}
Spacer()
}
}
}

矩形:

import SwiftUI

struct CropImageViewRectangle: View {
@Binding var currentPositionTopLeft: CGPoint
@Binding var currentPositionTopRight: CGPoint
@Binding var currentPositionBottomLeft: CGPoint
@Binding var currentPositionBottomRight: CGPoint

var body: some View {
GeometryReader { geometry in
Path { path in
path.move(to: self.currentPositionTopLeft)
path.addLine(
to: .init(
x: self.currentPositionTopRight.x,
y: self.currentPositionTopRight.y
)
)
path.addLine(
to: .init(
x: self.currentPositionBottomRight.x,
y: self.currentPositionBottomRight.y
)
)
path.addLine(
to: .init(
x: self.currentPositionBottomLeft.x,
y: self.currentPositionBottomLeft.y
)
)
path.addLine(
to: .init(
x: self.currentPositionTopLeft.x,
y: self.currentPositionTopLeft.y
)
)
}
.stroke(Color.blue, lineWidth: CGFloat(1))
}
}
}

角落:

import SwiftUI

struct CropImageViewRectangleCorner: View {
@Binding var currentPosition: CGPoint
@Binding var newPosition: CGPoint

var displacementX: CGFloat
var displacementY: CGFloat

var body: some View {
Circle().foregroundColor(Color.blue).frame(width: 24, height: 24)
.offset(x: self.currentPosition.x, y: self.currentPosition.y)
.gesture(DragGesture()
.onChanged { value in
self.currentPosition = CGPoint(x: value.translation.width + self.newPosition.x, y: value.translation.height + self.newPosition.y)
}
.onEnded { value in
self.currentPosition = CGPoint(x: value.translation.width + self.newPosition.x, y: value.translation.height + self.newPosition.y)
self.newPosition = self.currentPosition
}
)
.opacity(0.5)
.position(CGPoint(x: 0, y: 0))
.onAppear() {
if self.displacementX > 0 || self.displacementY > 0 {
self.currentPosition = CGPoint(x: self.displacementX, y: self.displacementY)
self.newPosition = self.currentPosition
}
}
}
}

关于ios - 使用 SwiftUI 和 contentMode = .aspectFit 动态裁剪图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59211075/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com