如何使用SwiftUI MacOS应用程序正确添加视图,以使动画按预期工作?

时间:2019-10-21 00:22:33

标签: swift macos swiftui

我正在使用SwiftUI向MacOS应用添加一个简单视图。这是我的代码:

struct ContentView: View {
    @State var showOtherView = false
    var body: some View {
        VStack {
            Text("Hello World").padding().onTapGesture {
                withAnimation {
                    self.showOtherView.toggle()
                }
            }
            if showOtherView {
                Text("Other View").frame(height: 200).border(Color.red)
            }
        }
    }
}

动画有效,但看起来确实很糟糕。 谁能让我知道如何在动画中正确添加Views

3 个答案:

答案 0 :(得分:1)

问题在于,导航isActive状态和显示的选项卡状态均未记录。

通过记录每个选项卡的导航状态以及哪个选项卡处于活动状态,可以为每个选项卡显示正确的导航状态。

可以改进模型以删除元组并使其更灵活,但是关键是使用getter和setter对每个选项卡使用什么状态的封装模型,以便允许NavigationLink更新通过绑定。

我已经简化了顶层VStack,并删除了此处不需要的顶层开关,但是可以在实际实现中添加回去,以便在顶层使用不同类型的视图


enum TabName : String {
    case Explore, Network
}

struct ContentView: View {
    
    @State var model =  TabModel()
    
    init(){
         UINavigationBar.setAnimationsEnabled(false)
    }
    
    var body: some View {
        VStack(spacing: 0) {
            Spacer()
            AViewWhichNavigates(model: $model).background(Color.green)
            Spacer()
            CustomTabView(model:$model)
        }
    }
}

struct CustomTabView: View {
    @Binding var model: TabModel
    var body: some View {
        HStack {
            Spacer()
            Text("Explore").border(Color.black, width: 1).onTapGesture { model.selectedTab = .Explore }
            Spacer()
            Text("Network").border(Color.black, width: 1).onTapGesture { model.selectedTab = .Network }
            Spacer()
        }
    }
}

struct AViewWhichNavigates: View {
    @Binding var model:TabModel
    
    var body: some View {
        NavigationView(content: {
            NavigationLink(destination: Text("We are one level deep in navigation in \(model.selectedTab.rawValue)"), isActive: $model.isActive) {
                Text("You are at root of \(model.selectedTab.rawValue). Tap to navigate").navigationTitle(model.selectedTab.rawValue)
            }.onDisappear {
                UINavigationBar.setAnimationsEnabled(model.isActive)
            }
        })
    }
}

struct TabModel {
    var selectedTab:TabName = .Explore
    var isActive : Bool {
        get {
            switch selectedTab {
                case .Explore : return tabMap.0
                case .Network : return tabMap.1
            }
        }
        set {
            switch selectedTab {
                case .Explore : nOn(isActive, newValue); tabMap.0 = newValue;
                case .Network : nOn(isActive, newValue); tabMap.1 = newValue;
            }
        }
    }
    //tuple used to represent a fixed set of tab isActive navigation states
    var tabMap = (false, false)
     
    func  nOn(_ old:Bool,_ new:Bool ){
        UINavigationBar.setAnimationsEnabled(new && !old)
    }
    
}

答案 1 :(得分:1)

您需要的只是具有不透明性的ZStack。

import SwiftUI

enum TabName {
    case explore, network
}

struct ContentView: View {
    @State var displayedTab: TabName = .explore
    var body: some View {
        VStack {
            ZStack {
                AViewWhichNavigates(title: "Explore")
                    .background(Color.green)
                    .opacity(displayedTab == .explore ? 1 : 0)
                AViewWhichNavigates(title: "Network")
                    .background(Color.green)
                    .opacity(displayedTab == .network ? 1 : 0)
            }
            CustomTabView(displayedTab: $displayedTab)
        }
    }
}

struct CustomTabView: View {
    @Binding var displayedTab: TabName
    var body: some View {
        HStack {
            Spacer()
            Text("Explore").border(Color.black, width: 1).onTapGesture { self.displayedTab = .explore }
            Spacer()
            Text("Network").border(Color.black, width: 1).onTapGesture { self.displayedTab = .network }
            Spacer()
        }
    }
}

struct AViewWhichNavigates: View {
    let title: String
    var body: some View {
        NavigationView(content: {
            NavigationLink(destination: Text("We are one level deep in navigation")) {
                Text("You are at root. Tap to navigate").navigationTitle(title)
            }
        })
    }
}

答案 2 :(得分:0)

我认为即使使用自定义标签页视图也是可行的,因为问题在于切换标签页时重建ExploreTab(),所以该标签页的所有内容也都被重建,因此内部NavigationView处于打开状态重建在第一页上。

假设您的应用程序中只有一个ExploreTab(很明显),可能的解决方案是显式地使其Equatable,并且不允许SwiftUI在刷新时替换它。

所以

struct ExploreTab: View, Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool {
        return true    // prevent replacing ever !!
    }

    var body: some View {
       // ... your code here
    }
}

    VStack(spacing: 0) {
        switch displayedTab {
        case .explore: ExploreTab().equatable()      // << here !!
        case .network: NetworkTab()
        }
        CustomTabView(displayedTab: $displayedTab)   //This is the Custom TabBar
    }

更新:已在Xcode 12 / iOS 14上进行了测试-如上所述,其工作原理(对于标准容器,实际上是相同的想法)

这里是如上所述CustomTabView与测试环境的快速演示复制。

demo

完整的模块代码:

struct ExploreTab: View, Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool {
        return true    // prevent replacing ever !!
    }

    var body: some View {
       NavigationView {
            NavigationLink("Go", destination: Text("Explore"))
       }
    }
}

enum TestTabs {
    case explore
    case network
}

struct CustomTabView: View {
    @Binding var displayedTab: TestTabs
    var body: some View {
        HStack {
            Button("Explore") { displayedTab = .explore }
            Divider()
            Button("Network") { displayedTab = .network }
        }
        .frame(maxWidth: .infinity)
        .frame(height: 80).background(Color.yellow)
    }
}

struct TestCustomTabView: View {
    @State private var displayedTab = TestTabs.explore

    var body: some View {
        VStack(spacing: 0) {
            switch displayedTab {
                case .explore: ExploreTab().equatable()      // << here !!
                case .network: Text("NetworkTab").frame(maxWidth: .infinity, maxHeight: .infinity)
            }
            CustomTabView(displayedTab: $displayedTab)   //This is the Custom TabBar
        }

        .edgesIgnoringSafeArea(.bottom)
    }
}