I am engaged on an iOS app utilizing SwiftUI the place I’ve a picture that may be zoomed out and in. I am making an attempt so as to add a signature overlay to this picture that ought to scale and place accurately because the picture is zoomed. Nevertheless, I am encountering two points:
- The signature is all the time centered on the picture, and I need it to be
positioned on the location I specify. - The scale of the signature stays the identical whatever the zoom
degree of the picture. I need it to scale together with the picture like right here. - After every snapshot my picture on the background is shrinking bit by
bit.
Picture for Drawback 1 and three offered right here.
I’ve offered the related code beneath. Can somebody please assist me perceive what I am doing incorrect and information me on the way to repair these points?
// MARK: - MY SNAPSHOT LOGIC
extension View {
func snapshot(withSize targetSize: CGSize) -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
view?.bounds = CGRect(origin: .zero, dimension: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(dimension: targetSize)
return renderer.picture { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
// STARTING VIEW
struct AddingSignatureView3: View {
@State var choosenSignature: UIImage = UIImage()
@Binding var scannedImage: UIImage
@State personal var signatureOffset: CGSize = .zero
var physique: some View {
VStack {
ZStack {
ScannedImageView(scannedImage: $scannedImage, signatureOffset: $signatureOffset)
.onTapGesture {
// That is to have the ability to see the quick results of snapshot
scannedImage = ScannedImageView(scannedImage: $scannedImage, signatureOffset: $signatureOffset).snapshot(withSize: scannedImage.dimension)
}
}
}
}
}
// IMAGE WITH SIGNATURE IN ZSTACK
struct ScannedImageView: View {
@Binding var scannedImage: UIImage
@Binding var signatureOffset: CGSize
@State personal var scale: CGFloat = 1
@State personal var scaleAnchor: UnitPoint = .heart
@State personal var lastScale: CGFloat = 1
@State personal var offset: CGSize = .zero
@State personal var lastOffset: CGSize = .zero
@State personal var debug = ""
var physique: some View {
GeometryReader { geometry in
let magnificationGesture = MagnificationGesture()
.onChanged{ gesture in
scaleAnchor = .heart
scale = lastScale * gesture
}
.onEnded { _ in
fixOffsetAndScale(geometry: geometry)
}
let dragGesture = DragGesture()
.onChanged { gesture in
var newOffset = lastOffset
newOffset.width += gesture.translation.width
newOffset.top += gesture.translation.top
offset = newOffset
}
.onEnded { _ in
fixOffsetAndScale(geometry: geometry)
}
ZStack {
Picture(uiImage: scannedImage)
.resizable()
.scaledToFit()
.place(x: geometry.dimension.width / 2,
y: geometry.dimension.top / 2)
.scaleEffect(scale, anchor: scaleAnchor)
.offset(offset)
.gesture(dragGesture)
.gesture(magnificationGesture)
// .body(width: geometry.dimension.width, top: geometry.dimension.top)
DraggableSignatureView(signatureOffset: $signatureOffset)
}
}
}
personal func fixOffsetAndScale(geometry: GeometryProxy) {
let newScale: CGFloat = .minimal(.most(scale, 1), 4)
let screenSize = geometry.dimension
let originalScale = scannedImage.dimension.width / scannedImage.dimension.top >= screenSize.width / screenSize.top ?
geometry.dimension.width / scannedImage.dimension.width :
geometry.dimension.top / scannedImage.dimension.top
let imageWidth = (scannedImage.dimension.width * originalScale) * newScale
var width: CGFloat = .zero
if imageWidth > screenSize.width {
let widthLimit: CGFloat = imageWidth > screenSize.width ?
(imageWidth - screenSize.width) / 2
: 0
width = offset.width > 0 ?
.minimal(widthLimit, offset.width) :
.most(-widthLimit, offset.width)
}
let imageHeight = (scannedImage.dimension.top * originalScale) * newScale
var top: CGFloat = .zero
if imageHeight > screenSize.top {
let heightLimit: CGFloat = imageHeight > screenSize.top ?
(imageHeight - screenSize.top) / 2
: 0
top = offset.top > 0 ?
.minimal(heightLimit, offset.top) :
.most(-heightLimit, offset.top)
}
let newOffset = CGSize(width: width, top: top)
lastScale = newScale
lastOffset = newOffset
withAnimation() {
offset = newOffset
scale = newScale
}
}
}
// MARK: - SIGNATURE VIEW
struct DraggableSignatureView: View {
@Binding var signatureOffset: CGSize
var physique: some View {
Picture(systemName: "pencil.circle.fill") // You possibly can substitute this along with your signature view
.font(.system(dimension: 48))
.foregroundColor(.blue)
.offset(signatureOffset)
.gesture(
DragGesture()
.onChanged { gesture in
signatureOffset = CGSize(
width: gesture.translation.width,
top: gesture.translation.top
)
}
)
}
}
I used to be capable of obtain taking a proper snapshot with completely different perform, however It scaled my picture to the dimensions of the iPhone display screen and never the true dimension of the picture, so I must keep away from this outcome.
I’ve tried to manage the place and dimension of the signature utilizing the signatureOffset variable, however it’s not working as anticipated. The signature is all the time centered, and its dimension would not change with the picture zoom. How can I modify this code to attain the specified habits?
Thanks in your assist!