在 SwiftUI 中动画显示/隐藏孩子的 VStack

时间:2021-03-14 12:53:53

标签: swiftui swiftui-animation

** 编辑 ** 怀疑这个 SwiftUI 视图嵌入的 UIKit 视图(通过 UIHostingController)是问题所在,因为@eXcore 的解决方案在预览中对我有用。这是 UIKit 布局代码:

var searchBox: UIHostingController<WKMapDropdownView> = UIHostingController(rootView: WKMapDropdownView(activityTypes: []))


searchBox.view.backgroundColor = UIColor.clear
self.view.addSubview(searchBox.view)
    
searchBox.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
    searchBox.view.topAnchor.constraint(equalTo: self.mapView!.topAnchor, constant: 30),
    searchBox.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
    searchBox.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
]
NSLayoutConstraint.activate(constraints)

========

我有一个 SwiftUI 视图,其中包含一个 VStack,其中一些子视图在点击第一个子视图时显示/隐藏。

动画看起来有点奇怪,因为动画会导致已经可见的孩子跳到中间然后扩展回顶部。我希望它发生,所以已经可见的孩子留在原地,隐藏的选项滑到下面。

我认为为孩子们制作高度动画可能会更好,或者 VStack 本身就是问题的原因。

有人可以帮忙吗?

视图模型:

import Foundation

enum WKMapDropdownState {
    case optionsCollapsed
    case optionsExpanded
    case dropdownCollapsed
}

enum WKMapSearchType {
    case postcode
    case name
    case activityType
}

class WKMapDropdownViewModel : ObservableObject {
    
    @Published var optionsExpanded : Bool = false
    
    @Published var titleVisible : Bool = true
    
    @Published var backArrowVisible : Bool = false
    
    private var currentState : WKMapDropdownState = .optionsCollapsed
    
    func toggleDropdown() {
        switch (currentState) {
            case .optionsCollapsed:
                expandOptions()
            case .optionsExpanded:
                collapseOptions()
            case .dropdownCollapsed:
    //                self.delegate?.dropdownBackTapped() -> Hide everything
                expandDropdown()
        }
    }
    
    func searchTypeButtonTapped(searchType: WKMapSearchType) {
        collapseDropdown()
    }
    
    private func showDropdownOptions() {
        optionsExpanded = true
    }
    
    private func hideDropdownOptions() {
        optionsExpanded = false
    }
    
    private func showCollapsedOpenviewButton() {
        titleVisible = true
        backArrowVisible = true
    }
    
    private func showExpandedOpenviewButton() {
        titleVisible = true
        backArrowVisible = false
    }
    
    private func expandOptions() {
        showDropdownOptions()
        currentState = .optionsExpanded
    }
    
    private func collapseOptions() {
        hideDropdownOptions()
        currentState = .optionsCollapsed
    }
    
    private func expandDropdown() {
        hideDropdownOptions()
        showExpandedOpenviewButton()
        currentState = .optionsCollapsed
    }
    
    private func collapseDropdown() {
        hideDropdownOptions()
        showCollapsedOpenviewButton()
        currentState = .dropdownCollapsed
    }
}

查看:

struct WKMapDropdownView: View {
    @ObservedObject var viewModel = WKMapDropdownViewModel()
        var body: some View {
            VStack {
                HStack {
                    Button(action: {
                        withAnimation {
                            self.viewModel.toggleDropdown()
                        }
                    }) {
                        HStack {
                            Text("I want to search...")
                                .opacity(self.viewModel.titleVisible ? 1 : 0)
                                .foregroundColor(.white)
                                .font(.custom("Roboto-Regular", size: 19))
                                .frame(maxWidth: .infinity, alignment: .leading)
                            Image(self.viewModel.optionsExpanded ? "searchBoxUpArrow" : "searchBoxDownArrow")
                                .frame(maxWidth: .infinity, alignment: .trailing)
                                .animation(nil)
    
                                    
                        }
                        .frame(maxWidth: .infinity)
                        .padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
                    }
                        .background(Color.init("Tealish"))
                }
                .frame(maxHeight: .infinity, alignment: .top)
                
                
                if self.viewModel.optionsExpanded {
                    VStack {
                        Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .postcode) }) {
                            Text("By Postcode")
                                .font(.custom("Roboto-Regular", size: 19))
                                .foregroundColor(Color.init("Tealish"))
                                .background(Color.clear)
                                .padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                        .transition(.identity)
                            
                        Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .name) }) {
                            Text("All Wiki Places")
                                .font(.custom("Roboto-Regular", size: 19))
                                .foregroundColor(Color.init("Tealish"))
                                .background(Color.clear)
                                .padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                        .transition(.identity)
                            
                        Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .activityType) }) {
                            Text("By Activity Type")
                                .font(.custom("Roboto-Regular", size: 19))
                                .foregroundColor(Color.init("Tealish"))
                                .background(Color.clear)
                                .padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                        .transition(.identity)
                    }
                    .frame(maxHeight: .infinity, alignment: .top)
                }
            }
            .frame(maxHeight: .infinity, alignment: .top)
            .background(Color(UIColor.systemBackground))
            .clipShape(RoundedRectangle(cornerRadius:10))
        }
    }
    struct WKMapDropdownView_Previews: PreviewProvider {
        static var previews: some View {
            WKMapDropdownView()
        }
}

Current behaviour which I don't want

1 个答案:

答案 0 :(得分:0)

我无法发表评论,所以我假设我正在解决正确的问题。

首先,我不太明白为什么你的 maxHeight: .infinity 中有 VStack,删除它们可能是答案的一部分,之后我有这个:

Option 1

接下来,如果您想将此下拉列表固定在顶部,我认为最简单的选择是将 Spacer() 放在下拉视图下,如下所示:

Option 2

这里是修复的概述:

struct WKMapDropdownView: View {
    @ObservedObject var viewModel = WKMapDropdownViewModel()
    var body: some View {
        VStack { // Wrapped into another VStack
            VStack {
                HStack {
                   ...
                }
                .frame(alignment: .top) // Removed maxHeight
                
                
                if self.viewModel.optionsExpanded {
                    VStack {
                        ...
                    }
                    .frame(alignment: .top) // Removed maxHeight
                }
            }
            .frame(alignment: .top) // Removed maxHeight
            .background(Color(UIColor.systemBackground))
            .clipShape(RoundedRectangle(cornerRadius:10))
            Spacer() // Added Spacer
        }
    }
}