为什么计时器更改会触发LazyvGrid视图更新?

发布于 2025-01-21 12:08:11 字数 5116 浏览 1 评论 0原文

import SwiftUI

struct ContentView: View {
    @State private var set = Set<Int>()
    @State private var count = "10"
    private let columns:[GridItem] = Array(repeating: .init(.flexible()), count: 3)
    
    @State private var timer:Timer? = nil
    @State private var time = 0
    
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(Array(set)) { num in
                        Text(String(num))
                    }
                }
            }
            .frame(width: 400, height: 400, alignment: .center)
            
            HStack{
                TextField("Create \(count) items", text: $count)
                
                Button {
                    createSet(count: Int(count)!)
                } label: {
                    Text("Create")
                }
            }
            
            if let _ = timer {
                Text(String(time))
                    .font(.title2)
                    .foregroundColor(.green)
            }
            
            HStack {
                Button {
                    time = 100
                    
                    let timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
                        time -= 10
                        
                        if time == 0 {
                            self.timer?.invalidate()
                            self.timer = nil
                        }
                    }
                    
                    self.timer = timer
                } label: {
                    Text("Start Timer")
                }
                
                Button {
                    self.timer?.invalidate()
                    self.timer = nil
                } label: {
                    Text("Stop Timer")
                }
            }
        }
        .padding()
    }
    
    private func createSet(count:Int) {
        set.removeAll(keepingCapacity: true)
        
        repeat {
            let num = Int.random(in: 1...10000)
            set.insert(num)
        } while set.count < count
    }
}

extension Int:Identifiable {
    public var id:Self { self }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我在text(String(num))上提出了一个断点。每次计时器触发时,GridView都会更新。为什么会发生这种情况?由于网格模型没有改变。

如果我将计时器放置在另一个视图中,则已更新

,网格视图不会触发。

import SwiftUI

struct ContentView: View {
    @State private var set = Set<Int>()
    @State private var count = "10"
    private let columns:[GridItem] = Array(repeating: .init(.flexible()), count: 3)
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(Array(set)) { num in
                        Text(String(num))
                    }
                }
            }
            .frame(width: 400, height: 400, alignment: .center)
            
            HStack{
                TextField("Create \(count) items", text: $count)
                
                Button {
                    createSet(count: Int(count)!)
                } label: {
                    Text("Create")
                }
            }
            
            TimerView()
        }
        .padding()
    }
    
    private func createSet(count:Int) {
        set.removeAll(keepingCapacity: true)
        
        repeat {
            let num = Int.random(in: 1...10000)
            set.insert(num)
        } while set.count < count
    }
}

extension Int:Identifiable {
    public var id:Self { self }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
import SwiftUI

struct TimerView: View {
    @State private var timer:Timer? = nil
    @State private var time = 0
    
    var body: some View {
        if let _ = timer {
            Text(String(time))
                .font(.title2)
                .foregroundColor(.green)
        }
        
        HStack {
            Button {
                time = 100
                
                let timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
                    time -= 10
                    
                    if time == 0 {
                        self.timer?.invalidate()
                        self.timer = nil
                    }
                }
                
                self.timer = timer
            } label: {
                Text("Start Timer")
            }
            
            Button {
                self.timer?.invalidate()
                self.timer = nil
            } label: {
                Text("Stop Timer")
            }
        }
    }
}

struct TimerView_Previews: PreviewProvider {
    static var previews: some View {
        TimerView()
    }
}
import SwiftUI

struct ContentView: View {
    @State private var set = Set<Int>()
    @State private var count = "10"
    private let columns:[GridItem] = Array(repeating: .init(.flexible()), count: 3)
    
