SwiftUIHTML에서 커스텀 태그를 만들고 등록하는 방법을 소개합니다. This guide introduces how to create and register custom tags in SwiftUIHTML.
SwiftUIHTML은 세 가지 타입의 태그를 지원합니다:
- BlockTag: 블록 레벨 요소 (div, section, header 등)
- InlineTag: 인라인 텍스트 요소 (span, strong, em 등)
- InlineAttachmentTag: 인라인 첨부 요소 (img, video 등)
SwiftUIHTML supports three types of tags:
- BlockTag: Block-level elements (div, section, header, etc.)
- InlineTag: Inline text elements (span, strong, em, etc.)
- InlineAttachmentTag: Inline attachment elements (img, video, etc.)
h3 태그를 위한 커스텀 블록 태그를 만드는 예제입니다.
Example of creating a custom block tag for h3 element.
import SwiftUI
import SwiftUIHTML
struct Headinglevel3TagView: BlockTag {
let element: BlockElement
init(element: BlockElement) {
self.element = element
}
var body: some View {
HTMLBlock(element: element)
.font(.system(size: 18, weight: .semibold))
.padding(.vertical, 8)
}
}
// Configuration에 등록
let configuration = HTMLConfiguration.default
.register(tag: "h3", renderer: Headinglevel3TagView.self)header 태그에 특별한 스타일을 적용하는 커스텀 구현입니다.
Custom implementation that applies special styling to header tags.
struct HeaderTagView: BlockTag {
let element: BlockElement
init(element: BlockElement) {
self.element = element
}
var body: some View {
HTMLBlock(element: element)
.background(Color.gray.opacity(0.1))
.padding()
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray.opacity(0.3), lineWidth: 1)
)
}
}
// 사용 예제
let configuration = HTMLConfiguration.default
.register(tag: "header", renderer: HeaderTagView.self)
let html = """
<header>
<h1>앱 제목</h1>
<p>앱 설명</p>
</header>
"""ul 태그를 처리하는 커스텀 블록 태그입니다. 기본적으로 ul은 지원되지 않으므로 커스텀으로 구현해야 합니다.
Custom block tag for handling ul tags. Since ul is not supported by default, it needs custom implementation.
struct ULTagView: BlockTag {
let element: BlockElement
init(element: BlockElement) {
self.element = element
}
var body: some View {
VStack(alignment: .leading, spacing: 4) {
ForEach(element.children.indices, id: \.self) { index in
if case let .node(node) = element.children[index],
node.tag == "li" {
HStack(alignment: .top, spacing: 8) {
Text("•")
.font(.system(size: 16))
HTMLNodeView(node: node)
}
}
}
}
.padding(.leading, 16)
}
}
// 사용 예제
let configuration = HTMLConfiguration.default
.register(tag: "ul", renderer: ULTagView.self)
let html = """
<ul>
<li>첫 번째 항목</li>
<li>두 번째 항목</li>
<li>세 번째 항목</li>
</ul>
"""AVPlayer를 사용하여 비디오를 재생하는 커스텀 태그입니다.
Custom tag that plays videos using AVPlayer.
import AVKit
struct VideoTagView: BlockTag, Equatable {
static func == (lhs: VideoTagView, rhs: VideoTagView) -> Bool {
lhs.element == rhs.element
}
@MainActor
final class Context: ObservableObject {
private var player: AVPlayer?
private var videoURL: URL?
@Published var ratio: CGFloat = 1
func player(url: URL?) -> AVPlayer? {
guard let url else { return nil }
guard videoURL != url else { return player }
let _player = AVPlayer(url: url)
videoURL = url
player = _player
_player.play()
return player
}
}
let element: BlockElement
let url: URL?
@StateObject var context = Context()
init(element: BlockElement) {
self.element = element
self.url = element.attributes["src"]?.url
}
var body: some View {
if let player = context.player(url: url) {
VideoPlayer(player: player)
.aspectRatio(context.ratio, contentMode: .fit)
.frame(height: 200)
}
}
}
// 사용 예제
let configuration = HTMLConfiguration.default
.register(tag: "video", renderer: VideoTagView.self)
let html = """
<video src="https://example.com/video.mp4" />
"""텍스트를 하이라이트하는 커스텀 인라인 태그를 만듭니다.
Create a custom inline tag that highlights text.
struct HighlightTag: InlineTag {
static func applyStyles(
with attributes: [String: AttributeValue],
to styleContainer: inout HTMLStyleContainer
) {
// 노란색 배경 적용
styleContainer.backgroundColor = .yellow.withAlphaComponent(0.3)
// 속성에서 색상 지정 가능
if let colorValue = attributes["color"]?.string {
styleContainer.foregroundColor = Color(hex: colorValue)
}
}
}
// 사용 예제
let configuration = HTMLConfiguration.default
.register(tag: "mark", renderer: HighlightTag.self)
.register(tag: "highlight", renderer: HighlightTag.self)
let html = """
<p>이것은 <mark>하이라이트된</mark> 텍스트입니다.</p>
<p>커스텀 <highlight color="#ff0000">빨간색 하이라이트</highlight>도 가능합니다.</p>
"""코드를 표시하는 커스텀 블록 태그입니다.
Custom block tag for displaying code.
struct CodeBlockTag: BlockTag {
let element: BlockElement
init(element: BlockElement) {
self.element = element
}
var body: some View {
ScrollView(.horizontal, showsIndicators: true) {
Text(element.text)
.font(.system(.body, design: .monospaced))
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
.textSelection(.enabled)
}
.frame(maxWidth: .infinity)
}
}
// pre 태그와 code 태그 모두 등록
let configuration = HTMLConfiguration.default
.register(tag: "pre", renderer: CodeBlockTag.self)
.register(tag: "code", renderer: CodeBlockTag.self)태그를 동적으로 등록하고 제거하는 방법입니다.
How to dynamically register and remove tags.
// 여러 태그 한 번에 등록
let configuration = HTMLConfiguration.default
.register(tag: "h3", "h4", "h5", "h6", renderer: HeadingTag.self)
.register(tag: "mark", "highlight", renderer: HighlightTag.self)
// 특정 태그 제거
let modifiedConfig = configuration
.remove(tag: "h6") // h6 태그 제거
// 모든 태그 제거 후 재등록
let cleanConfig = HTMLConfiguration()
.removeAll() // 모든 기본 태그 제거
.register(tag: "div", renderer: MyCustomDivTag.self)
.register(tag: "p", renderer: MyCustomParagraphTag.self)전역 스타일 처리를 위한 AttributeStyler를 구현합니다.
Implement AttributeStyler for global style processing.
struct MyCustomAttributeStyler: AttributeStyleable {
func applyStyles(
attributes: [String: AttributeValue],
to styleContainer: inout HTMLStyleContainer
) {
// 모든 요소에 기본 패딩 적용
if attributes["style"] != nil {
styleContainer.padding = EdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
}
// data-theme 속성 처리
if let theme = attributes["data-theme"]?.string {
switch theme {
case "dark":
styleContainer.backgroundColor = .black
styleContainer.foregroundColor = .white
case "light":
styleContainer.backgroundColor = .white
styleContainer.foregroundColor = .black
default:
break
}
}
}
}
// Configuration에 적용
let configuration = HTMLConfiguration.default
.attributeStyler(MyCustomAttributeStyler())여러 커스텀 태그를 함께 사용하는 완전한 예제입니다.
Complete example using multiple custom tags together.
struct ContentView: View {
let html = """
<header>
<h1>커스텀 태그 데모</h1>
<h3>다양한 커스텀 태그 예제</h3>
</header>
<main>
<section>
<h2>리스트 예제</h2>
<ul>
<li>커스텀 UL 태그</li>
<li><mark>하이라이트</mark>된 항목</li>
</ul>
</section>
<section>
<h2>코드 예제</h2>
<code>
let message = "Hello, SwiftUIHTML!"
print(message)
</code>
</section>
<section>
<h2>비디오</h2>
<video src="https://example.com/sample.mp4" />
</section>
</main>
"""
var body: some View {
ScrollView {
HTMLView(html: html, parser: HTMLFuziParser())
.htmlEnvironment(\.configuration, customConfiguration())
.padding()
}
}
func customConfiguration() -> HTMLConfiguration {
HTMLConfiguration.default
.register(tag: "h3", renderer: Headinglevel3TagView.self)
.register(tag: "header", renderer: HeaderTagView.self)
.register(tag: "ul", renderer: ULTagView.self)
.register(tag: "mark", "highlight", renderer: HighlightTag.self)
.register(tag: "code", "pre", renderer: CodeBlockTag.self)
.register(tag: "video", renderer: VideoTagView.self)
.attributeStyler(MyCustomAttributeStyler())
}
}- 커스텀 태그는 기본 태그를 덮어쓸 수 있습니다
- BlockTag는 View 프로토콜을 구현해야 합니다
- InlineTag는 스타일만 적용하고 렌더링은 시스템이 처리합니다
- InlineAttachmentTag는 인라인으로 삽입되는 뷰를 생성합니다
- Custom tags can override default tags
- BlockTag must implement the View protocol
- InlineTag only applies styles; rendering is handled by the system
- InlineAttachmentTag creates views that are inserted inline