视图之间的GeometryReader转换最初导致onAppear触发

时间:2020-11-10 06:33:54

标签: swift user-interface swiftui transition geometryreader

我一直在使用SwiftUI 1.0开发一个项目,因为它需要支持iOS 13.0设备(因此没有fullScreenCover可用)。我要求我的应用在视图之间具有清晰的过渡动画,因此我在整个应用中广泛使用GeometryReader,如下所示:

recording

现在,我已经完成了80%的UI,并且正在尝试集成数据获取部分。因此,我要做的就是在视图上的onAppear方法中调用数据获取方法。这是我的代码:

struct ContentView: View {

    @State private var isUserLoggedIn = false

    var body: some View {
        GeometryReader { geometry in
            HomeView()
            LoginView(isUserLoggedIn: $isUserLoggedIn)
                .offset(y: isUserLoggedIn ? geometry.size.height + geometry.safeAreaInsets.bottom : 0)
        }
    }
}

struct LoginView: View {

    @Binding var isUserLoggedIn: Bool

    var body: some View {
        Button("Login") {
            withAnimation {
                isUserLoggedIn.toggle()
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.red)
        .edgesIgnoringSafeArea(.all)
        .onAppear {
            print("Login appeared!")
        }
    }
}

struct HomeView: View {

    @State private var isTwoSelected = false

    var body: some View {
        GeometryReader { geometry in
            OneView(isTwoSelected: $isTwoSelected)
                .offset(x: isTwoSelected ? -geometry.size.width : 0)
            TwoView(isTwoSelected: $isTwoSelected)
                .offset(x: isTwoSelected ? 0 : geometry.size.width)
        }
    }
}

struct OneView: View {

    @Binding var isTwoSelected: Bool

    var body: some View {
        NavigationView {
            Button("Goto Two") {
                withAnimation {
                    isTwoSelected.toggle()
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.green)
            .edgesIgnoringSafeArea(.all)
            .navigationBarTitle(Text("One"))
            .onAppear {
                print("One appeared! Fetch data...")
            }
        }
    }
}

struct TwoView: View {

    @Binding var isTwoSelected: Bool

    var body: some View {
        NavigationView {
            Button("Goto One") {
                withAnimation {
                    isTwoSelected.toggle()
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.yellow)
            .edgesIgnoringSafeArea(.all)
            .navigationBarTitle(Text("Two"))
            .onAppear {
                print("Two appeared! Fetch data...")
            }
        }
    }
}

但是我现在面临的问题是它们被立即触发。您可以看到控制台打印如下:

Login appeared!
Two appeared! Fetch data...
One appeared! Fetch data...

仅当数据出现在设备的视图框中时,我该如何将其提取到相应的视图?

2 个答案:

答案 0 :(得分:0)

有关获取的问题已解决!看代码,我可以更新动画或其他小东西!这只是为您快速编码。

enter image description here

import SwiftUI




struct ContentView: View {
    
    @State var isUserLoggedIn: Bool = false
    
    var body: some View {
        
        
        
        Group
        {
            if isUserLoggedIn
            {
                Home()
            }
            else
            {
                LoginView(isUserLoggedIn: $isUserLoggedIn)
            }
        }
        
        
        
    }
}




struct LoginView: View {
    
    @Binding var isUserLoggedIn: Bool
    
    var body: some View {
        
        
        
        Button("Login") { withAnimation(.easeInOut(duration: 1.0)) { isUserLoggedIn.toggle(); print("Successful Login!") } }.font(Font.largeTitle).padding()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.red)
            .edgesIgnoringSafeArea(.all)
            .offset(y: isUserLoggedIn ? UIScreen.main.bounds.height : 0)
            .onAppear { print("Login appeared!") }
        
        
    }
}

enum appPage {
    case One
    case Two
    case Home
}



struct Home: View {
    
    
    
    @State var currentPage: appPage = appPage.Home
    @State var NavigationViewTitle: String = "Home"
    
