Skip to content

0x069-浅析SwiftUI库RichText

源码地址: https://github.com/NuPlay/RichText

这个库实际代码很少,原理也很简单,组件内嵌入WkWebView用于html标签的字符串渲染。
本文匆匆一撇Swiftui与原生的交互代码。

入口页面

  1. 入口页面,作者很贴心增加了placeholder,用于loading的时候展示
  2. 组件最终高度为WebView的高度
swift
import SwiftUI

public struct RichText: View {
    @State private var dynamicHeight: CGFloat = .zero

    let html: String
    var configuration: Configuration
    var placeholder: AnyView?

    public init(html: String, configuration: Configuration = .init(), placeholder: AnyView? = nil) {
        self.html = html
        self.configuration = configuration
        self.placeholder = placeholder
    }

    public var body: some View {
        GeometryReader { proxy in
            ZStack(alignment: .top) {
                WebView(width: proxy.size.width, dynamicHeight: $dynamicHeight, html: html, configuration: configuration)

                if self.dynamicHeight == 0 {
                    placeholder
                }
            }
        }
        .frame(height: dynamicHeight)
    }
}

#Preview {
    RichText(html: "<h1>Goode</h1> <h1>Night!</h1>",
             placeholder: AnyView(Text("waiting..")))
}
import SwiftUI

public struct RichText: View {
    @State private var dynamicHeight: CGFloat = .zero

    let html: String
    var configuration: Configuration
    var placeholder: AnyView?

    public init(html: String, configuration: Configuration = .init(), placeholder: AnyView? = nil) {
        self.html = html
        self.configuration = configuration
        self.placeholder = placeholder
    }

    public var body: some View {
        GeometryReader { proxy in
            ZStack(alignment: .top) {
                WebView(width: proxy.size.width, dynamicHeight: $dynamicHeight, html: html, configuration: configuration)

                if self.dynamicHeight == 0 {
                    placeholder
                }
            }
        }
        .frame(height: dynamicHeight)
    }
}

#Preview {
    RichText(html: "<h1>Goode</h1> <h1>Night!</h1>",
             placeholder: AnyView(Text("waiting..")))
}

WebView

WebView是一个遵从UIViewRepresentable 协议的结构体,保存了传入配置、读取环境中的换行配置。
UIViewRepresentable 用法见[0x06e-UIViewRepresentable]

swift
struct WebView {
    @Environment(\.multilineTextAlignment) var alignment
    @Binding var dynamicHeight: CGFloat

    let html: String
    let conf: Configuration
    let width: CGFloat
    
    init(width: CGFloat, dynamicHeight: Binding<CGFloat>, html: String, configuration: Configuration) {
        self._dynamicHeight = dynamicHeight
        
        self.html = html
        self.conf = configuration
        self.width = width
    }
}

extension  WebView :UIViewRepresentable {
 // ....
}
struct WebView {
    @Environment(\.multilineTextAlignment) var alignment
    @Binding var dynamicHeight: CGFloat

    let html: String
    let conf: Configuration
    let width: CGFloat
    
    init(width: CGFloat, dynamicHeight: Binding<CGFloat>, html: String, configuration: Configuration) {
        self._dynamicHeight = dynamicHeight
        
        self.html = html
        self.conf = configuration
        self.width = width
    }
}

extension  WebView :UIViewRepresentable {
 // ....
}

代码太长了,不再贴 https://github.com/NuPlay/RichText/blob/main/Sources/RichText/Views/Webview.swift

代码比较简单,关键点:

  • 定义Webview bridge 交互, 网页加载完后通知webview更新高度
  • 拦截导航操作,兼容tel等协议的链接