首页 > 解决方案 > 基于 Picker 值的 SwiftUI 动画背景图像

问题描述

我有一个带有背景图像和选择器的相当简单的视图。我想根据当前选择的值更改背景图像。下面的代码有效,但是缺少图像更改的动画-知道为什么吗?

@State private var pickerIndex = 0
@State private var currentBackgroundImage = "fitness-1"

var body: some View {
        ZStack {
            Image(currentBackgroundImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .transition(.opacity)
                .edgesIgnoringSafeArea(.all)
            
            VStack(spacing: 20) {
                    
                    Picker(selection: $pickerIndex, label: Text("")) {
                        Image("img1")
                            .tag(0)
                        Image("img2")
                            .tag(1)
                    }
                    .onChange(of: genderPickerIndex, perform: { value in
                        if(value == 0) {
                            withAnimation {
                                currentBackgroundImage = "fitness-1"
                            }
                        } else {
                            withAnimation {
                                currentBackgroundImage = "fitness-2"
                            }
                        }
                    })
                    .frame(width: 100)
                    .pickerStyle(SegmentedPickerStyle())
            }
        }
    }

编辑:

似乎它与 Picker 无关 - 我只是用一个按钮替换它,图像更改仍然没有动画。我在图像上缺少一些修饰符吗?

ZStack {
    Image(currentBackgroundImage)
        .resizable()
        .aspectRatio(contentMode: .fill)
        .transition(.opacity)
        .edgesIgnoringSafeArea(.all)
           
    Button(action: {
        withAnimation {
            currentBackgroundImage = "fitness-2"
        }
    }) {
        Text("Change Image")
    }
}

标签: swiftanimationswiftui

解决方案


您的 Picker 选择是 type String,并且您的选择器数据是 type Text,因此onChange不会调用您的修饰符。选择器选择类型和content类型必须相同,@Asperi 在链接Picker in SwiftUI 中也解释了适用于 ForEach 的一个版本,但不是另一个版本 - 错误或预期行为?. 相反,您可以使用forEach并循环您的[string]姓名。

struct ContentViewsss:View{
    var images = ["ABC","CDF"]
    @State private var pickerValue = "ABC"
    @State private var currentBackgroundImage = "ABC"
    
    var body: some View {
        VStack {
            Image(currentBackgroundImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 150, height: 150)
                .clipShape(Circle())
                .edgesIgnoringSafeArea(.all)
                .transition(.opacity)
            
            Picker(selection: $pickerValue, label: Text("")) {
                ForEach(images,id:\.self) { imageName in
                    Text("\(imageName)")
                }
            }
            .onChange(of: pickerValue, perform: { value in
                
                self.changeBackgroundImage(value)
            })
            .frame(width: 200)
            .pickerStyle(SegmentedPickerStyle())
            
        }
    }
    
    func changeBackgroundImage(_ value: String) {
        switch value {
        case "ABC":
            withAnimation {
                currentBackgroundImage = "ABC"
            }//the app changes the image, but the "withAnimation" does not seem to do anything
        
        case "CDF":
            withAnimation {
                currentBackgroundImage = "CDF"
            }//the app changes the image, but the "withAnimation" does not seem to do anything
        
        default:
            currentBackgroundImage = "landing-page"
        }
        
    }
}

或者您可以直接分配newValueinside onChange,而不是单独的函数。

struct ContentViewsss:View{
    var images = ["ABC","CDF"]
    @State private var pickerValue = "ABC"
    @State private var currentBackgroundImage = "ABC"
    
    var body: some View {
        VStack {
            Image(currentBackgroundImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 150, height: 150)
                .clipShape(Circle())
                .edgesIgnoringSafeArea(.all)
                .transition(.opacity)
            
            Picker(selection: $pickerValue, label: Text("")) {
                ForEach(images,id:\.self) { imageName in
                    Text("\(imageName)")
                }
            }
            .onChange(of: pickerValue, perform: { value in
           withAnimation {
                currentBackgroundImage = value
             } 
                //self.changeBackgroundImage(value)
            })
            .frame(width: 200)
            .pickerStyle(SegmentedPickerStyle())
            
        }
    }
}

推荐阅读