【问题标题】:onAppear SwiftUI iOS 14.4onAppear SwiftUI iOS 14.4
【发布时间】:2021-02-15 03:57:22
【问题描述】:

我想在选择目的地时显示旅游目的地的详细信息。下面是我创建的语法,我在.onAppear 中调用self.presenter.getDetail(request: destination.id),当程序启动并按下目的地时,xcode 说self.presenter.detailDestination!.like 不存在或为零。即使我插入 print ("TEST") 发生的错误是来自 self.presenter.detailDestination!.like 的错误 nil

struct DetailView: View {
  @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
  @State private var showingAlert = false
  @ObservedObject var presenter: GetDetailPresenter<
    Interactor<String, DestinationDomainModel, GetDetailDestinationRepository<
        GetDestinationLocaleDataSource, GetDetailDestinationRemoteDataSource,
        DetailDestinationTransformer>>,
    Interactor<String, DestinationDomainModel, UpdateFavoriteDestinationRepository<
        FavoriteDestinationLocaleDataSource, DetailDestinationTransformer>>>
  
  var destination: DestinationDomainModel
  
  var body: some View {
    ZStack {
      if presenter.isLoading {
        loadingIndicator
      } else {
        ZStack {
          GeometryReader { geo in
            ScrollView(.vertical) {
              VStack {
                self.imageCategory
                  .padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                  .frame(width: geo.size.width, height: 270)
                
                self.content
                  .padding()
              }
            }
          }
          .edgesIgnoringSafeArea(.all)
          .padding(.bottom, 80)
          
          VStack {
            Spacer()
            favorite
              .padding(EdgeInsets.init(top: 0, leading: 16, bottom: 10, trailing: 16))
          }
        }
      }
    }
    .onAppear {
      self.presenter.getDetail(request: destination.id)
    }
    .navigationBarBackButtonHidden(true)
    .navigationBarItems(leading: btnBack)
  }
}

extension DetailView {
  var btnBack : some View { Button(action: {
    self.presentationMode.wrappedValue.dismiss()
  }) {
    HStack {
      Image(systemName: "arrow.left.circle.fill")
        .aspectRatio(contentMode: .fill)
        .foregroundColor(.black)
      Text("Back")
        .foregroundColor(.black)
    }
   }
  }
  
  var spacer: some View {
    Spacer()
  }
  
  var loadingIndicator: some View {
    VStack {
      Text("Loading...")
      ActivityIndicator()
    }
  }
  
  var imageCategory: some View {
    WebImage(url: URL(string: self.destination.image))
      .resizable()
      .indicator(.activity)
      .transition(.fade(duration: 0.5))
      .aspectRatio(contentMode: .fill)
      .frame(width: UIScreen.main.bounds.width, height: 270, alignment: .center)
      .clipShape(RoundedCorner(radius: 30, corners: [.bottomLeft, .bottomRight]))
  }
  
  var header: some View {
    VStack(alignment: .leading) {
      Text("\(self.presenter.detailDestination!.like) Peoples Like This")
        .padding(.bottom, 10)
      
      Text(self.presenter.detailDestination!.name)
        .font(.largeTitle)
        .bold()
        .padding(.bottom, 5)
      
      Text(self.presenter.detailDestination!.address)
        .font(.system(size: 18))
        .bold()
      
      Text("Coordinate: \(self.presenter.detailDestination!.longitude), \(self.presenter.detailDestination!.latitude)")
        .font(.system(size: 13))
    }
  }
  
  var favorite: some View {
    Button(action: {
      self.presenter.updateFavoriteDestination(request: String(self.destination.id))
      
      self.showingAlert.toggle()
    }) {
      if self.presenter.detailDestination!.isFavorite == true {
        Text("Remove From Favorite")
          .font(.system(size: 20))
          .bold()
          .onAppear {
            self.presenter.getDetail(request: destination.id)
          }
      } else {
        Text("Add To Favorite")
          .font(.system(size: 20))
          .bold()
      }
    }
    .alert(isPresented: $showingAlert) {
      if self.presenter.detailDestination!.isFavorite == true {
        return Alert(title: Text("Info"), message: Text("Destination Has Added"),
                     dismissButton: .default(Text("Ok")))
      } else {
        return Alert(title: Text("Info"), message: Text("Destination Has Removed"),
                     dismissButton: .default(Text("Ok")))
      }
    }
    .frame(width: UIScreen.main.bounds.width - 32, height: 50)
    .buttonStyle(PlainButtonStyle())
    .foregroundColor(Color.white)
    .background(Color.red)
    .cornerRadius(12)
  }
  
  var description: some View {
    VStack(alignment: .leading) {
      Text("Description")
        .font(.system(size: 17))
        .bold()
        .padding(.bottom, 7)
      
      Text(self.presenter.detailDestination!.placeDescription)
        .font(.system(size: 15))
        .multilineTextAlignment(.leading)
        .lineLimit(nil)
        .lineSpacing(5)
    }
  }
  
  var content: some View {
    VStack(alignment: .leading, spacing: 0) {
      header
        .padding(.bottom)
      description
    }
  }
}

【问题讨论】:

  • 您使用! 的事实意味着您有发生这种情况的固有风险。你怎么确定detailDestination 不是nil
  • 实际上我可以使用? 并在它为空时填充另一个值,但在这里我相信该值存在,因为我之前尝试过使用我拥有的语法调试领域已创建,结果就在那里。
  • 不知为何.onAppear 不起作用,导致getDetail 函数无法先调用获取目的地详情
  • 你在哪里调用header,你在代码示例的底部显示?
  • 你可以看到,我已经用我所有的语法重新编辑了它

标签: ios swift swiftui


【解决方案1】:

onAppear 在第一次渲染期间被调用。这意味着视图层次结构中引用的任何值(在这种情况下为detailDestination)都将在此传递期间呈现——而不仅仅是之后onAppear

在您的header 中,您指的是self.presenter.detailDestination!.like。在第一次渲染时,不能保证 onAppear 会在您强制解开 detailDestination

之前完成它的操作

对此最简单的解决方案可能是仅在存在detailDestination 时有条件地渲染视图的其余部分。看起来您已经在尝试使用 isLoading 执行此操作,但肯定存在状态不匹配——我的猜测是在 isLoading 甚至设置为 true 之前。

因此,您的内容视图可能类似于:

if self.presenter.detailDestination != nil {
   VStack(alignment: .leading, spacing: 0) {
      header
        .padding(.bottom)
      description
    }
} else {
  EmptyView()
} 

这一切都假设您的presenter 具有@Published 属性,该属性将在detailDestination 实际加载时触发当前组件的重新渲染。

【讨论】:

  • 我还是一头雾水,你可以在我的github上查看我的项目link希望你能进一步帮助我
猜你喜欢
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-29
  • 1970-01-01
相关资源
最近更新 更多