    @State private var timer:Timer? = nil
    @State private var time = 0
    
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(Array(set)) { num in
                        Text(String(num))
                    }
                }
            }
            .frame(width: 400, height: 400, alignment: .center)
            
            HStack{
                TextField("Create \(count) items", text: $count)
                
                Button {
                    createSet(count: Int(count)!)
                } label: {
                    Text("Create")
                }
            }
            
            if let _ = timer {
                Text(String(time))
                    .font(.title2)
                    .foregroundColor(.green)
            }
            
            HStack {
                Button {
                    time = 100
                    
                    let timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
                        time -= 10
                        
                        if time == 0 {
                            self.timer?.invalidate()
                            self.timer = nil
                        }
                    }
                    
                    self.timer = timer
                } label: {
                    Text("Start Timer")
                }
                
                Button {
                    self.timer?.invalidate()
                    self.timer = nil
                } label: {
                    Text("Stop Timer")
                }
            }
        }
        .padding()
    }
    
    private func createSet(count:Int) {
        set.removeAll(keepingCapacity: true)
        
        repeat {
            let num = Int.random(in: 1...10000)
            set.insert(num)
        } while set.count < count
    }
}

extension Int:Identifiable {
    public var id:Self { self }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I made a break point on Text(String(num)). Every time the timer was trigger, the GridView updated. Why this happened? As the model of grid didn't change.

Updated

If I put the timer in another view, the grid view wouldn't be trigger.

import SwiftUI

struct ContentView: View {
    @State private var set = Set<Int>()
    @State private var count = "10"
    private let columns:[GridItem] = Array(repeating: .init(.flexible()), count: 3)
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(Array(set)) { num in
                        Text(String(num))
                    }
                }
            }
            .frame(width: 400, height: 400, alignment: .center)
            
            HStack{
                TextField("Create \(count) items", text: $count)
                
                Button {
                    createSet(count: Int(count)!)
                } label: {
                    Text("Create")
                }
            }
            
            TimerView()
        }
        .padding()
    }
    
    private func createSet(count:Int) {
        set.removeAll(keepingCapacity: true)
        
        repeat {
            let num = Int.random(in: 1...10000)
            set.insert(num)
        } while set.count < count
    }
}

extension Int:Identifiable {
    public var id:Self { self }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
import SwiftUI

struct TimerView: View {
    @State private var timer:Timer? = nil
    @State private var time = 0
    
    var body: some View {
        if let _ = timer {
            Text(String(time))
                .font(.title2)
                .foregroundColor(.green)
        }
        
        HStack {
            Button {
                time = 100
                
                let timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
                    time -= 10
                    
                    if time == 0 {
                        self.timer?.invalidate()
                        self.timer = nil
                    }
                }
                
                self.timer = timer
            } label: {
                Text("Start Timer")
            }
            
            Button {
                self.timer?.invalidate()
                self.timer = nil
            } label: {
                Text("Stop Timer")
            }
        }
    }
}

struct TimerView_Previews: PreviewProvider {
    static var previews: some View {
        TimerView()
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

别在捏我脸啦 2025-01-28 12:08:11

这几乎是Swiftui的工作方式。每个更改@State var触发重新评估的视图。如果将foreach放在另一个视图中,则只有更改更改视图的VAR时,才会重新评估。例如set

struct ExtractedView: View {
    var columns: [GridItem]
    var set: Set<Int>
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(Array(set)) { num in
                    Text(String(num))
                }
            }
        }
        .frame(width: 400, height: 400, alignment: .center)
    }
}

swiftui中鼓励它制作许多小型视图。驾驶此驱动的系统在识别需要更改的内容和不进行的系统方面非常好。有一个非常好的WWDC视频描述了这一点。

wwdc

That´s pretty much how SwiftUI works. Every change to a @State var triggers the View to reevaluate. If you put your ForEach in another view it will only reevaluate if you change a var that changes that view. E.g. set or columns.

struct ExtractedView: View {
    var columns: [GridItem]
    var set: Set<Int>
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(Array(set)) { num in
                    Text(String(num))
                }
            }
        }
        .frame(width: 400, height: 400, alignment: .center)
    }
}

It is encouraged in SwiftUI to make many small Views. The system driving this is pretty good in identifying what needs to be changed and what not. There is a very good WWDC video describing this.

WWDC

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文