I want to rotate a stackedImageView (which contains image and shape) based on the DragGesture's value. The rotation should rotate with the center of the stackedImageView as anchorPoint. It seems to rotate based on the centre of the parent view.
The rotation works fine when placed in the middle of the screen (location = CGPoint(x: 0, y: 0); however, the rotation went unpredictable when placed near to the edges of the parent view (eg CGPoint(x: -80, y: -100)).
I think this is due to the anchorPoint in .rotateEffect(). How can I compute the UnitPoint to the centre of my rotating stackedImageView and use it as anchorPoint for the rotateEffect? Is there a way to compute it from the DragGesture's startLocation?
Thanks!
struct ContentView: View {
@State private var angle: CGFloat = 0
@State private var lastAngle: CGFloat = 0
@State private var length : CGFloat = 100
@State private var location: CGPoint = CGPoint(x: -80, y: -100)
let systemImage = UIImage(systemName: "camera")!
var body: some View {
var body: some View {
VStack {
Text("Rotating View Example")
.frame(height: 200)
rotationView
.frame(width: 300, height: 300)
.border(.blue, width: 2)
}
}
private var rotationView: some View {
let imageSize = newSize(100)
return ZStack {
Image(uiImage: systemImage)
.resizable()
.aspectRatio(contentMode: .fit)
.offset(x: location.x, y: location.y)
Circle()
.foregroundColor(Color.accentColor)
.frame(width: 20, height: 20)
.position(x: location.x + imageSize.width, y: location.y + imageSize.height)
}
.rotationEffect(.degrees(Double(self.angle)))
.gesture(DragGesture()
.onChanged{ value in
let centerPt = CGPoint(x: value.startLocation.x - imageSize.width / 2, y: value.startLocation.y - imageSize.height / 2)
let theta = (atan2(value.location.y - centerPt.y, value.location.x - centerPt.x) - atan2(value.startLocation.y - centerPt.y, value.startLocation.x - centerPt.x)) * 180 / .pi
angle = theta + lastAngle
print("angle: \(angle), centerPt: (\(centerPt.x), \(centerPt.y)), start: (\(value.startLocation.x), \(value.startLocation.y), end: (\(value.location.x), \(value.location.y)")
}
.onEnded { v in
self.lastAngle = self.angle
}
)
.animation(.none)
.frame(width: imageSize.width, height: imageSize.height)
}
func newSize(_ maxDimension: CGFloat) -> CGSize {
let imageSize = systemImage.size
if imageSize.width > imageSize.height {
return recalculateSize(imageSize: systemImage.size, width: maxDimension)
} else {
return recalculateSize(imageSize: systemImage.size, height: maxDimension)
}
}
func recalculateSize(imageSize: CGSize, width: CGFloat? = nil, height: CGFloat? = nil) -> CGSize {
if let width = width, height == nil {
let newHeight = imageSize.height / (imageSize.width / width)
return CGSize(width: width, height: newHeight)
} else if let height = height, width == nil {
let newWidth = imageSize.width / (imageSize.height / height)
return CGSize(width: newWidth, height: height)
}
return imageSize
}
}