Panduan Lengkap Rich Text Editing di SwiftUI iOS 26: Bikin Editor Teks Kaya Tanpa UIKit

Pelajari cara membangun rich text editor native di SwiftUI iOS 26 menggunakan AttributedString, AttributedTextSelection, dan transformAttributes. Panduan lengkap dengan contoh kode siap pakai.

Pendahuluan: Akhirnya, Rich Text Editing yang Waras di SwiftUI

Kalau kamu pernah mencoba bikin editor teks kaya (rich text editor) di SwiftUI sebelum iOS 26, saya yakin kamu pernah merasakan sakitnya. Bukan sakit hati ditinggal gebetan — ini lebih parah. Kamu harus bikin UIViewRepresentable wrapper untuk UITextView, handle semua delegate-nya, sinkronisasi state antara UIKit dan SwiftUI, dan berdoa semoga nggak ada bug aneh yang muncul pas user rotate device-nya.

Saya sendiri pernah menghabiskan hampir dua hari penuh hanya untuk membuat bold button yang berfungsi dengan benar di wrapper UITextView. Dua hari. Untuk satu tombol.

Jujur, itu bikin frustrasi level tinggi.

Tapi sekarang, dengan hadirnya iOS 26, Apple akhirnya memberikan kita apa yang sudah lama kita minta: dukungan native rich text editing langsung di TextEditor SwiftUI menggunakan AttributedString. Nggak perlu lagi bridge ke UIKit. Nggak perlu lagi wrapper yang bikin pusing. Cukup ganti tipe binding dari String ke AttributedString, dan boom — kamu sudah punya rich text editor.

Di artikel ini, kita akan bahas tuntas semua yang perlu kamu tahu tentang Rich Text Editing di SwiftUI iOS 26, mulai dari konsep dasar AttributedString, cara bikin toolbar formatting, sinkronisasi state toolbar, sampai menyimpan dan memuat dokumen. Siap? Ayo mulai.

Persyaratan Sebelum Mulai

Sebelum kamu mulai coding, pastikan environment kamu sudah siap:

  • Xcode 26 atau lebih baru
  • iOS 26 SDK
  • Deployment target minimal iOS 26.0
  • Device atau simulator yang menjalankan iOS 26

Ya, ini memang fitur yang hanya tersedia di iOS 26 ke atas. Nggak ada backward compatibility ke iOS 18 atau sebelumnya. Tapi jujur, kalau kamu sedang memulai proyek baru atau punya luxury untuk set minimum deployment target ke iOS 26, fitur ini sangat worth it.

Konsep Dasar: Mengenal AttributedString

Sebelum kita masuk ke TextEditor, penting untuk memahami dulu fondasi dari semuanya: AttributedString.

Apa Itu AttributedString?

AttributedString adalah tipe value (value type) yang diperkenalkan di Swift 5.5 sebagai pengganti modern untuk NSAttributedString dari Objective-C. Karena ini value type (struct, bukan class), dia bermain dengan baik di ekosistem SwiftUI yang memang sangat mengandalkan value semantics.

Yang bikin AttributedString powerful adalah kemampuannya menyimpan metadata formatting pada setiap karakter atau range teks. Kamu bisa set font, warna, underline, strikethrough, link, baseline offset, kerning — dan banyak lagi. Semuanya per-range, jadi fleksibilitasnya tinggi banget.

Membuat AttributedString Secara Programatis

Nah, ini contoh sederhana bagaimana kamu bisa membangun AttributedString dengan berbagai format:

var greeting = AttributedString("Halo, ")
var name = AttributedString("Dunia")
name.font = .body.bold()
name.foregroundColor = .blue
greeting.append(name)
greeting += AttributedString("!")

Hasilnya? Teks "Halo, " dengan format default, diikuti "Dunia" yang tebal dan berwarna biru, lalu "!" di akhir. Simpel dan intuitif, kan?

