【问题标题】:How could I simply have a View transition to SwiftUI View?我怎么能简单地将视图转换为 SwiftUI 视图?
【发布时间】:2019-12-26 01:45:30
【问题描述】:

我想通过 SwiftUI 和 Timer 实现一个简单的视图转换。 我有一个主视图,它是内容视图。如果我从视图中调用 func FireTimer(),该函数会触发计时器。然后在 5 秒后,我会有一个视图转换。

我试过 NavigationLink,但它有一个按钮。计时器无法按下按钮,所以现在我很困惑。 我将在下面显示我的代码。

TimerFire.swift

import Foundation
import UIKit
import SwiftUI

let TIME_MOVENEXT = 5
var timerCount : Int = 0

class TimerFire : ObservableObject{
    var workingTimer = Timer()

    @objc func FireTimer() {
        print("FireTimer")
        workingTimer = Timer.scheduledTimer(timeInterval: 1,     
            target: self,                                        
            selector: #selector(TimerFire.timerUpdate),      
            userInfo: nil,                                   
            repeats: true)                                   
    }

    @objc func timerUpdate(timeCount: Int) {
        timerCount += 1
        let timerText = "timerCount:\(timerCount)"
        print(timerText)

        if timerCount == TIME_MOVENEXT {
            print("timerCount == TIME_MOVENEXT")

            workingTimer.invalidate()
            print("workingTimer.invalidate()")
            timerCount = 0

            //
            //want to have a transition to SecondView here
            //
        }
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        Button(action: {
            // What to perform
            let timerFire = TimerFire()
            timerFire.FireTimer()
        }) {
            // How the button looks like
            Text("Fire timer")
        }
    }
}

SecondView.swift

import Foundation
import SwiftUI

struct SecondView: View {
    var body: some View {
        Text("Second World")
    }
}

我怎样才能简单地显示这个 SecondView?

【问题讨论】:

    标签: ios swift timer swiftui swift5


    【解决方案1】:

    好的,如果您想在第一个屏幕上不使用NavigationView 执行此操作(出于任何原因),这是一种基于两个视图之间转换的可能方法。

    注意:预览版对过渡的支持有限,因此请在模拟器和真实设备上进行测试

    这是一个演示(初始白屏只是模拟器启动)

    这里是单个测试模块:

    import SwiftUI
    import UIKit
    
    let TIME_MOVENEXT = 5
    var timerCount : Int = 0
    
    class TimerFire : ObservableObject{
        var workingTimer = Timer()
        @Published var completed = false
    
        @objc func FireTimer() {
            print("FireTimer")
            workingTimer = Timer.scheduledTimer(timeInterval: 1,
                target: self,
                selector: #selector(TimerFire.timerUpdate),
                userInfo: nil,
                repeats: true)
        }
    
        @objc func timerUpdate(timeCount: Int) {
            timerCount += 1
            let timerText = "timerCount:\(timerCount)"
            print(timerText)
    
            if timerCount == TIME_MOVENEXT {
                print("timerCount == TIME_MOVENEXT")
    
                workingTimer.invalidate()
                print("workingTimer.invalidate()")
                timerCount = 0
    
                //
                //want to have a transition to SecondView here
                //
                self.completed = true
            }
        }
    }
    
    struct SecondView: View {
        var body: some View {
            Text("Second World")
        }
    }
    
    struct TestTransitionByTimer: View {
        @ObservedObject var timer = TimerFire()
    
        @State var showDefault = true
        var body: some View {
            ZStack {
                Rectangle().fill(Color.clear) // << to make ZStack full-screen
                if showDefault {
                    Rectangle().fill(Color.blue) // << just for demo
                        .overlay(Text("Hello, World!"))
                        .transition(.move(edge: .leading))
                }
                if !showDefault {
                    Rectangle().fill(Color.red) // << just for demo
                        .overlay(SecondView())
                        .transition(.move(edge: .trailing))
                }
            }
            .onAppear {
                self.timer.FireTimer()
            }
            .onReceive(timer.$completed, perform: { completed in
                withAnimation {
                    self.showDefault = !completed
                }
            })
        }
    }
    
    struct TestTransitionByTimer_Previews: PreviewProvider {
        static var previews: some View {
            TestTransitionByTimer()
        }
    }
    

    【讨论】:

    • 非常感谢 Asperi san,它运行良好。我有一个问题,为什么我们只能设置代码@Published var completed = false 和 self.completed = true 然后可以运行 TestTransitionByTimer?我猜转换触发器与 TestTransitionByTimer 中的 showDefault 有关,但在 TimerFire 中它已完成。为什么可以连接?
    • SwiftUI 设计的 @State 旨在仅在 View 内部使用(内部状态),我们需要通过显式动画对其进行更改,以便过渡工作。因此这是职责分离:completed 用于 Timer,showDefault 用于仅此视图的内部状态(因为其他视图可能会有不同的反应)。
    【解决方案2】:

    ContentView 没有代码 sn-p,所以我尝试自己构建简单的示例。您可以在您的情况下使用NavigationLink(destination: _, isActive: Binding&lt;Bool&gt;, label: () -&gt; _)。在接收来自Timer.publish 的更改时更改一些State var,您将立即转到SecondView

    struct TransitionWithTimer: View {
    
        @State var goToSecondWorld = false
        let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
    
        var body: some View {
    
            NavigationView {
                VStack {
                    NavigationLink(destination: SecondWorld(), isActive: self.$goToSecondWorld) {
                        Text("First World")
                            .onReceive(timer) { _ in
                                self.goToSecondWorld = true
                        }
                    }
    
                }
    
            }
    
        }
    }
    
    // you can use ZStack and opacity/offset of view's:
    
    struct TransitionWithTimerAndOffset: View {
    
        @State var goToSecondWorld = false
        let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
    
        var body: some View {
    
            ZStack {
                Text("First world") // here can be your first View
                    .opacity(self.goToSecondWorld ? 0 : 1)
                    .offset(x: goToSecondWorld ? 1000 : 0)
    
                Text("Second world") // and here second world View
                    .opacity(self.goToSecondWorld ? 1 : 0)
                    .offset(x: goToSecondWorld ? 0 : -1000)
            }
            .onReceive(timer) { _ in
                withAnimation(.spring()) {
                    self.goToSecondWorld = true
                }
    
            }
    
        }
    
    }
    
    struct SecondWorld: View {
        var body: some View {
            Text("Second World")
        }
    }
    

    【讨论】:

    • 嗨 Александр Грабовский san,感谢您提供解决方案,它有效。很抱歉,我没有发布我的 contentview。我添加了它。您的代码非常简单易读。因此您的代码将按钮设置为 NavigationLink,并观察计时器并在触发时进行转换。但是它怎么能识别只是设置 $goToSecondWorld 呢?
    • @Haru,检查代码更新,我添加了没有NavigationViewNavigationLink 的解决方案(struct TransitionWithTimerAndOffset)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    相关资源
    最近更新 更多