26

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")
            }
        }
    }
}

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")
        }) 
    }
}
4

10 回答 10

11

我有同样的问题。

我所做的是以下内容:

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
    }
}

这通过确保只有 onLoad() 的 if 条件内部的内容才会运行一次来​​解决这个问题。

更新:Apple 开发者论坛上的某个人已经提交了一张票,Apple 已经意识到了这个问题。在 Apple 解决问题之前,我的解决方案是临时破解。

于 2020-07-28T15:00:44.063 回答
6

您可以创建一个布尔变量来检查是否首先出现

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
    })
  }
}
于 2020-07-25T00:44:07.257 回答
5

您可以为此错误创建第一个出现功能

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()
        }
    }
}
于 2020-12-16T15:19:33.103 回答
4

我一直在使用这样的东西

   import SwiftUI

    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))
      }
   }

我用它代替 .onAppear()

 .onFirstAppear{
   self.vm.fetchData()
 }
于 2021-06-03T06:29:46.097 回答
3

对于仍然遇到此问题并使用 NavigationView 的每个人。将此行添加到根 NavigationView() 应该可以解决问题。

.navigationViewStyle(StackNavigationViewStyle())

从我所尝试的一切来看,这是唯一有效的方法。

于 2021-08-24T13:06:23.600 回答
2

让我们假设您现在正在设计 SwiftUI,并且您的 PM 也是物理学家和哲学家。有一天他告诉你我们应该统一 UIView 和 UIViewController,就像量子力学和相对论一样。OK,你和你的领导志同道合,投票支持“Simplicity is Tao”,并创建一个名为“View”的原子。现在你说:“视野就是一切,视野就是一切”。这听起来很棒,而且似乎可行。好吧,你提交代码并告诉 PM ......

onAppear并且onDisAppear存在于每个视图中,但您真正需要的是页面生命周期回调。如果你使用onAppearlike viewDidAppear,那么你会遇到两个问题:

  1. 受父视图影响,子视图会多次重建,导致onAppear被多次调用。
  2. SwiftUI 是封闭源代码,但你应该知道这一点:view = f(view)。所以,onAppear会运行返回一个新的View,这就是为什么onAppear被调用了两次。

我想告诉你onAppear是对的!你必须改变你的想法。不要在onAppearand中运行生命周期代码onDisAppear!您应该在“行为区域”中运行该代码。例如,在导航到新页面的按钮中。

于 2021-11-23T11:14:11.717 回答
2

我们不必这样做.onAppear(perform) 这可以在 View 的 init 上完成

于 2020-11-28T21:29:37.817 回答
1

我有这个应用程序:

@main
struct StoriesApp: App {
    
    var body: some Scene {
        WindowGroup {
            TabView {
                NavigationView {
                    StoriesView()
                }
            }
        }
    }
    
}

这是我的故事视图:

// ISSUE

struct StoriesView: View {
    
    @State var items: [Int] = []
    
    var body: some View {
        List {
            ForEach(items, id: \.self) { id in
                StoryCellView(id: id)
            }
        }
        .onAppear(perform: onAppear)
    }
    
    private func onAppear() {
        ///////////////////////////////////
        // Gets called 2 times on app start <--------
        ///////////////////////////////////
    }
    
}

我通过测量onAppear() 调用之间的差异时间解决了这个问题。根据我的观察, onAppear() 的两次调用发生在 0.02 到 0.45 秒之间:

// SOLUTION

struct StoriesView: View {
    
    @State var items: [Int] = []
    
    @State private var didAppearTimeInterval: TimeInterval = 0
    
    var body: some View {
        List {
            ForEach(items, id: \.self) { id in
                StoryCellView(id: id)
            }
        }
        .onAppear(perform: onAppear)
    }
    
    private func onAppear() {
        if Date().timeIntervalSince1970 - didAppearTimeInterval > 0.5 {
            ///////////////////////////////////////
            // Gets called only once in 0.5 seconds <-----------
            ///////////////////////////////////////
        }
        didAppearTimeInterval = Date().timeIntervalSince1970
    }
    
}
于 2021-05-14T12:38:43.753 回答
1

万一有人在我的船上,我现在是这样解决的:

struct ChannelListView: View {

@State private var searchText = ""
@State private var isNavLinkActive: Bool = false
@EnvironmentObject var channelStore: ChannelStore
@ObservedObject private var networking = Networking()

var body: some View {
    NavigationView {
        VStack {
            SearchBar(text: $searchText)
                .padding(.top, 20)
            List(channelStore.allChannels) { channel in
                ZStack {
                    NavigationLink(destination: VideoListView(channel: channel)) {
                        ChannelRowView(channel: channel)
                    }
                    HStack {
                        Spacer()
                        Button {
                            isNavLinkActive = true
                            
                            // Place action/network call here
                            
                        } label: {
                            Image(systemName: "arrow.right")
                        }
                        .foregroundColor(.gray)
                    }
                }
                .listStyle(GroupedListStyle())
            }
            .navigationTitle("Channels")
            }
        }
    }
}
于 2020-07-25T16:03:00.660 回答
1

In my case, I found that a few views up the hierarchy, .onAppear() (and .onDisappear()) was only being called once, as expected. I used that to post notifications that I listen to down in the views that need to take action on those events. It’s a gross hack, and I’ve verified that the bug is fixed in iOS 15b1, but Apple really needs to backport the fix.

于 2021-07-06T01:03:45.687 回答