使用 List/ScrollView 与 SFSafariViewController 全屏冲突 sheet
Conflict using List/ScrollView with SFSafariViewController fullscreen sheet
我正在尝试制作一个项目列表,每个项目都应该在全屏模式下用 SFSafariViewController 打开 sheet。为了全屏显示 SFSafariViewController,我使用了 link: https://dev.to/uchcode/web-sheet-with-sfsafariviewcontroller-4nlc 中可用的代码。控制器工作得很好。但是,当我将项目放入 List 或 ScrollView 时,它不显示 sheet。当我删除 List/ScrollView 时它工作正常。
这是代码。
import SwiftUI
import SafariServices
struct RootView: View, Hostable {
@EnvironmentObject private var hostedObject: HostingObject<Self>
let address: String
let title: String
func present() {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
hostedObject.viewController?.present(safari, animated: true)
}
var body: some View {
Button(title) {
self.present()
}
}
}
struct ContentView: View {
@State private var articlesList = [
ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
]
var body: some View {
NavigationView{
//Here is the problem when I add RootView inside a List or ScrollView it does not show Safari.
ScrollView(.vertical, showsIndicators: false) {
VStack{
ForEach(articlesList) {article in
RootView(address: article.link, title: article.title).hosting()
Spacer(minLength: 10)
}
}
}
.navigationBarTitle("Articles")
}
}
}
struct ArticlesList: Identifiable, Codable {
let id: Int
let title: String
let link: String
let lang: String
}
struct UIViewControllerView: UIViewControllerRepresentable {
final class ViewController: UIViewController {
var didAppear: (UIViewController) -> Void = { _ in }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
didAppear(self)
}
}
var didAppear: (UIViewController) -> Void
func makeUIViewController(context: Context) -> UIViewController {
let viewController = ViewController()
viewController.didAppear = didAppear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//
}
}
struct UIViewControllerViewModifier: ViewModifier {
var didAppear: (UIViewController) -> Void
var viewControllerView: some View {
UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
}
func body(content: Content) -> some View {
content.background(viewControllerView)
}
}
extension View {
func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View {
modifier(UIViewControllerViewModifier(didAppear:didAppear))
}
}
class HostingObject<Content: View>: ObservableObject {
@Published var viewController: UIViewController? = nil
}
struct HostingObjectView<Content: View>: View {
var rootView: Content
let hostedObject = HostingObject<Content>()
func getHost(viewController: UIViewController) {
hostedObject.viewController = viewController.parent
}
var body: some View {
rootView
.uiViewController(didAppear: getHost(viewController:))
.environmentObject(hostedObject)
}
}
protocol Hostable: View {
associatedtype Content: View
func hosting() -> Content
}
extension Hostable {
func hosting() -> some View {
HostingObjectView(rootView: self)
}
}
您的代码在 iOS 14 模拟器中有效!
我包装了 SFSafariViewController,如果我用以下代码替换您的 RootView
,它可以在我的 iOS 13 设备上运行。然而,它并不是真正的 full-screen,而是 sheet。
struct RootView: View {
@State private var isPresenting = false
let address: String
let title: String
var body: some View {
Button(self.title) {
self.isPresenting.toggle()
}
.sheet(isPresented: self.$isPresenting) {
SafariView(address: URL(string: self.address)!)
}
}
}
struct SafariView: UIViewControllerRepresentable {
let address: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: self.address, configuration: config)
return safari
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
//
}
}
下面是工作代码..
import SwiftUI
import SafariServices
struct ContentView: View {
var body: some View{
TabView {
HomeView()
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}.tag(0)
ArticlesView().hosting()
.tabItem{
VStack{
Image(systemName: "quote.bubble")
Text("Articles")
}
}.tag(1)
}
}
}
struct HomeView: View {
var body: some View{
Text("This is home")
}
}
struct ShareView: View{
var body: some View{
Text("Here the share")
}
}
struct ArticlesView: View, Hostable {
@EnvironmentObject private var hostedObject: HostingObject<Self>
@State private var showShare = false
@State private var articlesList = [
ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
]
func present(address: String) {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
hostedObject.viewController?.present(safari, animated: true)
}
var body: some View {
NavigationView{
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 40){
ForEach(articlesList) {article in
Button(article.title) {
self.present(address: article.link)
}
}
}
.sheet(isPresented: $showShare){
ShareView()
}
.navigationBarTitle("Articles")
.navigationBarItems(leading:
Button(action: {
self.showShare.toggle()
})
{
Image(systemName: "plus")
}
)
}
}
}
}
struct ArticlesList: Identifiable, Codable {
let id: Int
let title: String
let link: String
let lang: String
}
struct UIViewControllerView: UIViewControllerRepresentable {
final class ViewController: UIViewController {
var didAppear: (UIViewController) -> Void = { _ in }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
didAppear(self)
}
}
var didAppear: (UIViewController) -> Void
func makeUIViewController(context: Context) -> UIViewController {
let viewController = ViewController()
viewController.didAppear = didAppear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//
}
}
struct UIViewControllerViewModifier: ViewModifier {
var didAppear: (UIViewController) -> Void
var viewControllerView: some View {
UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
}
func body(content: Content) -> some View {
content.background(viewControllerView)
}
}
extension View {
func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View {
modifier(UIViewControllerViewModifier(didAppear:didAppear))
}
}
class HostingObject<Content: View>: ObservableObject {
@Published var viewController: UIViewController? = nil
}
struct HostingObjectView<Content: View>: View {
var rootView: Content
let hostedObject = HostingObject<Content>()
func getHost(viewController: UIViewController) {
hostedObject.viewController = viewController.parent
}
var body: some View {
rootView
.uiViewController(didAppear: getHost(viewController:))
.environmentObject(hostedObject)
}
}
protocol Hostable: View {
associatedtype Content: View
func hosting() -> Content
}
extension Hostable {
func hosting() -> some View {
HostingObjectView(rootView: self)
}
}
我正在尝试制作一个项目列表,每个项目都应该在全屏模式下用 SFSafariViewController 打开 sheet。为了全屏显示 SFSafariViewController,我使用了 link: https://dev.to/uchcode/web-sheet-with-sfsafariviewcontroller-4nlc 中可用的代码。控制器工作得很好。但是,当我将项目放入 List 或 ScrollView 时,它不显示 sheet。当我删除 List/ScrollView 时它工作正常。
这是代码。
import SwiftUI
import SafariServices
struct RootView: View, Hostable {
@EnvironmentObject private var hostedObject: HostingObject<Self>
let address: String
let title: String
func present() {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
hostedObject.viewController?.present(safari, animated: true)
}
var body: some View {
Button(title) {
self.present()
}
}
}
struct ContentView: View {
@State private var articlesList = [
ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
]
var body: some View {
NavigationView{
//Here is the problem when I add RootView inside a List or ScrollView it does not show Safari.
ScrollView(.vertical, showsIndicators: false) {
VStack{
ForEach(articlesList) {article in
RootView(address: article.link, title: article.title).hosting()
Spacer(minLength: 10)
}
}
}
.navigationBarTitle("Articles")
}
}
}
struct ArticlesList: Identifiable, Codable {
let id: Int
let title: String
let link: String
let lang: String
}
struct UIViewControllerView: UIViewControllerRepresentable {
final class ViewController: UIViewController {
var didAppear: (UIViewController) -> Void = { _ in }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
didAppear(self)
}
}
var didAppear: (UIViewController) -> Void
func makeUIViewController(context: Context) -> UIViewController {
let viewController = ViewController()
viewController.didAppear = didAppear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//
}
}
struct UIViewControllerViewModifier: ViewModifier {
var didAppear: (UIViewController) -> Void
var viewControllerView: some View {
UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
}
func body(content: Content) -> some View {
content.background(viewControllerView)
}
}
extension View {
func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View {
modifier(UIViewControllerViewModifier(didAppear:didAppear))
}
}
class HostingObject<Content: View>: ObservableObject {
@Published var viewController: UIViewController? = nil
}
struct HostingObjectView<Content: View>: View {
var rootView: Content
let hostedObject = HostingObject<Content>()
func getHost(viewController: UIViewController) {
hostedObject.viewController = viewController.parent
}
var body: some View {
rootView
.uiViewController(didAppear: getHost(viewController:))
.environmentObject(hostedObject)
}
}
protocol Hostable: View {
associatedtype Content: View
func hosting() -> Content
}
extension Hostable {
func hosting() -> some View {
HostingObjectView(rootView: self)
}
}
您的代码在 iOS 14 模拟器中有效!
我包装了 SFSafariViewController,如果我用以下代码替换您的 RootView
,它可以在我的 iOS 13 设备上运行。然而,它并不是真正的 full-screen,而是 sheet。
struct RootView: View {
@State private var isPresenting = false
let address: String
let title: String
var body: some View {
Button(self.title) {
self.isPresenting.toggle()
}
.sheet(isPresented: self.$isPresenting) {
SafariView(address: URL(string: self.address)!)
}
}
}
struct SafariView: UIViewControllerRepresentable {
let address: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: self.address, configuration: config)
return safari
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
//
}
}
下面是工作代码..
import SwiftUI
import SafariServices
struct ContentView: View {
var body: some View{
TabView {
HomeView()
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}.tag(0)
ArticlesView().hosting()
.tabItem{
VStack{
Image(systemName: "quote.bubble")
Text("Articles")
}
}.tag(1)
}
}
}
struct HomeView: View {
var body: some View{
Text("This is home")
}
}
struct ShareView: View{
var body: some View{
Text("Here the share")
}
}
struct ArticlesView: View, Hostable {
@EnvironmentObject private var hostedObject: HostingObject<Self>
@State private var showShare = false
@State private var articlesList = [
ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
]
func present(address: String) {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
hostedObject.viewController?.present(safari, animated: true)
}
var body: some View {
NavigationView{
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 40){
ForEach(articlesList) {article in
Button(article.title) {
self.present(address: article.link)
}
}
}
.sheet(isPresented: $showShare){
ShareView()
}
.navigationBarTitle("Articles")
.navigationBarItems(leading:
Button(action: {
self.showShare.toggle()
})
{
Image(systemName: "plus")
}
)
}
}
}
}
struct ArticlesList: Identifiable, Codable {
let id: Int
let title: String
let link: String
let lang: String
}
struct UIViewControllerView: UIViewControllerRepresentable {
final class ViewController: UIViewController {
var didAppear: (UIViewController) -> Void = { _ in }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
didAppear(self)
}
}
var didAppear: (UIViewController) -> Void
func makeUIViewController(context: Context) -> UIViewController {
let viewController = ViewController()
viewController.didAppear = didAppear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
//
}
}
struct UIViewControllerViewModifier: ViewModifier {
var didAppear: (UIViewController) -> Void
var viewControllerView: some View {
UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
}
func body(content: Content) -> some View {
content.background(viewControllerView)
}
}
extension View {
func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View {
modifier(UIViewControllerViewModifier(didAppear:didAppear))
}
}
class HostingObject<Content: View>: ObservableObject {
@Published var viewController: UIViewController? = nil
}
struct HostingObjectView<Content: View>: View {
var rootView: Content
let hostedObject = HostingObject<Content>()
func getHost(viewController: UIViewController) {
hostedObject.viewController = viewController.parent
}
var body: some View {
rootView
.uiViewController(didAppear: getHost(viewController:))
.environmentObject(hostedObject)
}
}
protocol Hostable: View {
associatedtype Content: View
func hosting() -> Content
}
extension Hostable {
func hosting() -> some View {
HostingObjectView(rootView: self)
}
}