如何在SwiftUI中将Picker放入上下文菜单弹出窗口

3
我需要一个按钮,它可以打开一个本地上下文菜单弹出窗口,其中有两列带有滚轮选择器。类似于苹果在他们的日期选择器中所做的方式。

enter image description here

我尝试将Picker放入菜单或.contextMenu中,但无效。
Menu {
    Picker(selection: $selection, label: Text(label)) {
        ...
    }
    .pickerStyle(WheelPickerStyle())
}

Text("Open menu")
    .contextMenu {
        Picker(selection: $selectedValue2, label: Text("")) {
            VStack {
                Picker("", selection: $selectedValue.0) {
                    ...
                }
                .pickerStyle(WheelPickerStyle())
            }
            VStack {
                Picker("", selection: $selectedValue.1) {
                    ...
                }
                .pickerStyle(WheelPickerStyle())
            }
        }
    }
1个回答

7
在菜单和contextMenu中,你只能放置按钮。
对于你的问题,你可以使用这个扩展:
extension View {
  @ViewBuilder
  func IOSPopover<Content: View>(isPresented: Binding<Bool>, arrowDirection: UIPopoverArrowDirection, @ViewBuilder content: @escaping ()->Content)->some View {
    self
      .background {
        PopOverController(isPresented: isPresented, arrowDirection: arrowDirection, content: content())
      }
  }
}

使用这个:

struct PopOverController<Content: View>: UIViewControllerRepresentable {
  @Binding var isPresented: Bool
  var arrowDirection: UIPopoverArrowDirection
  var content: Content

  @State private var alreadyPresented: Bool = false

  func makeCoordinator() -> Coordinator {
    return Coordinator(parent: self)
  }

  func makeUIViewController(context: Context) -> some UIViewController {
    let controller = UIViewController()
    controller.view.backgroundColor = .clear
    return controller
  }

  func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
    if alreadyPresented {
      if !isPresented {
        uiViewController.dismiss(animated: true) {
          alreadyPresented = false
        }
      }
    } else {
      if isPresented {
        let controller = CustomHostingView(rootView: content)
        controller.view.backgroundColor = .systemBackground
        controller.modalPresentationStyle = .popover
        controller.popoverPresentationController?.permittedArrowDirections = arrowDirection
            
        controller.presentationController?.delegate = context.coordinator
            
        controller.popoverPresentationController?.sourceView = uiViewController.view
            
        uiViewController.present(controller, animated: true)
      }
    }
  }

  class Coordinator: NSObject,UIPopoverPresentationControllerDelegate{
    var parent: PopOverController
    init(parent: PopOverController) {
      self.parent = parent
    }
    
    func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
      return .none
    }
    
    func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
      parent.isPresented = false
    }
    
    func presentationController(_ presentationController: UIPresentationController, willPresentWithAdaptiveStyle style: UIModalPresentationStyle, transitionCoordinator: UIViewControllerTransitionCoordinator?) {
      DispatchQueue.main.async {
        self.parent.alreadyPresented = true
      }
    }
  }
}

还有这个:

class CustomHostingView<Content: View>: UIHostingController<Content>{
  override func viewDidLoad() {
    super.viewDidLoad()
    preferredContentSize = view.intrinsicContentSize
  }
}

我用以下代码进行了测试:

@State private var openMenu: Bool = false
@State private var selection: Date = Date()

//MARK: - Body
var body: some View {
  VStack {
    Text(stringDate(date: selection))
      .padding(8)
      .background(Color.white.opacity(0.6).shadow(radius: 3))
      .cornerRadius(10)
      .onTapGesture {
        openMenu.toggle()
      }
      .IOSPopover(isPresented: $openMenu, arrowDirection: .down, content: {
        DatePicker("", selection: $selection, displayedComponents: .date)
          .datePickerStyle(.wheel)
      })
    }
} //END body

//MARK: Fonctions
func stringDate(date: Date) -> String {
  let formatter = DateFormatter()
  formatter.dateFormat = "MMM d, YYYY"
  return formatter.string(from: date)
}

这是结果:

result picture

请告诉我这是否有帮助!


但是如何更新这个var content: Content呢? 如果IOSPopover块有变量,它不会更新。 - undefined
适用于iOS 16。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接