Avs*_*est 22 networking list swiftui navigationlink onappear
Q1:为什么 onAppears 会被调用两次?
Q2:或者,我可以在哪里拨打网络电话?
我在代码中的几个不同位置放置了 onAppears,它们都被调用了两次。最终,我试图在显示下一个视图之前进行网络调用,所以如果您知道不使用 onAppear 的方法,我会全力以赴。
我还尝试在我的列表中放置和删除 ForEach,但它没有改变任何东西。
Xcode 12 Beta 3 -> Target iOs 14 CoreData 已启用但尚未使用
struct ChannelListView: View {
@EnvironmentObject var channelStore: ChannelStore
@State private var searchText = ""
@ObservedObject private var networking = Networking()
var body: some View {
NavigationView {
VStack {
SearchBar(text: $searchText)
.padding(.top, 20)
List() {
ForEach(channelStore.allChannels) { channel in
NavigationLink(destination: VideoListView(channel: channel)
.onAppear(perform: {
print("PREVIOUS VIEW ON APPEAR")
})) {
ChannelRowView(channel: channel)
}
}
.listStyle(GroupedListStyle())
}
.navigationTitle("Channels")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
struct VideoListView: View {
@EnvironmentObject var videoStore: VideoStore
@EnvironmentObject var channelStore: ChannelStore
@ObservedObject private var networking = Networking()
var channel: Channel
var body: some View {
List(videoStore.allVideos) { video in
VideoRowView(video: video)
}
.onAppear(perform: {
print("LIST ON APPEAR")
})
.navigationTitle("Videos")
.navigationBarItems(trailing: Button(action: {
networking.getTopVideos(channelID: channel.channelId) { (videos) in
var videoIdArray = [String]()
videoStore.allVideos = videos
for video in videoStore.allVideos {
videoIdArray.append(video.videoID)
}
for (index, var video) in videoStore.allVideos.enumerated() {
networking.getViewCount(videoID: videoIdArray[index]) { (viewCount) in
video.viewCount = viewCount
videoStore.allVideos[index] = video
networking.setVideoThumbnail(video: video) { (image) in
video.thumbnailImage = image
videoStore.allVideos[index] = video
}
}
}
}
}) {
Text("Button")
})
.onAppear(perform: {
print("BOTTOM ON APPEAR")
})
}
}
Run Code Online (Sandbox Code Playgroud)
Tap*_*asa 15
我一直在使用这样的东西
struct OnFirstAppearModifier: ViewModifier {
let perform:() -> Void
@State private var firstTime: Bool = true
func body(content: Content) -> some View {
content
.onAppear {
if firstTime {
firstTime = false
self.perform()
}
}
}
}
extension View {
func onFirstAppear( perform: @escaping () -> Void ) -> some View {
return self.modifier(OnFirstAppearModifier(perform: perform))
}
}
Run Code Online (Sandbox Code Playgroud)
我用它代替 .onAppear()
.onFirstAppear{
self.vm.fetchData()
}
Run Code Online (Sandbox Code Playgroud)
小智 14
让我们假设您现在正在设计 SwiftUI,并且您的产品经理也是一位物理学家和哲学家。有一天他告诉你我们应该统一 UIView 和 UIViewController,就像量子力学和相对论一样。OK,你和你的leader志同道合,投票给“简即道”,创建一个名为“View”的原子。现在你说:“景色就是一切,景色就是一切”。这听起来很棒而且似乎可行。好吧,你提交代码并告诉 PM\xe2\x80\xa6。
\nonAppear并且onDisAppear存在于每个视图中,但您真正需要的是页面生命周期回调。如果你使用onAppearlike viewDidAppear,那么你会遇到两个问题:
onAppear被调用多次。onAppear会运行返回一个新的View,这就是为什么onAppear被调用两次。我想告诉你的onAppear是对的!你必须改变你的想法。onAppear不要\xe2\x80\x99t 在和中运行生命周期代码onDisAppear!您应该在“行为区域”中运行该代码。例如,在导航到新页面的按钮中。
您可以创建一个 bool 变量来检查是否首先出现
struct VideoListView: View {
@State var firstAppear: Bool = true
var body: some View {
List {
Text("")
}
.onAppear(perform: {
if !self.firstAppear { return }
print("BOTTOM ON APPEAR")
self.firstAppear = false
})
}
}
Run Code Online (Sandbox Code Playgroud)
我有同样的问题。
我所做的是以下内容:
struct ContentView: View {
@State var didAppear = false
@State var appearCount = 0
var body: some View {
Text("Appeared Count: \(appearrCount)"
.onAppear(perform: onLoad)
}
func onLoad() {
if !didAppear {
appearCount += 1
//This is where I loaded my coreData information into normal arrays
}
didAppear = true
}
}
Run Code Online (Sandbox Code Playgroud)
这通过确保仅在 onLoad() 内部的 if 条件中的内容将运行一次来解决它。
更新:Apple 开发者论坛上的某个人提交了一张票,Apple 已经意识到这个问题。我的解决方案是临时破解,直到 Apple 解决问题。
对于仍然遇到此问题并使用 NavigationView 的每个人。将此行添加到根 NavigationView() 中,它应该可以解决问题。
.navigationViewStyle(StackNavigationViewStyle())
Run Code Online (Sandbox Code Playgroud)
从我尝试过的一切来看,这是唯一有效的方法。
您可以为此错误创建第一个出现的函数
extension View {
/// Fix the SwiftUI bug for onAppear twice in subviews
/// - Parameters:
/// - perform: perform the action when appear
func onFirstAppear(perform: @escaping () -> Void) -> some View {
let kAppearAction = "appear_action"
let queue = OperationQueue.main
let delayOperation = BlockOperation {
Thread.sleep(forTimeInterval: 0.001)
}
let appearOperation = BlockOperation {
perform()
}
appearOperation.name = kAppearAction
appearOperation.addDependency(delayOperation)
return onAppear {
if !delayOperation.isFinished, !delayOperation.isExecuting {
queue.addOperation(delayOperation)
}
if !appearOperation.isFinished, !appearOperation.isExecuting {
queue.addOperation(appearOperation)
}
}
.onDisappear {
queue.operations
.first { $0.name == kAppearAction }?
.cancel()
}
}
}
Run Code Online (Sandbox Code Playgroud)