I have three (3) SF Symbols
grouped and arranged in a specific relationship to each other. Now I want the entire group to rotate, as one item, around a specific point.
I have spent hours with trial & error (mostly error) trying to adjust combinations of padding
and offsets
and anchor points
.
This rotatable view will be used as part of an iOS 17 Annotation
using MapKit
to mark a specific GPS location indicating the direction of travel (hence the rotating bit and the desired anchor point).
IF there is a better way to accomplish my ultimate goal … I'm open to all ideas. :-)
import SwiftUI
struct StarArrowView: View {
var bumpColor: Color
@State var rotaAtAnchor = false
var body: some View {
ZStack {
Divider()
Divider().offset(y: 25)
Divider().offset(y: -25)
Divider().rotationEffect(.degrees(90))
Divider().rotationEffect(.degrees(90)).offset(x: 25)
Divider().rotationEffect(.degrees(90)).offset(x: -25)
Group {
HStack(spacing: 0) {
Image(systemName: "star.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(bumpColor)
.font(.system(size: 30, weight: .light))
.padding(0)
.offset(x: 8, y: 6)
Image(systemName: "line.diagonal.arrow")
.foregroundStyle(bumpColor)
.font(.system(size: 40, weight: .light))
.padding(0)
.offset(x: -10, y: -15)
Text("➘")
.symbolRenderingMode(.palette)
.foregroundStyle(bumpColor, bumpColor)
.font(.system(size: 30, weight: .ultraLight))
.rotationEffect(.degrees(190), anchor: .topLeading)
.padding(0)
.offset(x: 0, y: 0)
}
.padding(.vertical, -20)
.padding(.horizontal, -5)
.rotationEffect(.degrees(45))
.offset(x: 15, y: 22)
.rotationEffect(.degrees(rotaAtAnchor ? 0 : 180), anchor: .trailing) //Anchor Position
.animation(Animation.spring ().repeatForever(autoreverses: true))
.onAppear() {
self.rotaAtAnchor.toggle()
}
}
}
}
}
#Preview {
StarArrowView(bumpColor: .heatmap10)
}
The tricky part about this problem is getting the anchor point right. One way would be to use a UnitPoint
with fractional position. Alternatively, in order to use one of the edges of the group as the anchor point, the arrow pointing up needs to be moved out of the bounds of the group. This can be done using an overlay.
Here is an attempt to get it working. Some notes:
ZStack
is used to combine the symbols. I would suggest, this works better than an HStack
because there is some overlap involved..topTrailing
alignment. It is then moved over the edge of the group with an x-offset.ZStack {
Divider()
Divider().offset(y: 25)
Divider().offset(y: -25)
Divider().rotationEffect(.degrees(90))
Divider().rotationEffect(.degrees(90)).offset(x: 25)
Divider().rotationEffect(.degrees(90)).offset(x: -25)
ZStack(alignment: .leading) {
Image(systemName: "star.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(bumpColor)
.font(.system(size: 30, weight: .light))
.rotationEffect(.degrees(45))
Image(systemName: "line.diagonal.arrow")
.foregroundStyle(bumpColor)
.font(.system(size: 40, weight: .light))
.rotationEffect(.degrees(45))
.padding(.leading, 30)
}
.padding(.trailing, 10)
.overlay(alignment: .topTrailing) {
Text("➘")
.symbolRenderingMode(.palette)
.foregroundStyle(bumpColor, bumpColor)
.font(.system(size: 30, weight: .ultraLight))
.rotationEffect(.degrees(235))
.frame(width: 30)
.offset(x: 15)
}
.rotationEffect(.degrees(rotaAtAnchor ? 180 : 0), anchor: .trailing)
// Fine adjustment of the position of the entire group
.padding(.leading, 30)
.padding(.top, 20)
.animation(Animation.spring ().repeatForever(autoreverses: true), value: rotaAtAnchor)
.onAppear() {
self.rotaAtAnchor.toggle()
}
}