Dukungan Markdown Bawaan

Salah satu fitur favorit saya dari AttributedString adalah dukungan Markdown bawaan. Kamu bisa langsung parse string Markdown menjadi AttributedString yang sudah ter-format:

let richText = try AttributedString(
    markdown: "**Teks tebal** dan *teks miring* dengan [link](https://apple.com)"
)

Ini sangat berguna kalau kamu menerima konten dari API atau database yang formatnya Markdown. Nggak perlu parser tambahan — semua sudah built-in. Honestly, ini salah satu hal kecil yang bikin hidup developer jauh lebih mudah.

Atribut yang Tersedia

Berikut beberapa atribut yang paling sering dipakai:

  • .font — mengatur jenis, ukuran, dan trait font (bold, italic)
  • .foregroundColor — warna teks
  • .backgroundColor — warna latar belakang teks
  • .underlineStyle — garis bawah (single, double, thick, dll.)
  • .strikethroughStyle — garis coret
  • .link — menyematkan URL pada teks
  • .baselineOffset — menggeser teks ke atas atau bawah dari baseline
  • .kern — mengatur jarak antar karakter

TextEditor dengan AttributedString: Ajaibnya Satu Perubahan Kecil

Ini bagian yang bikin saya kagum. Untuk mengaktifkan rich text editing di SwiftUI iOS 26, yang perlu kamu lakukan hanyalah mengganti tipe state dari String menjadi AttributedString.

Serius, itu saja.

struct BasicRichTextEditor: View {
    @State private var text = AttributedString("Mulai mengetik di sini...")

    var body: some View {
        TextEditor(text: $text)
            .padding()
    }
}

Dengan perubahan sekecil ini, TextEditor kamu sekarang sudah mendukung rich text. Dan yang lebih keren lagi, keyboard shortcuts bawaan langsung bekerja:

  • Cmd+B untuk Bold
  • Cmd+I untuk Italic
  • Cmd+U untuk Underline

Tanpa satu baris kode tambahan pun. Saya ingat pertama kali mencoba ini dan langsung bilang "kok bisa sesimpel ini?" ke layar monitor saya. Untung nggak ada orang di sekitar.

Memahami AttributedTextSelection

Untuk membangun toolbar formatting yang proper, kamu perlu tahu posisi kursor dan teks yang sedang dipilih user. Di sinilah AttributedTextSelection berperan.

AttributedTextSelection adalah tipe baru di SwiftUI iOS 26 yang melacak posisi kursor (insertion point) dan range teks yang sedang diseleksi. Cara pakainya gampang — tinggal bind ke TextEditor menggunakan parameter selection::

@State private var text = AttributedString("Ketik sesuatu...")
@State private var selection = AttributedTextSelection()

var body: some View {
    TextEditor(text: $text, selection: $selection)
}

Dengan binding ini, setiap kali user mengubah posisi kursor atau menyeleksi teks, state selection akan otomatis ter-update. Dan sebaliknya, kalau kamu mengubah selection secara programatis, kursor di TextEditor juga akan berpindah. Two-way binding yang bekerja persis seperti yang kamu harapkan.

API Kunci: transformAttributes(in:&selection)

Oke, ini mungkin API yang paling penting untuk rich text editing di SwiftUI iOS 26. Method transformAttributes(in:&selection) pada AttributedString memungkinkan kamu mengubah atribut teks pada range yang sedang dipilih.

Yang membuat API ini istimewa adalah parameter selection yang menggunakan inout. Kenapa? Karena ketika kamu mengubah atribut teks (misalnya dari regular ke bold), panjang glyph bisa berubah. Dengan inout, SwiftUI bisa otomatis menyesuaikan posisi selection setelah transformasi. Kamu nggak perlu menghitung ulang index-nya secara manual.

Brilliant, kalau boleh saya bilang.

