SwiftUI cra sh: «ошибка предварительного условия: атрибут не смог установить начальное значение: 71» - PullRequest
2 голосов
/ 02 февраля 2020

У меня есть очень интересный cra sh, который происходит только при очень определенных c обстоятельствах. Я уже отправил отчет об ошибке в Apple, но, может быть, кто-то здесь видел подобный cra sh, знает, что происходит, и знает обходной путь?

Минимальный проект, показывающий, что cra sh может можно найти на https://github.com/kevinrenskers/SwiftUICrash, но я также добавил соответствующий код ниже. Проект имеет 3 вида: RootView, DetailsView и ListView. RootView встраивает либо DetailsView, либо ListView.

. Cra sh происходит, когда вы нажимаете кнопку задней навигационной панели в DetailsView, чтобы переключиться обратно на ListView. Приложение вылетает с ошибкой «ошибка предварительного условия: атрибут не смог установить начальное значение: 71».

Когда вы используете Button в центре экрана, чтобы переключиться обратно на ListView, однако, cra sh НЕ происходит. И когда вы удаляете модификатор .resizable() из фонового изображения, cra sh также НЕ происходит.

Кроме того, если вы измените Group на NavigationView внутри RootView приложение не создает sh. К сожалению, это не вариант для моего реального приложения.

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 ]

2 голосов
/ 14 февраля 2020

Попробуйте заменить группу в RootView аннотацией @ViewBuilder:

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

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

Я не уверен, насколько это вообще надежно. Я имел неоднозначный успех при вставке @ViewBuilder аннотаций в прошлом, но, похоже, это решает проблему с вложенными NavigationView.

0 голосов
/ 13 февраля 2020

Вот альтернативный обходной путь (на самом деле он просто избегает возможности этой проблемы), и, поскольку он был протестирован, не имеет нежелательных побочных эффектов. Просто для рассмотрения ...

Идея состоит не в том, чтобы удалить DetailsView, а сделать его явно неактивным и скрытым. Протестировано с Xcode 11.2 / iOS 13.2 без cra * sh.

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

Без изменений в других представлениях.

0 голосов
/ 13 февраля 2020

В конце концов, исправление, которое работает, заключается в использовании пользовательского UIImageView через UIViewRepresentable.

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.

0 голосов
/ 04 февраля 2020

Редактировать: этот обходной путь вызывает проблемы с разделенным навигационным обзором iPad. Смотрите мой другой ответ для лучшего обхода.


Обходной путь - обернуть RootView Group в NavigationView со скрытой панелью навигации (каждое вложенное представление может потенциально иметь свое собственная навигационная панель, не у всех она есть):

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

  var body: some View {
    NavigationView {
      Group {
        if store.showingDetails != nil {
          DetailsView(bg: store.showingDetails!)
        } else {
          ListView()
        }
      }
      .navigationBarHidden(true)
      .navigationBarTitle("")
    }
  }
}

Cra sh все еще очень и очень странно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...