    var body: some View {
        
        
        
        
        
        
        
        VStack
        {
            HStack {Text(NavigationViewTitle).font(Font.largeTitle).padding(); Spacer() }.padding()
            
            if      currentPage == appPage.One
            {
                OneView(currentPage: $currentPage, NavigationViewTitle: $NavigationViewTitle)
            }
            else if currentPage == appPage.Two
            {
                TwoView(currentPage: $currentPage, NavigationViewTitle: $NavigationViewTitle)
            }
            else if currentPage == appPage.Home
            {
                
                
                Button(action: {
                    
                    withAnimation { withAnimation(.spring(response: 1, dampingFraction: 0.4, blendDuration: 1)  ) {currentPage = appPage.One} }
                }){
                    HStack {Text("Goto One"); Image(systemName:"arrow.right")}
                }.font(Font.largeTitle).padding()
                
                
                Button(action: {
                    withAnimation { withAnimation(.spring(response: 1, dampingFraction: 0.4, blendDuration: 1)  ) {currentPage = appPage.Two} }
                }){
                    HStack {Text("Goto Two"); Image(systemName:"arrow.right")}
                }.font(Font.largeTitle).padding()
                
                
                
                
            }
            
            
            
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.yellow)
        .edgesIgnoringSafeArea(.all)
        .onAppear { print("Home appeared!") }
        .onChange(of: currentPage) { _ in
            
            
            if      currentPage == appPage.One
            {
                NavigationViewTitle = "View One"
            }
            else
            {
                NavigationViewTitle = "View Two"
            }
        }
        
        
    }
}





struct OneView: View {
    
    
    
    @Binding var currentPage: appPage
    @Binding var NavigationViewTitle: String
    
    var body: some View {
        
        
        
        VStack
        {
            
            Button(action: {
                
                withAnimation { withAnimation(.spring(response: 1, dampingFraction: 0.4, blendDuration: 1)  ) {currentPage = appPage.Two} }
            }){
                HStack {Text("Back to Two"); Image(systemName:"arrow.right")}
            }.font(Font.largeTitle).padding()
            
            
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.orange)
        .edgesIgnoringSafeArea(.all)
        .cornerRadius(30)
        .offset(x: currentPage == .Two ? UIScreen.main.bounds.width : 0)
        .onAppear { print("OneView appeared! Fetch data...") }
        
    }
}



struct TwoView: View {
    
    
    
    @Binding var currentPage: appPage
    @Binding var NavigationViewTitle: String
    
    var body: some View {
        
        
        
        
        VStack
        {
            
            Button(action: {
                
                withAnimation { withAnimation(.spring(response: 1, dampingFraction: 0.4, blendDuration: 1)  ) {currentPage = appPage.One} }
                
            }){
                HStack {Text("Back to One"); Image(systemName:"arrow.right")}
            }.font(Font.largeTitle).padding()
            
            
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.green)
        .edgesIgnoringSafeArea(.all)
        .cornerRadius(30)
        .offset(x: currentPage == .One ? UIScreen.main.bounds.width : 0)
        .onAppear { print("TwoView appeared! Fetch data...") }
        
    }
}

答案 1 :(得分:0)

正如@Asperi在评论中所述,transition似乎是代替offset的正确方法。这是transition的实现(仅添加已更改的视图):

struct ContentView: View {

    @State private var isUserLoggedIn = false

    var body: some View {
        if isUserLoggedIn {
            HomeView()
                .transition(.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom)))
        } else {
            LoginView(isUserLoggedIn: $isUserLoggedIn)
                .transition(.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom)))
        }
    }
}

struct HomeView: View {

    @State private var isTwoSelected = false

    var body: some View {
        Group {
            if !isTwoSelected {
                OneView(isTwoSelected: $isTwoSelected)
                    .transition(.asymmetric(insertion: .move(edge: .leading), removal: .move(edge: .trailing)))
            } else {
                TwoView(isTwoSelected: $isTwoSelected)
                    .transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
            }
        }
    }
}