text.transformAttributes(in: &selection) { container in
    // container adalah inout AttributeContainer
    // Ubah atribut sesuka hati di sini
    container.font = .body.bold()
}

Closure-nya menerima inout AttributeContainer, jadi kamu bisa membaca atribut yang ada sekarang dan mengubahnya sesuai kebutuhan.

FontResolutionContext: Jangan Lewatkan yang Satu Ini

Ada satu environment value baru yang sangat krusial tapi mungkin mudah terlewat: fontResolutionContext.

@Environment(\.fontResolutionContext) var fontResolutionContext

Kenapa ini penting? Karena di SwiftUI, font itu abstrak. Ketika kamu menulis .body, ukuran sebenarnya tergantung pada Dynamic Type setting user, accessibility preferences, dan bahkan custom font configuration. FontResolutionContext berisi semua informasi yang dibutuhkan untuk resolve font abstrak menjadi font konkret dengan trait yang benar.

Tanpa context ini, kamu nggak bisa secara akurat menentukan apakah font saat ini bold, italic, atau keduanya. Dan itu penting banget kalau kamu mau bikin tombol toggle yang highlight ketika format aktif.

Catatan penting: Selalu gunakan fontResolutionContext saat kamu perlu memeriksa trait font. Jangan coba-coba hardcode ukuran atau trait — itu resep bencana di dunia Dynamic Type dan accessibility.

Membangun Toolbar Formatting: Bold, Italic, Underline

Sekarang kita gabungkan semuanya. Mari bikin toolbar formatting yang proper dengan tombol Bold, Italic, dan Underline.

So, ini contoh lengkapnya:

struct RichTextEditorView: View {
    @Environment(\.fontResolutionContext) var fontResolutionContext
    @State private var text = AttributedString("Ketik sesuatu...")
    @State private var selection = AttributedTextSelection()

    var body: some View {
        VStack(spacing: 0) {
            formattingToolbar
            TextEditor(text: $text, selection: $selection)
                .padding()
        }
    }

    private var formattingToolbar: some View {
        HStack(spacing: 12) {
            Button("Bold", systemImage: "bold") {
                text.transformAttributes(in: &selection) { container in
                    let currentFont = container.font ?? .default
                    let resolved = currentFont.resolve(in: fontResolutionContext)
                    container.font = currentFont.bold(!resolved.isBold)
                }
            }

            Button("Italic", systemImage: "italic") {
                text.transformAttributes(in: &selection) { container in
                    let currentFont = container.font ?? .default
                    let resolved = currentFont.resolve(in: fontResolutionContext)
                    container.font = currentFont.italic(!resolved.isItalic)
                }
            }

            Button("Underline", systemImage: "underline") {
                text.transformAttributes(in: &selection) { container in
                    if container.underlineStyle == .single {
                        container.underlineStyle = nil
                    } else {
                        container.underlineStyle = .single
                    }
                }
            }
        }
        .padding()
        .background(.ultraThinMaterial)
    }
}

Perhatikan pattern-nya: untuk Bold dan Italic, kita perlu resolve font dulu menggunakan fontResolutionContext untuk mengetahui state saat ini, baru kemudian toggle-nya. Untuk Underline, lebih simpel karena kita cukup cek value underlineStyle langsung.

Mengatur Warna Teks dan Background

Rich text editor tanpa warna itu seperti nasi goreng tanpa kecap — secara teknis masih valid, tapi kurang seru. Untungnya, mengubah warna teks dan background sangat mudah:

func setTextColor(_ color: Color) {
    text.transformAttributes(in: &selection) { container in
        container.foregroundColor = color
    }
}

func setBackgroundColor(_ color: Color) {
    text.transformAttributes(in: &selection) { container in
        container.backgroundColor = color
    }
}

Kamu bisa kombinasikan ini dengan ColorPicker bawaan SwiftUI untuk memberikan user pilihan warna yang lengkap. Kita akan lihat implementasi lengkapnya di contoh proyek di bawah nanti.

