Swift Charts iOS 26: Tạo Biểu Đồ Tương Tác trong SwiftUI (2026)
Hướng dẫn đầy đủ Swift Charts trong iOS 26 và SwiftUI: từ BarMark, LineMark, AreaMark đến Chart3D, scrolling axes, chartGesture và VectorizedChartContent với code Xcode 26.
Swift Charts là framework gốc của Apple cho phép bạn tạo biểu đồ tương tác trong SwiftUI chỉ với vài dòng code khai báo, hỗ trợ đầy đủ từ biểu đồ cột, đường, vùng, đến biểu đồ 3D mới trong iOS 26. Trong bài viết này, tôi sẽ hướng dẫn bạn xây dựng Swift Charts iOS 26 từ những Mark cơ bản nhất đến các tính năng nâng cao như chartXSelection, scrolling axes, gesture mới và tích hợp accessibility cho AXChartDescriptor, kèm code ví dụ chạy được trực tiếp trong Xcode 26.
Swift Charts trong iOS 26 bổ sung VectorizedChartContent giúp render hàng triệu data point mà vẫn giữ 60fps trên iPhone 17.
Các Mark cốt lõi gồm BarMark, LineMark, PointMark, AreaMark, RectangleMark, RuleMark và SectorMark cho biểu đồ tròn/donut.
chartXSelection và chartGesture (iOS 26) cho phép tương tác phong phú: hover, drag, pinch-to-zoom trên cả iPhone và iPad.
Customization qua chartXAxis, chartYAxis, chartLegend, chartForegroundStyleScale hoạt động giống modifier SwiftUI thông thường.
Animation tự động khi data thay đổi nhờ animation(_:value:) bên ngoài Chart, không cần code interpolation.
Accessibility yêu cầu áp dụng accessibilityLabel trên từng Mark hoặc cung cấp AXChartDescriptor cho VoiceOver.
Swift Charts là gì và tại sao nên dùng trong iOS 26?
Swift Charts là framework data visualization do Apple giới thiệu tại WWDC22 và đã trải qua bốn năm tiến hóa để trở thành lựa chọn mặc định cho việc hiển thị dữ liệu trong app iOS, iPadOS, macOS, watchOS và visionOS. Trong iOS 26, framework này nhận được những nâng cấp đáng kể: VectorizedChartContent để render dataset cực lớn, hỗ trợ chart 3D có thể xoay bằng gesture, API chartGesture đơn giản hóa logic tương tác, và Mark mới FunctionMark để vẽ hàm toán học trực tiếp mà không cần sample dữ liệu thủ công.
Thật lòng mà nói, lý do lớn nhất tôi chọn Swift Charts thay vì thư viện bên ngoài như Charts (Daniel Cohen Gindi) hay DGCharts là vì cách tiếp cận declarative giống hệt SwiftUI: bạn chỉ cần mô tả data và Mark, framework tự xử lý layout, axis, legend và animation. Không cần delegate, không cần ghi đè drawRect, không cần lo tương thích với UIKit vs SwiftUI. Theo tài liệu chính thức Swift Charts của Apple, framework này đã được redesign hoàn toàn cho Liquid Glass design language trong iOS 26, tự động hỗ trợ dark mode, Dynamic Type và Reduce Motion.
Nếu bạn từng đọc bài Liquid Glass trong SwiftUI, bạn sẽ thấy Chart trong iOS 26 tự động kế thừa hiệu ứng glass material khi đặt trong NavigationStack hoặc TabView mà không cần config thêm.
Cài đặt biểu đồ Swift Charts đầu tiên
Để bắt đầu, bạn cần Xcode 26 trở lên và deployment target tối thiểu iOS 16 (mặc dù tính năng 3D và chartGesture yêu cầu iOS 26). Import framework như sau.
import SwiftUI
import Charts
struct SalesData: Identifiable {
let id = UUID()
let month: String
let revenue: Double
}
struct SimpleBarChart: View {
let data: [SalesData] = [
.init(month: "T1", revenue: 12_500),
.init(month: "T2", revenue: 18_300),
.init(month: "T3", revenue: 15_800),
.init(month: "T4", revenue: 22_100),
.init(month: "T5", revenue: 28_900),
.init(month: "T6", revenue: 31_400)
]
var body: some View {
Chart(data) { item in
BarMark(
x: .value("Tháng", item.month),
y: .value("Doanh thu", item.revenue)
)
.foregroundStyle(Color.accentColor.gradient)
.cornerRadius(6)
}
.frame(height: 280)
.padding()
}
}
Đoạn code trên tạo một biểu đồ cột đầy đủ với labels trục, padding, animation entry, và tự động chọn bước nhảy trục Y dựa trên data range. Khái niệm cốt lõi cần nắm: Chart là container, bên trong là một hoặc nhiều Mark. Mỗi Mark nhận giá trị qua PlottableValue với label cho từng trục. Label này sẽ xuất hiện trong VoiceOver và tooltip khi tương tác.
Các loại Mark trong Swift Charts: BarMark, LineMark, AreaMark, PointMark
Swift Charts cung cấp một bộ Mark phong phú, mỗi loại phục vụ một mục đích cụ thể. Hiểu sự khác biệt giữa chúng giúp bạn chọn đúng loại biểu đồ cho mỗi tình huống.
BarMark, biểu đồ cột và stacked bar
BarMark phù hợp khi so sánh giá trị giữa các category rời rạc. Để tạo stacked bar, thêm tham số stacking: hoặc dùng foregroundStyle(by:) với một dimension thứ ba.
.interpolationMethod(.catmullRom) tạo đường cong mượt giữa các điểm, trong khi .stepStart hoặc .stepEnd tạo dạng bậc thang. Cách này hữu ích khi giá trị thay đổi đột ngột (ví dụ on/off state).
AreaMark, RectangleMark và RuleMark
AreaMark tô vùng dưới đường, phù hợp khi nhấn mạnh tổng cộng dồn. RectangleMark dùng cho heatmap với hai dimension category. RuleMark vẽ đường ngang/dọc để đánh dấu ngưỡng (ví dụ KPI target hoặc median).
Mặc định Swift Charts chọn tick marks thông minh, nhưng cho UI thương mại bạn thường cần kiểm soát chi tiết. Ba modifier quan trọng nhất là chartXAxis, chartYAxis, và chartLegend.
Chart(salesData) { item in
BarMark(
x: .value("Tháng", item.date, unit: .month),
y: .value("Doanh thu", item.revenue)
)
}
.chartXAxis {
AxisMarks(values: .stride(by: .month)) { value in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.month(.abbreviated))
}
}
.chartYAxis {
AxisMarks(position: .leading) { value in
AxisGridLine()
if let revenue = value.as(Double.self) {
AxisValueLabel {
Text(revenue, format: .currency(code: "VND").precision(.fractionLength(0)))
}
}
}
}
.chartYScale(domain: 0...50_000_000)
.chartLegend(position: .top, alignment: .leading, spacing: 12)
Trong ví dụ trên, tôi format trục Y theo VND, đặt legend lên trên, và pin domain để biểu đồ không tự co giãn khi data thay đổi. chartXScale và chartYScale cũng cho phép áp dụng scale phi tuyến (.log, .squareRoot) nếu cần thể hiện dữ liệu có phân phối lệch.
Làm thế nào để thêm tương tác cho Chart trong iOS 26?
Đây là câu hỏi tôi nhận được nhiều nhất sau khi WWDC25 kết thúc. Câu trả lời ngắn gọn: trong iOS 26, dùng modifier chartXSelection(value:) để binding giá trị được chọn, và chartGesture mới cho các gesture phức tạp hơn như drag-to-select range hay pinch-to-zoom. Trước đây bạn phải tự xử lý DragGesture + ChartProxy, giờ thì Apple đã đóng gói tất cả vào API declarative.
Lưu ý cách tôi dùng RuleMark kèm .annotation(position: .top) để hiển thị tooltip. Pattern này được Apple đề xuất chính thức trong WWDC25 session về Swift Charts. Annotation tự động tránh tràn khỏi chart frame và xử lý dark mode.
Scrollable Axes và pinch-to-zoom
Khi data có hàng nghìn điểm, ví dụ heart rate trong 24 giờ hay candlestick chứng khoán, bạn không thể nhồi nhét tất cả vào một viewport. Swift Charts iOS 26 cung cấp chartScrollableAxes và chartXVisibleDomain để hiển thị một window và cho phép scroll/zoom.
Với iOS 26, bạn còn có thể bật pinch-to-zoom bằng cách thêm chartGesture { proxy in ... } kết hợp MagnifyGesture.
.chartGesture { proxy in
MagnifyGesture()
.onChanged { value in
let currentLength = proxy.xVisibleDomain ?? 3600
let newLength = max(60, currentLength / value.magnification)
proxy.xVisibleDomain = newLength
}
}
Đây là một trong những cải tiến lớn nhất của iOS 26. Trước đây bạn phải capture GeometryReader và tự tính scale factor, code thường dài 50–80 dòng. Tôi đã từng phải viết tay đoạn xử lý zoom này trong một app y tế năm ngoái, và việc Apple chính thức hoá API thật sự nhẹ gánh.
Biểu đồ 3D mới trong iOS 26
iOS 26 và visionOS 26 giới thiệu Chart3D, container mới cho phép render scatter plot, surface plot và bar chart trong không gian ba chiều. API tương tự Chart 2D nhưng thêm trục Z và rotation gesture mặc định.
import Charts
struct ProductPerformance: Identifiable {
let id = UUID()
let price: Double
let rating: Double
let sales: Double
let category: String
}
struct Scatter3DChart: View {
let products: [ProductPerformance]
var body: some View {
Chart3D(products) { product in
PointMark(
x: .value("Giá", product.price),
y: .value("Đánh giá", product.rating),
z: .value("Doanh số", product.sales)
)
.foregroundStyle(by: .value("Danh mục", product.category))
.symbolSize(100)
}
.chart3DCameraProjection(.perspective)
.frame(height: 400)
}
}
Mặc định Chart3D hỗ trợ rotation bằng one-finger drag và zoom bằng pinch, không cần config thêm. Trên visionOS, bạn có thể tương tác bằng hand tracking. Chi tiết kỹ thuật có trong tài liệu Chart3D của Apple.
Animation và cập nhật dữ liệu real-time
Một trong những điểm mạnh nhất của Swift Charts là animation tự động khi data binding thay đổi. Bạn chỉ cần đặt .animation(_:value:) bên ngoài Chart và framework sẽ interpolate giữa giá trị cũ và mới.
struct LiveStockChart: View {
@State private var prices: [PricePoint] = []
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Chart(prices) { point in
LineMark(
x: .value("Thời gian", point.timestamp),
y: .value("Giá", point.value)
)
.interpolationMethod(.monotone)
}
.animation(.smooth(duration: 0.4), value: prices)
.frame(height: 240)
.onReceive(timer) { _ in
// Append giá mới và giữ window 60 điểm
let newPrice = PricePoint(
timestamp: Date(),
value: Double.random(in: 95...105)
)
prices.append(newPrice)
if prices.count > 60 { prices.removeFirst() }
}
}
}
Trong production, tôi khuyên dùng .smooth hoặc .snappy thay vì .spring cho biểu đồ real-time. Spring có thể gây overshoot khi value thay đổi nhanh, làm người dùng khó đọc. Nếu bạn cần performance cao hơn cho streaming data, hãy dùng VectorizedChartContent mà chúng ta sẽ bàn ở phần sau.
Tích hợp Accessibility và VoiceOver
Đây là phần thường bị bỏ quên nhưng cực kỳ quan trọng. Chart không có accessibility là chart loại trừ người dùng VoiceOver. Swift Charts cung cấp hai cấp độ accessibility: tự động trên mỗi Mark, và override toàn cục qua AXChartDescriptor.
Khi conform AXChartDescriptorRepresentable, bạn cung cấp descriptor đầy đủ với title, summary, series và axes. VoiceOver sẽ đọc chart như một bảng số liệu có cấu trúc và cho phép user di chuyển điểm-theo-điểm bằng rotor. Đây là yêu cầu bắt buộc nếu app của bạn nhắm đến khách hàng doanh nghiệp hoặc cơ quan nhà nước theo chuẩn WCAG 2.2.
Để hiểu sâu hơn về Observable pattern dùng để feed data cho chart, bạn có thể tham khảo UIKit Observable trong iOS 26, bài viết giải thích cách @Observable tích hợp với view tree.
Tối ưu hiệu suất với VectorizedChartContent
Nếu bạn render 10.000 điểm trở lên, ChartBuilder DSL truyền thống bắt đầu chậm vì mỗi Mark là một SwiftUI view riêng. iOS 26 giới thiệu VectorizedChartContent, một path để pass thẳng buffer dữ liệu cho Metal renderer mà không qua view tree.
Benchmark nội bộ của Apple (chia sẻ tại WWDC25) cho thấy VectorizedLineMark render 1 triệu điểm ở 60fps trên iPhone 17 Pro, trong khi LineMark truyền thống chậm hẳn sau 50.000 điểm. Hạn chế: VectorizedMark hiện không hỗ trợ chartXSelection trên từng điểm, bạn cần fallback về Mark thường khi user zoom đủ sâu để thấy điểm riêng lẻ. Tôi đã đụng đúng case này khi ship một app theo dõi cảm biến IoT, và workaround dưới đây cứu cả sprint.
Khi kết hợp với tính năng InlineArray trong Swift 6.2 để giữ buffer time-series trên stack, bạn có thể đạt zero allocation trong hot path. Điều này đặc biệt hữu ích cho app giao dịch chứng khoán hay y tế cần real-time guarantee.
Câu hỏi thường gặp
Swift Charts có miễn phí và đi kèm sẵn với Xcode không?
Có. Swift Charts là framework gốc của Apple, đi kèm sẵn với Xcode 14 trở lên. Bạn chỉ cần import Charts mà không cần cài SwiftPM package hay CocoaPod nào. Tuy nhiên một số tính năng như Chart3D và VectorizedChartContent yêu cầu iOS 26 và Xcode 26.
Swift Charts có thể chạy trên iOS dưới 16 không?
Không. import Charts chỉ available từ iOS 16, iPadOS 16, macOS 13, watchOS 9 và tvOS 16. Nếu app phải hỗ trợ iOS 15 hoặc thấp hơn, bạn cần dùng thư viện bên thứ ba như DGCharts hoặc tự vẽ bằng Path trong SwiftUI/Canvas.
Sự khác biệt giữa BarMark với foregroundStyle và stacking là gì?
foregroundStyle(by:) chỉ thay đổi màu theo dimension nhưng giữ các bar tách riêng theo X, trong khi position: .stacking xếp chồng các giá trị thuộc cùng X lại với nhau. Dùng stacking khi bạn muốn so sánh tổng giữa các category, dùng grouped (mặc định) khi muốn so sánh chi tiết từng phần.
Làm sao export biểu đồ Swift Charts thành ảnh PNG?
Dùng ImageRenderer của SwiftUI (iOS 16+): tạo ImageRenderer(content: chartView), set .proposedSize nếu cần, rồi truy cập .uiImage hoặc .nsImage. Trên iOS 17+, bạn có thể bọc chart trong ShareLink(item: ..., preview:) để cho phép share trực tiếp.
Swift Charts có hỗ trợ candlestick chart cho ứng dụng tài chính không?
Không có CandlestickMark riêng, nhưng bạn có thể compose từ RectangleMark (cho thân nến) và RuleMark (cho bóng nến). Cộng đồng đã chia sẻ nhiều extension CandlestickMark mã nguồn mở; trong iOS 26, kết hợp với VectorizedChartContent có thể render hàng nghìn nến mượt mà.
Học cách dùng KeyframeAnimator và PhaseAnimator trong SwiftUI iOS 26 để dàn dựng animation nhiều track song song, xử lý Reduce Motion đúng cách và tối ưu hiệu năng render pipeline.
Hướng dẫn Swift Concurrency trong Swift 6.2: từ async/await cơ bản, actors, đến @concurrent mới và chiến lược migrate dự án legacy thực tế kèm code mẫu.
Swift Macros (Swift 5.9+) cho phép metaprogramming tại compile time. Tìm hiểu @Observable, @Model, #Preview, #Predicate và cách tự tạo custom macro với SwiftSyntax cho iOS 2026.