Как растянуть изображение в соответствии с изменением четырехстороннего пути в Swiftui?IOS

Программируем под IOS
Ответить
Anonymous
 Как растянуть изображение в соответствии с изменением четырехстороннего пути в Swiftui?

Сообщение Anonymous »

Я пытаюсь реализовать интерактивный эффект растяжения изображений в Swiftui, где пользователи могут перетаскивать четыре угловые точки, а изображение внутри должно растянуть соответствующим образом, следуя пути, созданного этими точками. < /p>
В настоящее время я применяю CiperSpectiveTransform, чтобы деформировать изображение на основе обновленных точек. Тем не менее, проблема в том, что изображение не растягивается точно так же, как и ожидалось - оно либо обрезается, либо не соответствует полностью новой четырехсторонней форме. /strong> < /p>
  • Затягиваемая четырехугольная форма, где пользователи могут перемещать углы. на обновленных точках. < /li>
    Изображение должно динамически растягиваться, когда пользователь перемещает точки. < /li>
    < /ul>
    < Strong> проблема < /strong> < /p>

    Изображение не всегда подходит внутри четырехстороннего; Иногда детали выходят за рамки или неверно искажаются. />
текущий код
import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins

struct AdjustableImage: View {
let uiImage: UIImage
@State private var topLeading: CGPoint = .zero
@State private var topTrailing: CGPoint = .zero
@State private var bottomLeading: CGPoint = .zero
@State private var bottomTrailing: CGPoint = .zero
@State private var processedImage: UIImage?
@State private var lastSize: CGSize = .zero

var body: some View {
GeometryReader { geometry in
ZStack {
if let processedImage = processedImage {
Image(uiImage: processedImage)
.resizable()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
} else {
Color.clear
}

QuadrilateralShape(
topLeading: topLeading,
topTrailing: topTrailing,
bottomLeading: bottomLeading,
bottomTrailing: bottomTrailing
)
.stroke(Color.red, lineWidth: 2)

DraggablePoint(position: $topLeading, geometry: geometry)
DraggablePoint(position: $topTrailing, geometry: geometry)
DraggablePoint(position: $bottomLeading, geometry: geometry)
DraggablePoint(position: $bottomTrailing, geometry: geometry)
}
.onAppear {
updatePoints(for: geometry.size)
processImage(size: geometry.size)
}
.onChange(of: geometry.size) { newSize in
updatePoints(for: newSize)
processImage(size: newSize)
}
.onChange(of: topLeading) { _ in processImage(size: geometry.size) }
.onChange(of: topTrailing) { _ in processImage(size: geometry.size) }
.onChange(of: bottomLeading) { _ in processImage(size: geometry.size) }
.onChange(of: bottomTrailing) { _ in processImage(size: geometry.size) }
}
}

private func updatePoints(for size: CGSize) {
guard size != lastSize else { return }
lastSize = size

topLeading = CGPoint(x: 0, y: 0)
topTrailing = CGPoint(x: size.width, y: 0)
bottomLeading = CGPoint(x: 0, y: size.height)
bottomTrailing = CGPoint(x: size.width, y: size.height)
}

private func processImage(size: CGSize) {
guard size != .zero else { return }

let inputImage = CIImage(image: uiImage)
guard let inputImage = inputImage else { return }

let imageSize = uiImage.size

let scaleX = imageSize.width / size.width
let scaleY = imageSize.height / size.height

let transformedPoints = [
convertPoint(topLeading, scaleX: scaleX, scaleY: scaleY, viewHeight: size.height),
convertPoint(topTrailing, scaleX: scaleX, scaleY: scaleY, viewHeight: size.height),
convertPoint(bottomLeading, scaleX: scaleX, scaleY: scaleY, viewHeight: size.height),
convertPoint(bottomTrailing, scaleX: scaleX, scaleY: scaleY, viewHeight: size.height)
]

let filter = CIFilter.perspectiveTransform()
filter.inputImage = inputImage
filter.setValue(transformedPoints[0], forKey: "inputTopLeft")
filter.setValue(transformedPoints[1], forKey: "inputTopRight")
filter.setValue(transformedPoints[2], forKey: "inputBottomLeft")
filter.setValue(transformedPoints[3], forKey: "inputBottomRight")

guard let outputImage = filter.outputImage else { return }
let context = CIContext()
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }

processedImage = UIImage(cgImage: cgImage)
}

private func convertPoint(_ point: CGPoint, scaleX: CGFloat, scaleY: CGFloat, viewHeight: CGFloat) -> CIVector {
let x = point.x * scaleX
let y = (viewHeight - point.y) * scaleY
return CIVector(x: x, y: y)
}
}

struct DraggablePoint: View {
@Binding var position: CGPoint
var geometry: GeometryProxy

var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 40, height: 40)
.contentShape(Circle())
.position(position)
.gesture(
DragGesture()
.onChanged { value in
var newLocation = value.location
newLocation.x = min(newLocation.x, geometry.size.width)
newLocation.y = min(newLocation.y, geometry.size.height)
position = newLocation
}
)
}
}

struct QuadrilateralShape: Shape {
var topLeading: CGPoint
var topTrailing: CGPoint
var bottomLeading: CGPoint
var bottomTrailing: CGPoint

func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: topLeading)
path.addLine(to: topTrailing)
path.addLine(to: bottomTrailing)
path.addLine(to: bottomLeading)
path.closeSubpath()
return path
}
}
< /code>
Что я хочу < /strong> < /p>
  • Когда пользователь перетаскивает точки, изображение должно растянуть точно внутри нового четырехстороннего. Вдали изображение должно масштабироваться/растягиваться динамически, а не просто перемещение перспективы.

Я рассмотрел:


Использование металла для пользовательского вершинного шейдера для лучшего обработки изображения. Линейные искажения вместо просто перспективного преобразования. < /li>
Вручную картирование координат текстуры в пользовательском холсте Swiftui, чтобы динамически перерисовать изображение. < /li>
< /ul>
image
Введите описание изображения здесь

Подробнее здесь: https://stackoverflow.com/questions/794 ... in-swiftui
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»