Menyematkan Link pada Teks

Mau bikin teks yang bisa diklik dan membuka URL? Gampang banget:

text.transformAttributes(in: &selection) { container in
    container.link = URL(string: "https://swiftcrafted.com")
}

Setelah atribut link ditambahkan, teks tersebut akan otomatis tampil sebagai hyperlink (biasanya berwarna biru dan underline) dan bisa di-tap oleh user untuk membuka URL. Fitur ini sangat berguna untuk aplikasi note-taking atau CMS sederhana.

Sinkronisasi Toolbar dengan typingAttributes

Ini bagian yang sering dilupakan developer — dan saya pun hampir melewatkannya pertama kali: toolbar sync. Maksudnya, ketika user memindahkan kursor ke teks yang sudah di-bold, tombol Bold di toolbar seharusnya tampil dalam keadaan aktif (highlighted). Dan sebaliknya.

Untuk mencapai ini, kamu perlu membaca typing attributes — yaitu atribut yang akan diterapkan pada karakter berikutnya yang diketik di posisi kursor saat ini. Method typingAttributes(in:) pada AttributedTextSelection memberikan informasi ini:

Toggle(
    "Bold",
    systemImage: "bold",
    isOn: Binding(
        get: {
            let font = selection.typingAttributes(in: text).font
            let resolved = (font ?? .default).resolve(in: fontResolutionContext)
            return resolved.isBold
        },
        set: { isBold in
            text.transformAttributes(in: &selection) {
                $0.font = ($0.font ?? .default).bold(isBold)
            }
        }
    )
)

Dengan pattern Toggle + custom Binding ini, tombol toolbar akan otomatis sinkron dengan format teks di posisi kursor. Saat user klik di area bold, tombol Bold akan aktif. Saat pindah ke area normal, tombol Bold akan non-aktif. Smooth.

Saya akui, pertama kali lihat pattern ini agak overwhelming. Tapi setelah kamu paham logikanya, ini sebenarnya sangat elegan — jauh lebih bersih daripada cara lama di UIKit yang harus listen ke NSTextStorage delegate dan manually update UI.

Dukungan Markdown di AttributedString

Seperti yang sudah saya singgung sebelumnya, AttributedString punya dukungan Markdown bawaan yang sangat berguna. Ini bukan cuma parsing sederhana — dia mendukung cukup banyak syntax:

  • Bold (**text**)
  • Italic (*text*)
  • Link ([text](url))
  • Inline code (`code`)
  • Strikethrough (~~text~~)
do {
    let markdown = """
    **Judul Tebal** dan *penekanan miring*.
    Kunjungi [Swift Crafted](https://swiftcrafted.com) untuk tutorial lainnya.
    Gunakan `AttributedString` untuk rich text.
    """
    let attributed = try AttributedString(markdown: markdown)
    text = attributed
} catch {
    print("Gagal parse markdown: \(error)")
}

Ini sangat powerful kalau aplikasi kamu menerima konten Markdown dari server atau user input. Tinggal convert ke AttributedString dan tampilkan di TextEditor — formatting-nya langsung proper tanpa effort tambahan.

Menyimpan dan Memuat AttributedString

Setelah user selesai mengedit, tentu kamu perlu menyimpan hasilnya. Kabar baiknya, AttributedString conform ke Codable, jadi kamu bisa encode dan decode dengan JSONEncoder/JSONDecoder seperti biasa:

// Menyimpan
func saveDocument() {
    if let data = try? JSONEncoder().encode(text) {
        UserDefaults.standard.set(data, forKey: "richTextDocument")
    }
}

// Memuat
func loadDocument() {
    if let data = UserDefaults.standard.data(forKey: "richTextDocument"),
       let decoded = try? JSONDecoder().decode(AttributedString.self, from: data) {
        text = decoded
    }
}

