SFSafariViewController 是苹果在 iOS 9 引入的一个类,全名是 Safari Services Framework 的 SFSafariViewController。
它的作用是:在 App 内打开网页内容,但由系统 Safari 来渲染和管理。
苹果官方的定义:
“A view controller for displaying web content in your app, using Safari’s web engine.”
可以简单的理解为,相比WKWebView更简单、更安全。相比直接跳转到Safari更流畅、不会打断用户体验。
使用方式
在SwiftUI中需要封装SafariView组件:
import SwiftUI
import SafariServices
struct SafariView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
SFSafariViewController(url: url)
}
func updateUIViewController(_ vc: SFSafariViewController, context: Context) {}
}
SwiftUI使用sheet打开网页:
@State private var showSafari = false
Button("打开网页") {
showSafari = true
}
.sheet(isPresented: $showSafari) {
SafariView(url: URL(string: "https://www.fangjunyu.com")!)
.presentationDetents([.medium, .large]) // iOS 16+ 可选高度
.presentationCornerRadius(20)
}

主要特点
1、网页在独立的进程中运行,隔离你的 App,不会泄露 Cookie。
2、自带导航栏、加载进度条、分享菜单。
3、Safari样式:外观、手势、字体与 Safari 完全一致。
4、自动管理 Cookie:与系统 Safari 共用登录状态(比如登录过 Twitter / Gmail)。
5、可定制性有限:不能随意修改网页内容或注入 JS。
6、不能拦截链接:如果要完全自定义网页行为,必须用 WKWebView。
可自定义属性
需要创建更复杂的SafariView视图,定义阅读模式、背景色颜色等内容。
import SwiftUI
import SafariServices
struct SafariView: UIViewControllerRepresentable {
let url: URL
let entersReaderIfAvailable: Bool
let barTintColor: UIColor?
let controlTintColor: UIColor?
let barCollapsingEnabled: Bool
func makeUIViewController(context: Context) -> SFSafariViewController {
// 创建 Configuration
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = entersReaderIfAvailable
config.barCollapsingEnabled = barCollapsingEnabled
// 创建 Safari 控制器
let safariVC = SFSafariViewController(url: url, configuration: config)
// 设置外观
if let barColor = barTintColor {
safariVC.preferredBarTintColor = barColor
}
if let controlColor = controlTintColor {
safariVC.preferredControlTintColor = controlColor
}
return safariVC
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
// SafariViewController 的配置在创建后不能修改太多
// 所以这里一般不做修改
}
}
在SwiftUI中使用:
struct ContentView: View {
@State private var showSafari = false
var body: some View {
Button("打开网页") {
showSafari = true
}
.sheet(isPresented: $showSafari) {
SafariView(
url: URL(string: "https://www.fangjunyu.com")!,
entersReaderIfAvailable: true,
barTintColor: .systemBackground,
controlTintColor: .systemBlue,
barCollapsingEnabled: true
)
.presentationDetents([.medium, .large]) // iOS 16+ 可选高度
.presentationDragIndicator(.visible)
}
}
}
可以微调颜色、行为、是否支持阅读模式等,但不能改变UI。
entersReaderIfAvailable → 自动进入阅读模式(如果网页支持)。
barCollapsingEnabled → 滚动网页时隐藏顶部工具栏。
preferredBarTintColor → 导航栏背景色。
preferredControlTintColor → 按钮颜色(关闭、分享按钮)。
这些属性都必须在控制器创建时设置,之后再修改通常不起作用。
SwiftUI 只是包装和弹出它,不会自己提供属性修改接口。
对比同类网页打开方式
1、openURL:跳转到Safari,不支持定制,会离开当前App;
2、Link:最简单,内部通过openURL实现;
3、SFSafariViewController:Safari内嵌视图,定制样式少,不离开当前App;
4、WKWebView:自定义网页容器,定制样式丰富,不离开当前App。
总结
SFSafariViewController可以在App内使用Safari打开网页,保留了Safari的安全与性能,也提供了App的连贯体验。
在SwiftUI中SFSafariViewController模版保留底部的安全区域(造成底部是透明的效果),可以使用ignoresSafeArea()忽略安全区域,让视图最大化。
SafariView(url: URL(string: "https://fangjunyu.com/2024/06/03/%e5%ad%98%e9%92%b1%e7%8c%aa%e7%8c%aa-%e4%bd%bf%e7%94%a8%e6%9d%a1%e6%ac%be/")!, entersReaderIfAvailable: true)
.ignoresSafeArea()

相关文章
1、SwiftUI超链接Link:https://fangjunyu.com/2024/12/31/swift%e5%ba%94%e7%94%a8%e5%86%85%e5%88%9b%e5%bb%ba%e8%b6%85%e9%93%be%e6%8e%a5%e7%9a%84link%e6%96%b9%e6%b3%95/
2、SwiftUI打开链接openURL:https://fangjunyu.com/2025/11/07/swiftui%e6%89%93%e5%bc%80%e9%93%be%e6%8e%a5openurl/
3、SwiftUI WKWebView网页视图:https://fangjunyu.com/2025/11/07/swiftui-wkwebview%e7%bd%91%e9%a1%b5%e8%a7%86%e5%9b%be/
扩展知识
1、配置参数
在较新的iOS版本中,SFSafariViewController.Configuration添加了UI功能扩展和广告点击追踪归因。
1、activityButton(iOS 15+)
这是一个可自定义按钮,出现在 Safari 页面右上角(通常是“分享”按钮旁边)。
允许开发者为 Safari 页面添加一个额外的交互入口。
比如:“在 App 中打开” 、“收藏” 、“举报” 等按钮。
@available(iOS 15.0, *)
@NSCopying open var activityButton: SFSafariViewController.ActivityButton?
使用方法:
创建一个自定义按钮实例,然后赋值给配置。
if #available(iOS 15.0, *) {
let config = SFSafariViewController.Configuration()
let image = UIImage(systemName: "star.fill")!
let button = SFSafariViewController.ActivityButton(templateImage: image, extensionIdentifier: nil)
config.activityButton = button
}
这个按钮默认只会调用 Safari 自己的活动菜单逻辑(例如呼出分享菜单或扩展)。
如果希望有特定行为(比如回调),就需要结合 Safari Web Extension 或 Universal Links,让用户回到 App。
注意:
1)按钮图标必须是模板图像(单色可变色的 UIImage)。
2)不支持添加多个按钮。
3)点击行为会受限于系统安全策略,不能直接调用 App 内代码。
2、eventAttribution(iOS 15.2+)
这是一个与广告点击追踪(Ad Click Attribution) 有关的属性。
主要用于网页来源追踪,比如用户从广告点击进入网页时。
在展示 SFSafariViewController 时,将广告点击的来源信息传递进去,帮助系统在不泄露隐私的前提下进行事件归因(Attribution)。
这属于 Apple 的隐私保护广告机制(Private Click Measurement)。
@available(iOS 15.2, *)
@NSCopying open var eventAttribution: UIEventAttribution?
使用方法:
如果有广告点击事件,可以这样配置:
if #available(iOS 15.2, *) {
let attribution = UIEventAttribution(sourceIdentifier: "com.example.ad",
destinationURL: URL(string: "https://example.com")!,
sourceDescription: "Ad Campaign 123",
purchaser: nil)
config.eventAttribution = attribution
}
系统会将此信息用于隐私保护的统计归因,不会暴露用户身份。
这主要针对广告或带追踪来源的场景(例如点击 App 内广告后打开网页)。
