我正在制作一个简单的指南针视图,其中仅包含视图的基本内容,我希望将其动画化。我正在使用我的物理设备(iPhone 13 PRO)的指南针。所以,一切似乎都很好 - 标题是正确的,视图正在旋转,但是......动画不起作用,实际上任何动画类型都不起作用。但是如果我用它来旋转整个 ZStack 就可以了。一旦我尝试旋转仪表标记,它就不起作用了。请看下面的代码:
//
// CompassView.swift
// ExtasyCompleteNavigation
//
// Created by Vasil Borisov on 20.08.23.
//
import SwiftUI
struct CompassView: View {
var heading: CGFloat
var body: some View {
VStack{
ZStack{
GeometryReader{ geometry in
let width = geometry.size.width
let height = geometry.size.height
let fontSize = width/13
//compass background
Circle()
//compass heading display and direction
Text("\(-heading,specifier: "%.0f")°\n\(displayDirection())")
.font(Font.custom("AppleSDGothicNeo-Bold", size: width/13))
.foregroundColor(.white)
.position(x: width/2, y:height/2)
//compass degree ring
Group{
MyShape(sections: 12, lineLengthPercentage: 0.15)
.stroke(Color.white, style: StrokeStyle(lineWidth: 5))
MyShape(sections: 360, lineLengthPercentage: 0.15)
.stroke(Color.white, style: StrokeStyle(lineWidth: 2))
//compass arrow
Text("▼")
.font(Font.custom("AppleSDGothicNeo-Bold", size: fontSize))
.position(x:width/2, y: height/200 )
.foregroundColor(.red)
PseudoBoat()
.stroke(lineWidth: 4)
.foregroundColor(.white)
.scaleEffect(x: 0.30, y: 0.55, anchor: .top)
.offset(y: geometry.size.height/5)
}
//heading values
ForEach(GaugeMarkerCompass.labelSet()) { marker in
CompassLabelView(marker: marker, geometry: geometry)
.position(CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2))
}
.rotationEffect(.init(degrees: heading))
.animation(Animation.easeInOut(duration: 3), value: heading)
}.aspectRatio(1, contentMode: .fit)
}
}
}
//function can be moved in a structure with the rest of the tools in swift file
func displayDirection() -> String {
switch heading {
case 0:
return "N"
case 90:
return "E"
case 180:
return "S"
case 270:
return "W"
case 90..<180:
return "SE"
case 180..<270:
return "SW"
case 270..<360:
return "NW"
default:
return "NE"
}
}
//to be moved to a swift file and keep it separate
public struct CompassLabelView: View {
let marker: GaugeMarker
let geometry: GeometryProxy
@State var fontSize: CGFloat = 12
@State var paddingValue: CGFloat = 100
public var body: some View {
VStack {
Text(marker.label)
.foregroundColor(Color.gray)
.font(Font.custom("AppleSDGothicNeo-Bold", size: geometry.size.width * 0.05))
.rotationEffect(Angle(degrees: 0))
.padding(.bottom, geometry.size.width * 0.72)
}.rotationEffect(Angle(degrees: marker.degrees))
.onAppear {
paddingValue = geometry.size.width * 0.72
fontSize = geometry.size.width * 0.07
}
}
}
struct GaugeMarkerCompass: Identifiable, Hashable {
let id = UUID()
let degrees: Double
let label: String
init(degrees: Double, label: String) {
self.degrees = degrees
self.label = label
}
// adjust according to your needs
static func labelSet() -> [GaugeMarker] {
return [
GaugeMarker(degrees: 0, label: "N"),
GaugeMarker(degrees: 30, label: "30"),
GaugeMarker(degrees: 60, label: "60"),
GaugeMarker(degrees: 90, label: "E"),
GaugeMarker(degrees: 120, label: "120"),
GaugeMarker(degrees: 150, label: "150"),
GaugeMarker(degrees: 180, label: "S"),
GaugeMarker(degrees: 210, label: "210"),
GaugeMarker(degrees: 240, label: "240"),
GaugeMarker(degrees: 270, label: "W"),
GaugeMarker(degrees: 300, label: "300"),
GaugeMarker(degrees: 330, label: "330")
]
}
}
}
struct CompassView_Previews: PreviewProvider {
static var previews: some View {
CompassView(heading: 0)
.background(.white)
}
}
尝试这种方法,将
attach
旋转和动画添加到中VStack
,如示例代码所示,或者如果您愿意,您可以将attach
旋转效果和动画添加到CompassView
中ContentView
。也重命名
GaugeMarkerCompass
为GaugeMarker
好吧,在我尝试了一下 @workingdog 的答案(这再次非常有帮助,值得赞赏)之后,我做了以下更改,我想将它们发布出来,以防有人需要帮助。我决定将指南针的固定部分和移动部分分开。这是固定部分的代码和图片: fixed_compass_part
这是代码
然后我进行了旋转和我接受的答案中建议的动画,并应用了防止标签消失的事务(顺便说一下非常有用)并将 .rotationEffect 和 .animation 添加到 VStack。
见图:compass_moving_part
这是代码:
At the end in the ContentView, I just overlay them and everything seems to work as expected: see picture compass_complete_view see code:
I am using the internal compass of the iPhone for the moment and the heading is taken from there. It is located in a separate file called CompassLogic
code:
Now, there is only one problem remaining - when the compass reaches 360 or 0 degrees it rotates all the way back until it reaches 1 degree.
I know there is a way to do that, so it will behave like a real compass with transition from 360 to 1 degree properly but I haven't managed to find it so far. I have tried different solutions here and from different articles but to no success.
Does anybody have any good suggestions?