Untuk proyek yang lebih serius, kamu mungkin ingin menyimpan ke file system atau database. Pendekatan JSONEncoder tetap sama — yang berbeda hanya destinasi penyimpanan data-nya.

Catatan tentang SwiftData: Saat artikel ini ditulis, menyimpan AttributedString langsung sebagai property di SwiftData model belum se-straightforward menyimpan String biasa. Pendekatan yang direkomendasikan adalah menyimpan data JSON hasil encode sebagai Data di model kamu, lalu decode saat akan ditampilkan. Agak manual memang, tapi reliable.

Contoh Proyek Lengkap: Mini Rich Text Editor

Oke, sekarang saatnya menggabungkan semuanya menjadi satu proyek mini yang bisa langsung kamu pakai sebagai fondasi. Editor ini punya toolbar dengan Bold, Italic, Underline, Strikethrough, dan Color Picker:

struct MiniRichTextEditor: View {
    @Environment(\.fontResolutionContext) var fontResolutionContext
    @State private var text = AttributedString("")
    @State private var selection = AttributedTextSelection()
    @State private var selectedColor: Color = .primary

    var isBold: Bool {
        let font = selection.typingAttributes(in: text).font
        return (font ?? .default).resolve(in: fontResolutionContext).isBold
    }

    var isItalic: Bool {
        let font = selection.typingAttributes(in: text).font
        return (font ?? .default).resolve(in: fontResolutionContext).isItalic
    }

    var body: some View {
        NavigationStack {
            VStack(spacing: 0) {
                toolbar
                Divider()
                TextEditor(text: $text, selection: $selection)
                    .padding()
            }
            .navigationTitle("Editor")
            .navigationBarTitleDisplayMode(.inline)
        }
    }

    private var toolbar: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 16) {
                Toggle("Bold", systemImage: "bold", isOn: Binding(
                    get: { isBold },
                    set: { newValue in
                        text.transformAttributes(in: &selection) {
                            $0.font = ($0.font ?? .default).bold(newValue)
                        }
                    }
                ))
                .toggleStyle(.button)

                Toggle("Italic", systemImage: "italic", isOn: Binding(
                    get: { isItalic },
                    set: { newValue in
                        text.transformAttributes(in: &selection) {
                            $0.font = ($0.font ?? .default).italic(newValue)
                        }
                    }
                ))
                .toggleStyle(.button)

                Button("Underline", systemImage: "underline") {
                    text.transformAttributes(in: &selection) { container in
                        container.underlineStyle = container.underlineStyle == .single ? nil : .single
                    }
                }

                Button("Strikethrough", systemImage: "strikethrough") {
                    text.transformAttributes(in: &selection) { container in
                        container.strikethroughStyle = container.strikethroughStyle == .single ? nil : .single
                    }
                }

                Divider().frame(height: 20)

                ColorPicker("Warna", selection: $selectedColor)
                    .onChange(of: selectedColor) {
                        text.transformAttributes(in: &selection) { container in
                            container.foregroundColor = selectedColor
                        }
                    }
            }
            .padding(.horizontal)
            .padding(.vertical, 8)
        }
        .background(.ultraThinMaterial)
    }
}

Proyek ini sudah cukup fungsional untuk digunakan sebagai starting point. Kamu bisa kembangkan lebih lanjut dengan menambahkan fitur seperti font size picker, text alignment, undo/redo, dan export ke PDF atau HTML.

Saran saya: copy-paste kode di atas ke proyek Xcode 26, jalankan di simulator, dan mulai eksperimen. Coba seleksi teks, tekan tombol Bold, pindahkan kursor — rasakan sendiri betapa mulusnya pengalaman ini dibanding cara lama.

Best Practices untuk Rich Text Editing di SwiftUI

Setelah bermain cukup lama dengan fitur ini (dan beberapa kali trial-and-error), berikut beberapa best practices yang saya rekomendasikan:

1. Selalu Gunakan transformAttributes untuk Modifikasi

