ios 视图类型的SwiftUI绑定属性

zpqajqem  于 2023-03-31  发布在  iOS
关注(0)|答案(1)|浏览(160)

我创建的PopupView是这样的:

import SwiftUI

struct PopupView<Content>: View where Content: View {
    @Binding var isPresented: Bool
    @Binding var viewForPresentation: Content
    var content: () -> Content
    var body: some View {
        ZStack(alignment: .bottom) {
            content()
                .blur(radius: isPresented ? 3 : 0)
                .disabled(isPresented)
            HStack {
                viewForPresentation
            }
            .opacity(isPresented ? 1 : 0)
        }
    }
}

现在我想在另一个视图中使用它,如下所示:

struct ServiceEditView: View {
    @State var isPopupPresented: Bool
    @State var viewForPopupPresentation: any View

    private var firstView: some View {
            Button {
                isPopupPresented = false
            } label: {
                Text("First View")
            }
    }
    
    private var secondView: some View {
            Button {
                isPopupPresented = false
            } label: {
                Text("Second View")
            }
    }

    var body: some View {
        PopupView(isPresented: $isPopupPresented, viewForPresentation: $viewForPopupPresentation) {
            // ❌ Type 'any View' cannot conform to 'View'
            HStack {
                Button {
                    viewForPopupPresentation = firstView
                    isPopupPresented = true
                } label: {
                    Text("1st")
                }
                Button {
                    viewForPopupPresentation = secondView
                    isPopupPresented = true
                } label: {
                    Text("2nd")
                }
            }
        }
    }
}

是否有可能使用视图类型的绑定属性来呈现另一个视图?
我想用同一个单一的机制来呈现不同的观点,这取决于它来自哪里。

htrmnn0y

htrmnn0y1#

您的方法并不遵循“状态”方法。一般来说,您应该基于状态控制body中的内容,同时存储希望在属性(var viewForPopupPresentation: any View)中显示的视图。
SwiftUI已经做了类似的事情:使用.sheet(item:content:)修饰符呈现一个新的Sheet。所以我会为您的情况做一些非常相似的事情。
首先创建一个PopupModifier

struct PopupModifier<PopupView, Item>: ViewModifier where PopupView: View {
  @Binding var item: Item?
  
  var popupView: (Item) -> PopupView
  
  private var isPresented: Bool { item != nil }
  
  func body(content: Content) -> some View {
    ZStack(alignment: .bottom) {
      content
        .blur(radius: isPresented ? 3 : 0)
        .disabled(isPresented)
      if let item {
        popupView(item)
      }
      // you don't need any opacity modifier here, the view appears only if item is not nil
    }
  }
}

extension View {
  // just a syntactic sugar to apply the modifier, just like SwiftUI does
  func popup<PopupView, Item>(
    item: Binding<Item?>,
    @ViewBuilder content: @escaping (Item) -> PopupView
  ) -> some View where PopupView: View {
    modifier(PopupModifier(item: item, popupView: content))
  }
}

现在,我们可以使用与sheet非常相似的修饰符。

struct ServiceEditView: View {
  enum ViewToShow {
    case first
    case second
  }
  // the source of truth of your popup. When value is nil, no popup is shown
  @State var viewToShow: ViewToShow?
  
  private var firstView: some View {
    Button {
      viewToShow = nil
    } label: {
      Text("First View")
    }
  }
  
  private var secondView: some View {
    Button {
      viewToShow = nil
    } label: {
      Text("Second View")
    }
  }
  
  var body: some View {
    HStack {
      Button {
        viewToShow = .first
      } label: {
        Text("1st")
      }
      Button {
        viewToShow = .second
      } label: {
        Text("2nd")
      }
    }
    // apply the modifier, the closure will take your unwrapped item
    // and you can use it to decide which view will be returned
    .popup(item: $viewToShow) { viewToShow in
      switch viewToShow {
      case .first: firstView
      case .second: secondView
      }
    }
  }
}

相关问题