如何解决Swiftui-仅需旋转所选按钮
我正在Hackingwithswift(https://www.hackingwithswift.com/books/ios-swiftui/animation-wrap-up)中学习100天的快速课程。项目6挑战1请求“返回到Guess the Flag项目并添加一些动画:单击正确的标志时,使其在Y轴上旋转360度”。 选择正确答案后,我能够使按钮旋转,但是我不知道如何仅使所选按钮旋转。
这是创建按钮的循环:
ForEach(0..<3){ number in
Button(action:{
self.flagTapped(number)
}){
FlagImage(number: number,countries: self.countries)
}
.rotation3DEffect(.degrees(self.animationAmount),axis: (x: 0,y: 1,z: 0))
}
这是flagTapped函数:
func flagTapped(_ number: Int){
if number == correctAswer{
scoreTitle = "Correct"
self.score += 1
withAnimation() {
self.animationAmount += 360
}
}
else{
scoreTitle = "Wrong. That is the flag of \(self.countries[number])"
self.score -= 1
}
showingMessage = true
}
感谢您的帮助
我正在发布整个代码,希望能得到一个使第二个建议的选项起作用的答案
struct ContentView: View {
@State private var countries = ["Estonia","France","Germany","Ireland","Italy","Nigeria","Poland","Russia","Spain","UK","US"].shuffled()
@State private var correctAnswer = Int.random(in: 0...2)
@State private var showingMessage = false
@State private var scoreTitle = ""
@State private var score = 0
@State private var animationAmount = 0.0
var body: some View {
ZStack{
LinearGradient(gradient: Gradient(colors: [.blue,.black]),startPoint: .top,endPoint: .bottom)
.edgesIgnoringSafeArea(.all)
VStack (spacing:30){
VStack{
Text("Tap the flag of")
.foregroundColor(.white)
Text(countries[correctAnswer])
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(.white)
}
ForEach(0 ..< 3) { number in
if number == self.correctAnswer {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,countries: self.countries)
}
.rotation3DEffect(.degrees(self.animationAmount),z: 0))
} else {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,axis: (x: 1,y: 0,z: 0))
}
}
Text("Your score is \(score)")
.foregroundColor(.white)
Spacer()
}
}
.alert(isPresented: $showingMessage){
Alert(title: Text(scoreTitle),message: Text(""),dismissButton: .default(Text("Continue")){
self.askQuestion()
})
}
}
func flagTapped(_ number: Int){
if number == correctAnswer{
scoreTitle = "Correct"
self.score += 1
withAnimation() {
self.animationAmount += 360
}
}
else{
scoreTitle = "Wrong. That is the flag of \(self.countries[number])"
self.score -= 1
withAnimation() {
self.animationAmount += 360
}
}
showingMessage = true
}
func askQuestion(){
countries.shuffle()
correctAnswer = Int.random(in: 0...2)
}
}
struct FlagImage: View {
var number: Int
var countries:[String]=[]
var body: some View {
Image(countries[number])
.renderingMode(.original)
.clipShape(Capsule())
.overlay(Capsule().stroke(Color.black,lineWidth: 1))
.shadow(color: .black,radius: 2)
}
}
解决方法
您正在将.rotation3DEffect
应用于每个按钮,但是只有更改self.animationAmount
才会出现动画。
self.animationAmount
只能使用correctAnswer
:
更改:
.rotation3DEffect(.degrees(self.animationAmount),axis: (x: 0,y: 1,z: 0))
收件人:
.rotation3DEffect(.degrees(number == correctAnswer ? self.animationAmount : 0),z: 0))
另一种方法是:(可惜,这似乎只在Xcode 12b5中有效):
ForEach(0 ..< 3) { number in
if number == correctAnswer {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,countries: self.countries)
}
.rotation3DEffect(.degrees(self.animationAmount),z: 0))
} else {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,countries: self.countries)
}
}
}
由于重复使用Button
代码,这似乎不太令人满意,但是如果您希望对未选择的按钮应用不同的动画,这可能会很方便。
Xcode 11.6中的代码有问题
Xcode 11.6所存在的代码问题确实是您反复遇到SwiftUI的问题,除非您小心谨慎并以正确的方式进行操作。
使用ForEach
创建视图列表时,SwiftUI能够唯一标识项目非常重要。在原始代码中,我们使用的是ForEach(0..<3)
,而这些ids
却很糟糕,因为当标志改变时它们不会改变。
要解决此问题,最好的办法是将ForEach
与Identifiable
个项目(具有唯一id
的项目)一起使用。
我将国家名称数组替换为Flag
数组。 Flag
是一个struct
,它具有唯一的id
和country
名称。另外,Flag
符合Identifiable
(这意味着它提供了唯一的id
)。由于项目是可识别的,因此我们只需执行ForEach(countries.prefix(3))
即可显示前3个国家/地区的标志。而且由于标志是Identifiable
,所以当数组更改时,视图肯定会正确重绘。
看看我所做的更改。请注意,我添加了一个附加动画,当正确的旋转时,错误的标志会消失。开始新游戏时必须恢复opacity
设置,否则标志仍然不可见。
struct FlagImage: View {
var country: String
var body: some View {
Image(country)
.renderingMode(.original)
.clipShape(Capsule())
.overlay(Capsule().stroke(Color.black,lineWidth: 1))
.shadow(color: .black,radius: 2)
}
}
struct Flag: Identifiable {
let id = UUID()
let country: String
}
struct ContentView: View {
@State private var showingScore = false
@State private var scoreTitle = ""
@State private var countries = ["Estonia","France","Germany","Ireland","Italy","Nigeria","Poland","Russia","Spain","UK","US"].shuffled().map(Flag.init)
@State private var correctAnswer = Int.random(in: 0...2)
@State private var score = 0
@State private var alertMessage = ""
@State private var animationAmount = 0.0
@State private var animatedOpacity = 1.0
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [.blue,.black]),startPoint: .top,endPoint: .bottom).edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text("Tap the flag of ").foregroundColor(.white)
Text("\(countries[correctAnswer].country) ")
.foregroundColor(.white)
.font(.largeTitle)
.fontWeight(.black)
}
ForEach(countries.prefix(3)) { flag in
Group {
if flag.country == self.countries[self.correctAnswer].country {
Button(action: {
self.flagTapped(flag.country)
}) {
FlagImage(country: flag.country)
}
.rotation3DEffect(.degrees(self.animationAmount),z: 0))
} else {
Button(action: {
self.flagTapped(flag.country)
}) {
FlagImage(country: flag.country)
}
.opacity(self.animatedOpacity)
}
}
}
Text("Score: \(score)").foregroundColor(.white)
Spacer()
}
}
.alert(isPresented: $showingScore) {
Alert(title: Text(scoreTitle),message: Text(alertMessage),dismissButton: .default(Text("Continue")) {
self.askQuestion()
})
}
}
func flagTapped(_ country: String) {
if country == countries[correctAnswer].country {
score += 1
scoreTitle = "Correct!"
alertMessage = "Your score is now \(score)"
withAnimation {
animationAmount += 360
animatedOpacity = 0
}
} else {
scoreTitle = "Wrong."
alertMessage = "That is the flag of \(country)"
score -= 1
}
showingScore = true
}
func askQuestion() {
countries.shuffle()
correctAnswer = Int.random(in: 0...2)
self.animatedOpacity = 1.0
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
代码的最低修复程序:
现在,我已经确定了为什么在Xcode 11.6中不起作用,这是对代码进行最小更改以使其起作用的原因:
- 更改
ForEach
循环以遍历包含Array
的元组(offset,element)
,并通过添加id
将国家/地区名称用作,id: \.element
。由于国家/地区名称是唯一的,因此可以确保标记更改时进行更新。 - 在闭包中,选择元组的各个部分,并分别命名为
number
和name
。 - 在
if
内的按钮上放置Group { }
语句,因为Xcode 11.6中的SwiftUI无法单独处理if
。
ForEach(Array(self.countries.prefix(3).enumerated()),id: \.element) { number,name in
Group {
if number == self.correctAnswer {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,countries: self.countries)
}
.rotation3DEffect(.degrees(self.animationAmount),z: 0))
} else {
Button(action: {
self.flagTapped(number)
}) {
FlagImage(number: number,axis: (x: 1,y: 0,z: 0))
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。