Jangan coba memodifikasi AttributedString secara manual dengan index manipulation. Method transformAttributes(in:&selection) sudah di-design untuk handle edge cases seperti empty selection, multi-range selection, dan perubahan panjang teks. Biarkan SwiftUI yang mengurus kompleksitasnya.

2. Jangan Lupakan fontResolutionContext

Setiap kali kamu perlu memeriksa atau toggle trait font (bold, italic), selalu resolve font menggunakan fontResolutionContext. Ini memastikan kode kamu bekerja dengan benar di semua konfigurasi Dynamic Type dan accessibility. Percaya deh, ini bukan langkah yang boleh di-skip.

3. Gunakan typingAttributes untuk Toolbar Sync

Toolbar yang tidak sinkron dengan format teks saat ini memberikan pengalaman user yang buruk. Gunakan typingAttributes(in:) untuk membaca format di posisi kursor dan update toolbar accordingly. Pattern Toggle dengan custom Binding yang sudah kita bahas di atas adalah cara yang paling clean untuk ini.

4. Handle Dynamic Type

Pastikan editor kamu bekerja dengan baik di semua ukuran Dynamic Type. Jangan hardcode ukuran font — gunakan semantic font styles seperti .body, .title, .caption, dan biarkan sistem yang menentukan ukuran sebenarnya.

5. Pertimbangkan Performance untuk Dokumen Besar

AttributedString adalah value type, jadi setiap modifikasi menciptakan copy baru. Untuk kebanyakan use case ini nggak masalah, tapi kalau kamu bekerja dengan dokumen ratusan halaman, pertimbangkan strategi optimasi seperti lazy loading atau pagination. Ini edge case memang, tapi worth diingat.

6. Simpan Secara Berkala

Implementasikan auto-save. Nggak ada yang lebih menyebalkan daripada kehilangan tulisan panjang karena lupa save. Kamu bisa gunakan onChange(of: text) dengan debounce untuk auto-save setiap beberapa detik setelah perubahan terakhir.

Migrasi dari UIKit: Tips untuk yang Sudah Punya Codebase

Kalau kamu sudah punya rich text editor berbasis UIKit (UITextView dengan NSAttributedString), migrasi ke SwiftUI native nggak harus dilakukan sekaligus. Berikut strategi yang saya sarankan:

  • Konversi data: AttributedString bisa diinisialisasi dari NSAttributedString dan sebaliknya, jadi data yang sudah ada tetap bisa dipakai.
  • Migrasi bertahap: Mulai dengan layar atau fitur baru yang pure SwiftUI, lalu perlahan migrasi layar yang sudah ada.
  • Testing menyeluruh: Pastikan formatting yang dibuat di editor lama tampil dengan benar di editor baru, dan sebaliknya. Ini bagian yang sering bikin pusing kalau di-skip.

Konversi antara keduanya cukup straightforward:

// NSAttributedString ke AttributedString
let nsAttr = NSAttributedString(string: "Hello", attributes: [.font: UIFont.boldSystemFont(ofSize: 16)])
let swiftAttr = AttributedString(nsAttr)

// AttributedString ke NSAttributedString
let nsConverted = NSAttributedString(swiftAttr)

Pertanyaan yang Sering Diajukan (FAQ)

Apakah TextEditor di iOS 26 mendukung gambar atau attachment?

Sampai saat artikel ini ditulis, TextEditor dengan AttributedString di iOS 26 fokus pada text formatting — bold, italic, underline, warna, link, dan sejenisnya. Dukungan untuk inline image atau file attachment belum tersedia secara native di TextEditor. Kalau kamu butuh fitur tersebut, kamu masih perlu menggunakan UITextView via UIViewRepresentable atau library pihak ketiga. Semoga Apple menambahkan fitur ini di update berikutnya (fingers crossed).

Bagaimana cara menyimpan AttributedString ke SwiftData?

