gpt4 book ai didi

ios - SwiftUI:如何确定一个 View 是显示在 NavigationView、Sheet 中还是 Root View ?

转载 作者:行者123 更新时间:2023-12-04 07:48:01 28 4
gpt4 key购买 nike

我正在处理一个需要自定义导航栏的项目,该导航栏将具有自定义按钮和标题样式,同时还允许在主导航部分下方有一个辅助 View 。

基本上,我想抽象出根据演示样式选择自定义后退按钮的需要。如果它在工作表中显示,我计划显示一个 X 图标。如果它被推到导航 View 上,我想显示一个返回错误。如果它是 Root View ,我想完全隐藏按钮。

我已经映射了 presentationMode 环境变量,但是当我访问 isPresented 值时,我总是得到 true,即使在我的应用程序的 Root View 中也是如此。

以下是我正在从事的工作的总体思路:

import SwiftUI

struct CustomNavigationBar<Content>: View where Content: View {

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

private let title: LocalizedStringKey
private let content: (() -> Content)?

private var backButton: AnyView? {

let button = Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
// custom image extension, just resolves to a back icon
Image.Icons.arrowBack
}

if (presentationMode.wrappedValue.isPresented) {
return AnyView(button)
} else {
return nil
}
}

public init(_ title: LocalizedStringKey, content: (() -> Content)? = nil) {
self.title = title
self.content = content
}

var body: some View {
VStack {
content?()
Divider().foregroundColor(.gray)
}.navigationBarTitle(title, displayMode: .large)
.frame(minHeight: 96)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton)
}
}

有没有人有使用 SwiftUI 访问 View 在表示层次结构中的位置的经验或技巧?谢谢!

最佳答案

您可以使用 SwiftUI-Introspect ,用于“从 SwiftUI 内省(introspection)底层 UIKit 组件”。

这是您正在寻找的工作示例。这是一个交互式示例,因此您可以单击不同的模式。

import Introspect
import SwiftUI

/* ... */

struct ContentView: View {

@State private var testing = 1
private let thingsToTest = 3

var body: some View {
VStack {
Picker("Testing", selection: $testing) {
ForEach(1 ... thingsToTest, id: \.self) { index in
Text("\(index)")
.tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())

Divider()

Spacer()

switch testing {
case 1:
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}

case 2:
NavigationView {
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}
}

case 3:
Text("Parent")
.sheet(isPresented: .constant(true)) {
PresentationReader { kind in
Text("Hello! Kind: \(kind.rawValue)")
}
}

default:
fatalError("Unavailable")
}

Spacer()
}
}
}
enum Kind: String {
case navigationView
case root
case sheet
}


struct PresentationReader<Content: View>: View {
typealias PresentedContent = (Kind) -> Content

@State private var kind: Kind = .root
private let content: PresentedContent

init(@ViewBuilder content: @escaping PresentedContent) {
self.content = content
}

var body: some View {
content(kind)
.presentationReader(kind: $kind)
}
}


extension View {
func presentationReader(kind: Binding<Kind>) -> some View {
self
.introspectViewController { vc in
let rootVC = UIApplication.shared.windows.first?.rootViewController
let isRoot = vc === rootVC
var isHosted: Bool { Introspect.findHostingView(from: vc.view) != nil }

if isRoot {
kind.wrappedValue = .root
} else if isHosted {
kind.wrappedValue = .navigationView
} else {
kind.wrappedValue = .sheet
}
}
}
}

它通过获取 View 所在的当前 View Controller 来工作。

  • 如果 Root View Controller 的类引用与当前 Root View Controller 相同,则这是 Root View (意味着它没有嵌入到 NavigationView.sheet 中(...)).
  • 如果这不是 Root View ,我们将检查此 View 是否嵌入到托管 View 中。如果是,则它在 NavigationView 中。
  • 如果 View 既不是 Root View 也不在 NavigationView 中,则它在 .sheet(...) 中。

这就是您的 CustomNavigationBar 经过这 3 项更改后的样子:

struct CustomNavigationBar<Content>: View where Content: View {

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State private var kind: Kind = .root // <--- CHANGE #1

private let title: LocalizedStringKey
private let content: (() -> Content)?

private var backButton: AnyView? {

let button = Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
// custom image extension, just resolves to a back icon
Image.Icons.arrowBack
}

if kind == .navigationView { // <--- CHANGE #2
return AnyView(button)
} else {
return nil
}
}

public init(_ title: LocalizedStringKey, content: (() -> Content)? = nil) {
self.title = title
self.content = content
}

var body: some View {
VStack {
content?()
.presentationReader(kind: $kind) // <--- CHANGE #3

Divider().foregroundColor(.gray)
}.navigationBarTitle(title, displayMode: .large)
.frame(minHeight: 96)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton)
}
}

关于ios - SwiftUI:如何确定一个 View 是显示在 NavigationView、Sheet 中还是 Root View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67112638/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com