SwiftUI崩溃:“前提条件失败:属性无法设置初始值:71”

时间:2020-02-02 17:54:51

标签: swiftui

我有一个非常有趣的崩溃,只有在非常特殊的情况下才会发生。我已经向Apple提交了错误报告,但也许有人在这里遇到了类似的崩溃,知道发生了什么,并且知道解决方法?

可以在https://github.com/kevinrenskers/SwiftUICrash找到显示崩溃的最小项目,但是我还在下面添加了相关代码。该项目具有3个视图:RootViewDetailsViewListViewRootView嵌入DetailsViewListView

当您按下DetailsView中的尾随导航栏按钮以切换回ListView时,就会发生崩溃。该应用程序崩溃,并显示错误“前提条件失败:属性无法设置初始值:71”。

但是,当您使用屏幕中间的Button切换回ListView时,不会发生崩溃。而且,当您从背景图片中删除.resizable()修饰符时,也不会发生崩溃。

此外,如果您将Group内的NavigationView更改为RootView,则该应用程序不会崩溃。遗憾的是,对于我的实际应用程序,这不是一个选择。

import SwiftUI

final class AppStore: ObservableObject {
  @Published var showingDetails = true
}

struct RootView: View {
  @EnvironmentObject private var store: AppStore

  var body: some View {
    Group {
      if store.showingDetails {
        DetailsView()
      } else {
        ListView()
      }
    }
  }
}

struct DetailsView: View {
  @EnvironmentObject private var store: AppStore

  var body: some View {
    NavigationView {
      ZStack {
        GeometryReader { geo in
          Image("bg")
            .resizable()
            .aspectRatio(contentMode: .fill)
            .edgesIgnoringSafeArea(.all)
            .frame(width: geo.size.width, height: geo.size.height)
        }

        Button("List") {
          self.store.showingDetails = false // <- this works fine
        }
        .padding(20)
        .background(Color.white)
      }
      .navigationBarTitle(Text("Details"))
      .navigationBarItems(trailing: trailingNavigationBarItem)
    }
  }

  private var trailingNavigationBarItem: some View {
    Button("List") {
      self.store.showingDetails = false // <- this crashes the app!
    }
  }
}

struct ListView: View {
  @EnvironmentObject private var store: AppStore

  var body: some View {
    NavigationView {
      Button("Load details") {
        self.store.showingDetails = true
      }
      .padding(20)
      .background(Color.white)
      .navigationBarTitle("List")
    }
  }
}

4 个答案:

答案 0 :(得分:3)

尝试用RootView注释替换@ViewBuilder中的组:

struct RootView: View {
  @EnvironmentObject private var store: AppStore

  @ViewBuilder
  var body: some View {
    if store.showingDetails {
      DetailsView()
    } else {
      ListView()
    }
  }
}

我不确定那通常有多可靠。过去,我在插入@ViewBuilder注释中取得了一些成功,但这似乎可以解决嵌套NavigationView的问题。

答案 1 :(得分:1)

编辑:此替代方法导致iPad的拆分导航视图出现问题。请参阅我的其他答案以获得更好的解决方法。


一种解决方法是将RootView的{​​{1}}包装在Group中,并带有隐藏的导航栏(每个嵌套视图都可能具有自己的导航栏,但并非所有的视图都有一个):

NavigationView

崩溃仍然很奇怪。

答案 2 :(得分:1)

这里是替代解决方法(实际上是在避免发生此问题的可能性),并且经过测试,没有任何不良影响。只是为了考虑...

这个想法不是要删除DetailsView,而是要使其明确处于非活动状态和隐藏状态。经过Xcode 11.2 / iOS 13.2的测试,没有崩溃。

struct RootView: View {
  @EnvironmentObject private var store: AppStore

  var body: some View {
    ZStack {
        ListView()
            .zIndex(store.showingDetails ? 0 : 1)   // << bring to front
        DetailsView()
            .opacity(store.showingDetails ? 1 : 0)  // << hide
            .disabled(!store.showingDetails)        // << deactivate
    }
  }
}

其他视图无变化。

答案 3 :(得分:0)

最后,有效的解决方法是通过UIViewRepresentable使用自定义UIImageView。

struct CustomImage: UIViewRepresentable {
  var image: UIImage
  var frame: CGRect

  func makeUIView(context: Context) -> UIView {
    let imageView = UIImageView(frame: frame)
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.image = image

    let view = UIView(frame: frame)
    view.translatesAutoresizingMaskIntoConstraints = false

    view.addSubview(imageView)

    return view
  }

  func updateUIView(_ uiView: UIView, context: Context) {
  }
}

然后这样使用:

GeometryReader { geo in
  CustomImage(image: UIImage(named: "bg")!, frame: CGRect(x: 0, y: 0, width: geo.size.width, height: geo.size.height))
}
.edgesIgnoringSafeArea(.all)

另请参阅https://github.com/kevinrenskers/SwiftUICrash/tree/workarounds/CustomImage