You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中为macOS打印功能添加页眉与页脚?

可行解决方案

方案一:使用CSS @page 规则(推荐,无需修改打印逻辑)

直接在HTML内容中嵌入打印专用CSS样式,利用CSS的@page规则和伪元素定义页眉、页脚,WKWebView完全支持这套标准打印样式。

实现步骤:

  1. 在HTML字符串头部添加打印样式:
<style media="print">
    /* 设置页面边距,为页眉页脚预留空间 */
    @page {
        margin-top: 60px;
        margin-bottom: 60px;
        margin-left: 40px;
        margin-right: 40px;
    }

    /* 页眉:显示文档标题 */
    header {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        height: 40px;
        text-align: center;
        font-size: 14px;
        font-weight: bold;
    }

    /* 页脚:显示页码与总页数 */
    footer {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        height: 40px;
        text-align: center;
        font-size: 12px;
    }

    /* 用CSS计数器实现自动页码 */
    footer::after {
        content: "第 " counter(page) " 页,共 " counter(pages) " 页";
    }

    /* 避免正文被页眉页脚遮挡 */
    body {
        margin-top: 0;
        margin-bottom: 0;
    }
</style>
  1. 在HTML的<body>中添加页眉、页脚标签:
<header>你的文档标题</header>
<!-- 原有HTML内容 -->
<footer></footer>
  1. 替换代码中不可靠的延迟等待,改用WKNavigationDelegate监听页面加载完成:
    修改HTMLPrintView的实现:
public class HTMLPrintView: NSView {
    var webView: WKWebView
    
    public override init(frame frameRect: NSRect) {
        webView = WKWebView(frame: frameRect)
        super.init(frame: frameRect)
        addSubview(webView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public func printView(htmlContent: String, window parentWindow: NSWindow) {
        let webView = WKWebView(frame: .zero)
        webView.navigationDelegate = self
        webView.loadHTMLString(htmlContent, baseURL: nil)
    }
}

// 扩展实现WKNavigationDelegate
extension HTMLPrintView: WKNavigationDelegate {
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let printInfo = NSPrintInfo()
        printInfo.horizontalPagination = .fit
        printInfo.verticalPagination = .fit
        printInfo.isVerticallyCentered = true
        printInfo.isHorizontallyCentered = true
        
        let printOperation = webView.printOperation(with: printInfo)
        
        printOperation.printPanel.options.insert(.showsPaperSize)
        printOperation.printPanel.options.insert(.showsOrientation)
        printOperation.printPanel.options.insert(.showsPreview)
        
        printOperation.runModal(for: parentWindow, delegate: self, didRun: nil, contextInfo: nil)
    }
}

方案二:自定义打印视图(原生NSView绘制)

如果CSS方案无法满足复杂样式需求,可以自定义打印视图,将WKWebView作为子视图,在父视图中重写打印方法绘制页眉页脚。

实现步骤:

  1. 创建自定义打印视图子类:
class CustomPrintView: NSView {
    var webView: WKWebView!
    var documentTitle: String = ""
    
    init(frame: NSRect, webView: WKWebView, title: String) {
        self.webView = webView
        self.documentTitle = title
        super.init(frame: frame)
        addSubview(webView)
        // 为页眉页脚预留空间,调整webView位置
        webView.frame = bounds.inset(by: NSEdgeInsets(top: 60, left: 40, bottom: 60, right: 40))
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 重写绘制页面边框方法,添加页眉页脚
    override func drawPageBorder(with rect: NSRect, in context: NSGraphicsContext) {
        super.drawPageBorder(with: rect, in: context)
        
        let cgContext = context.cgContext
        
        // 绘制页眉标题
        let headerRect = NSRect(x: rect.origin.x, y: rect.origin.y + rect.size.height - 50, width: rect.size.width, height: 30)
        let headerAttrs: [NSAttributedString.Key: Any] = [
            .font: NSFont.boldSystemFont(ofSize: 14),
            .foregroundColor: NSColor.black
        ]
        NSAttributedString(string: documentTitle, attributes: headerAttrs).draw(in: headerRect)
        
        // 绘制页脚页码
        let pageNumber = cgContext.currentPage + 1
        let totalPages = cgContext.totalPages
        let footerText = "第 \(pageNumber) 页,共 \(totalPages) 页"
        let footerAttrs: [NSAttributedString.Key: Any] = [
            .font: NSFont.systemFont(ofSize: 12),
            .foregroundColor: NSColor.black
        ]
        let footerString = NSAttributedString(string: footerText, attributes: footerAttrs)
        let footerRect = NSRect(x: rect.origin.x, y: rect.origin.y + 10, width: rect.size.width, height: 30)
        footerString.draw(in: footerRect)
    }
    
    // 定义打印内容区域
    override var printFrame: NSRect {
        return bounds.inset(by: NSEdgeInsets(top: 60, left: 40, bottom: 60, right: 40))
    }
}
  1. 修改HTMLPrintView的打印逻辑,使用自定义视图:
public class HTMLPrintView: NSView {
    var webView: WKWebView
    private var documentTitle: String = ""
    
    public override init(frame frameRect: NSRect) {
        webView = WKWebView(frame: frameRect)
        super.init(frame: frameRect)
        addSubview(webView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public func printView(htmlContent: String, window parentWindow: NSWindow, documentTitle: String) {
        self.documentTitle = documentTitle
        let webView = WKWebView(frame: .zero)
        webView.navigationDelegate = self
        webView.loadHTMLString(htmlContent, baseURL: nil)
    }
}

extension HTMLPrintView: WKNavigationDelegate {
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let printInfo = NSPrintInfo()
        printInfo.horizontalPagination = .fit
        printInfo.verticalPagination = .fit
        // 清空默认边距,由自定义视图控制
        printInfo.topMargin = 0
        printInfo.bottomMargin = 0
        printInfo.leftMargin = 0
        printInfo.rightMargin = 0
        
        let printView = CustomPrintView(frame: printInfo.paperRect, webView: webView, title: documentTitle)
        let printOperation = NSPrintOperation(view: printView, printInfo: printInfo)
        
        printOperation.printPanel.options.insert(.showsPaperSize)
        printOperation.printPanel.options.insert(.showsOrientation)
        printOperation.printPanel.options.insert(.showsPreview)
        
        printOperation.runModal(for: parentWindow, delegate: self, didRun: nil, contextInfo: nil)
    }
}

关键注意事项

  • 绝对不要用固定延迟等待页面加载,必须通过WKNavigationDelegate的didFinish方法确保页面完全渲染后再打印,否则会出现样式失效或内容缺失问题。
  • CSS方案中要添加media="print",确保样式仅在打印场景生效,不影响页面正常显示。
  • 自定义打印视图时,要准确设置webView的frame,避免正文内容被页眉页脚遮挡。

内容的提问来源于stack exchange,提问作者sab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:57:05