SwiftUI: 在菜单中呈现多个共享链接

8
我希望呈现一个包含多个选项的共享菜单。我创建了一个菜单,并添加了所有ShareLink视图。我可以点击分享按钮,但如果我选择了ShareLink,什么也不会发生。没有错误信息。
这是我能想到的唯一创建“共享菜单”的方式:
ToolbarItemGroup(placement: SwiftUI.ToolbarItemPlacement.navigationBarTrailing) {

    Menu {
          ShareLink(
               item: URL(string: "https://www.apple.com")!,
               preview: SharePreview(
                   "Test 123",
                    image: Image(systemName: "plus")
                    )
                )
               ShareLink(
                    item: URL(string: "https://www.microsoft.com")!,
                    preview: SharePreview(
                        "Tests 321",
                         image: Image(systemName: "minus")
                    )
                )

        } label: {
               Image(systemName: "square.and.arrow.up")
        }
}

screenshot


1
提交一个错误报告,也许在RC之前就会得到修复。可能发生的情况是菜单已经出现在共享表单想要出现的位置。 - lorem ipsum
这个问题已经在iOS 16.1中得到修复!感谢您的报告。 - Jeffrey Wear
3个回答

6

菜单 中的ShareLink 目前无法正常工作。Menu 在技术上是一个新的视图控制器(UIContextMenuActionsOnlyViewController)在活动窗口上呈现。分享操作表需要一个视图控制器来进行呈现。当在菜单中点击 ShareLink 时,它会关闭菜单 VC和分享操作表。您可以通过检查打开菜单时的视图层次结构来验证此内容。

一种解决方法是手动创建 Button/MenuItem 并从底层 View 上的按钮点击显示分享操作表; 这避免了直接使用 ShareLink。

解决方法:

...
ToolbarItemGroup(placement: SwiftUI.ToolbarItemPlacement.navigationBarTrailing) {
  Menu {
    Button(action: {
      showShareSheet(url: URL("https://www.apple.com")!)
    }) {
      Label("Share1", systemImage: "square.and.arrow.up")
    }
    Button(action: {
      showShareSheet(url: URL(string: "https://www.microsoft.com")!)
    }) {
      Label("Share2", systemImage: "square.and.arrow.up")
    }
  } label: {
    Image(systemName: "square.and.arrow.up")
  }
}
...

// UIActivityViewController can be customised. 
// For examples, see https://www.hackingwithswift.com/articles/118/uiactivityviewcontroller-by-example
func showShareSheet(url: URL) {
  let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
  UIApplication.shared.currentUIWindow()?.rootViewController?.present(activityVC, animated: true, completion: nil)
}

// utility extension to easily get the window 
public extension UIApplication {
    func currentUIWindow() -> UIWindow? {
        let connectedScenes = UIApplication.shared.connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .compactMap { $0 as? UIWindowScene }
        
        let window = connectedScenes.first?
            .windows
            .first { $0.isKeyWindow }

        return window
        
    }
}


对我来说实际上并没有起作用,这里是日志 https://pastebin.com/DJPvHS50。 我正在 UISheetPresentationController 中使用它,所以可能是问题所在? - Grzegorz Barański
1
线索在第4行。在您的设置中,您正在从一个视图控制器呈现,该视图控制器已经呈现了另一个视图控制器。 - mani

2

iOS 16.1已经修复了这个问题!感谢您的反馈。


1
我已经成功地通过从UIKit框架中创建的自定义弹出窗口实现了所需的行为。
import SwiftUI

struct ShareSheetView: View {
    @State private var isShowingPopover = false
    var body: some View {
        NavigationStack {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
                
                Button("Show Popover") {
                    isShowingPopover = true
                }
                
            }
            .padding()
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button {
                        isShowingPopover.toggle()
                    } label: {
                        Image(systemName: "square.and.arrow.up")
                    }
                    .uiKitPopover(isPresented: $isShowingPopover) {
                        VStack {
                            ShareLink(
                                item: URL(string: "https://www.apple.com")!,
                                preview: SharePreview(
                                    "Test 123",
                                    image: Image(systemName: "plus")
                                )
                            )
                            Divider()
                            ShareLink(
                                item: URL(string: "https://www.microsoft.com")!,
                                preview: SharePreview(
                                    "Tests 321",
                                    image: Image(systemName: "minus")
                                )
                            )
                        }
                        .fixedSize()
                        .padding()
                    }
                }
            }
        }
    }
}

struct ShareSheetView_Previews: PreviewProvider {
    static var previews: some View {
        ShareSheetView()
    }
}

struct PopoverViewModifier<PopoverContent>: ViewModifier where PopoverContent: View {
    @Binding var isPresented: Bool
    let onDismiss: (() -> Void)?
    let content: () -> PopoverContent
    let permittedArrowDirections: UIPopoverArrowDirection = []
    
    func body(content: Content) -> some View {
        content
            .background(
                Popover(
                    isPresented: $isPresented,
                    onDismiss: onDismiss,
                    content: self.content
                )
            )
    }
}

struct Popover<Content: View> : UIViewControllerRepresentable {
    @Binding var isPresented: Bool
    let onDismiss: (() -> Void)?
    @ViewBuilder let content: () -> Content
    let permittedArrowDirections: UIPopoverArrowDirection = []
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self, content: self.content())
    }
    
    func makeUIViewController(context: Context) -> UIViewController {
        return UIViewController()
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        context.coordinator.host.rootView = self.content()
        if self.isPresented, uiViewController.presentedViewController == nil {
            let host = context.coordinator.host
            host.preferredContentSize = host.sizeThatFits(in: CGSize(width: Int.max, height: Int.max))
            host.modalPresentationStyle = UIModalPresentationStyle.popover
            host.popoverPresentationController?.delegate = context.coordinator
            host.popoverPresentationController?.sourceView = uiViewController.view
            host.popoverPresentationController?.sourceRect = uiViewController.view.bounds
            host.popoverPresentationController?.permittedArrowDirections = permittedArrowDirections
            uiViewController.present(host, animated: true, completion: nil)
        }
    }
    
    class Coordinator: NSObject, UIPopoverPresentationControllerDelegate {
        let host: UIHostingController<Content>
        private let parent: Popover
        
        init(parent: Popover, content: Content) {
            self.parent = parent
            self.host = UIHostingController(rootView: content)
        }
        
        func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
            self.parent.isPresented = false
            if let onDismiss = self.parent.onDismiss {
                onDismiss()
            }
        }
        
        func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
            return .none
        }
    }
}

extension View {
    func uiKitPopover<Content>(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil, content: @escaping () -> Content) -> some View where Content: View {
        self.modifier(PopoverViewModifier(isPresented: isPresented, onDismiss: onDismiss, content: content))
    }
}
 

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