Cara yang paling reliable saat ini adalah menyimpan AttributedString sebagai Data (hasil encode JSON) di model SwiftData kamu. Buat computed property untuk encode dan decode:

@Model
class Document {
    var title: String
    var contentData: Data

    var content: AttributedString {
        get {
            (try? JSONDecoder().decode(AttributedString.self, from: contentData)) ?? AttributedString("")
        }
        set {
            contentData = (try? JSONEncoder().encode(newValue)) ?? Data()
        }
    }
}

Pendekatan ini memastikan data kamu tetap aman dan bisa di-query oleh SwiftData tanpa masalah.

Apakah rich text editing backward compatible ke iOS 18?

Sayangnya, tidak. Fitur TextEditor dengan binding AttributedString, AttributedTextSelection, transformAttributes(in:&selection), dan fontResolutionContext semuanya membutuhkan iOS 26. Kalau kamu harus support iOS versi lebih lama, kamu tetap perlu menggunakan pendekatan UIViewRepresentable dengan UITextView. Kamu bisa pakai #available(iOS 26, *) check untuk menyediakan implementasi yang berbeda berdasarkan versi OS.

Apa perbedaan AttributedString dan NSAttributedString?

Perbedaan utamanya ada di tiga hal. Pertama, AttributedString adalah value type (struct) sedangkan NSAttributedString adalah reference type (class) — ini membuat AttributedString lebih cocok untuk SwiftUI yang mengandalkan value semantics. Kedua, AttributedString support type-safe attributes, jadi kamu nggak perlu pakai string keys seperti NSAttributedString.Key.font. Cukup tulis .font dan compiler akan memastikan tipe value-nya benar. Ketiga, AttributedString conform ke Codable, sehingga mudah diserialisasi ke JSON — sedangkan NSAttributedString butuh NSKeyedArchiver yang lebih ribet dan kurang portable.

Bagaimana cara menambahkan custom attribute ke AttributedString?

Kamu bisa mendefinisikan custom attribute dengan membuat struct yang conform ke AttributedStringKey, lalu extend AttributeScopes. Contohnya:

struct HighlightAttribute: AttributedStringKey {
    typealias Value = Bool
    static let name = "highlight"
}

extension AttributeScopes {
    struct CustomAttributes: AttributeScope {
        let highlight: HighlightAttribute
    }

    var custom: CustomAttributes.Type { CustomAttributes.self }
}

// Penggunaan
var text = AttributedString("Ini teks penting")
text.custom.highlight = true

Custom attributes sangat berguna kalau kamu butuh menyimpan metadata tambahan yang spesifik untuk aplikasi kamu — misalnya informasi highlighting, tagging, atau annotation yang nggak tersedia di atribut standar.

Penutup

Rich Text Editing di SwiftUI iOS 26 adalah salah satu peningkatan yang paling saya tunggu-tunggu. Setelah bertahun-tahun berurusan dengan UIViewRepresentable wrapper yang fragile dan susah di-maintain, akhirnya kita punya solusi native yang elegan, powerful, dan feels right di ekosistem SwiftUI.

Rangkuman singkatnya: ganti String dengan AttributedString untuk mengaktifkan rich text, gunakan AttributedTextSelection untuk tracking seleksi, pakai transformAttributes(in:&selection) untuk modifikasi format, dan jangan lupakan fontResolutionContext untuk resolve font traits yang akurat.

Dengan empat komponen ini, kamu bisa membangun rich text editor yang fully functional tanpa menyentuh UIKit sama sekali.

Sekarang giliran kamu untuk bereksperimen. Buka Xcode 26, buat proyek baru, dan mulai build editor impian kamu. Dan kalau kamu bikin sesuatu yang keren, jangan lupa share — saya selalu senang lihat apa yang dibuat komunitas developer Indonesia dengan teknologi baru ini. Selamat ngoding!

Tentang Penulis Editorial Team

Our team of expert writers and editors.