【问题标题】:Animating Background Image based on Picker value SwiftUI基于 Picker 值的 SwiftUI 动画背景图像
【发布时间】:2021-06-03 20:35:30
【问题描述】:

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

@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")
    }
}

【问题讨论】:

  • 尝试将 withAnimation 放入 onChange 中。
  • 试过了,没用

标签: swift animation swiftui


【解决方案1】:

您的选择器选择类型为String,您的选择器数据类型为Text,因此不会调用您的onChange 修饰符。选择器选择类型和content 类型必须相同,@Asperi 在链接Picker in SwiftUI works with one version of ForEach, but not the other - a bug, or expected behavior? 中也有解释。相反,您可以使用 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"
        }
        
    }
}

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

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())
            
        }
    }
}

【讨论】:

  • 在我的代码中,我已经用 ForEach 拥有了它。我试图在 StackOverflow 中使它更简单,并弄乱了代码。对不起,这是我的错!我现在尝试直接在 onChange 中分配新值。图像再次发生变化,但没有动画。知道为什么吗?
  • @Tobilence 问题解决了吗?我可以在我的代码中看到动画。我刚刚在 onChange 中添加了 withAnimation 块。
  • 不,在我的代码中它仍然不起作用 - 给我几分钟,我会用我当前的代码更新我的帖子。
  • 我编辑了它 - 我现在在选择器中使用索引而不是字符串值,但没有动画发生
  • @Tobilence 什么是 genderPickerIndex?我把它改成了pickerIndex,它工作正常。
【解决方案2】:

好的,为了得到我想要的动画,我最终创建了一个额外的组件:

struct ImageSwitcher: View {
    
    let firstImage: String
    let secondImage: String
    
    @Binding var firstImageShowing: Bool
    
    var body: some View {
        ZStack {
            Image(firstImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .edgesIgnoringSafeArea(.all)
                .opacity(firstImageShowing ? 1 : 0)
                .transition(.move(edge: .trailing))
                .animation(.easeInOut(duration: 0.17))
                .isHidden(!firstImageShowing)
               
            Image(secondImage)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .edgesIgnoringSafeArea(.all)
                .opacity(firstImageShowing ? 0 : 1)
                .transition(.move(edge: .leading))
                .isHidden(firstImageShowing)
                .animation(.easeInOut(duration: 0.17))
        }
    }
}

.isHidden 修饰符是基于状态变量隐藏图像的扩展。您可以在这篇文章中看到更多详细信息:Dynamically hiding view in SwiftUI

您显然可以修改过渡,但我最终使用过渡和不透明度修改器同时进行了移动和淡入/淡出。

用法:

创建状态变量:

@State private var showFirst = true

然后将它与您的图像名称一起提供给组件:

ImageSwitcher(firstImage: "img-1", secondImage: "img-2", firstImageShowing: $showFirst)

【讨论】:

    猜你喜欢
    • 2021-02-07
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 2011-01-08
    • 2023-04-04
    相关资源
    最近更新 更多