当前位置:网站首页>Create a table under swiftui with table
Create a table under swiftui with table
2022-07-28 14:40:00 【Dongpo Pig Knuckle】
Visit my blog www.fatbobman.com[1] You can get a better reading experience . Welcome to Discord channel [2] More communication in
Table yes SwiftUI 3.0 In Chinese, it means macOS Table controls provided by the platform , Developers can quickly create interactive multi column tables through it . stay WWDC 2022 in ,Table Expanded to iPadOS platform , So that it has more space to display . This article will introduce Table Usage of 、 analysis Table And how to implement similar functions on other platforms .
With columns ( Row ) The characteristics of the List
stay Table The definition of , Have clear lines ( Row ) And column ( Column ) The concept of . But compared to SwiftUI Grid container in ( LazyVGrid、Grid ) Come on ,Table In essence, it is closer to List . Developers can put Table Regarded as having column characteristics List .
image-20220620142551830
The picture above shows us using List Create a about Locale Forms of information , Each line shows an and Locale Relevant data . Create the code as follows :
struct LocaleInfoList: View {
@State var localeInfos: [LocaleInfo] = []
let titles = [" identifier ", " Language ", " Price ", " Currency code ", " Currency symbols "]
var body: some View {
List {
HStack {
ForEach(titles, id: \.self) { title in
Text(title)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Divider()
}
}
ForEach(localeInfos) { localeInfo in
HStack {
Group {
Text(localeInfo.identifier)
Text(localeInfo.language)
Text(localeInfo.price.formatted())
.foregroundColor(localeInfo.price > 4 ? .red : .green)
Text(localeInfo.currencyCode)
Text(localeInfo.currencySymbol)
}
.lineLimit(1)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
}
.task {
localeInfos = prepareData()
}
}
}
struct LocaleInfo: Identifiable, Hashable {
var id: String {
identifier
}
let identifier: String
let language: String
let currencyCode: String
let currencySymbol: String
let price: Int = .random(in: 3...6)
let updateDate = Date.now.addingTimeInterval(.random(in: -100000...100000))
var supported: Bool = .random()
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
// Generate demo data
func prepareData() -> [LocaleInfo] {
Locale.availableIdentifiers
.map {
let cnLocale = Locale(identifier: "zh-cn")
let locale = Locale(identifier: $0)
return LocaleInfo(
identifier: $0,
language: cnLocale.localizedString(forIdentifier: $0) ?? "",
currencyCode: locale.currencyCode ?? "",
currencySymbol: locale.currencySymbol ?? ""
)
}
.filter {
!($0.currencySymbol.isEmpty || $0.currencySymbol.isEmpty || $0.currencyCode.isEmpty)
}
}
The following is the use of Table Code to create the same table :
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
var body: some View {
Table {
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
TableColumn(" Price ") {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
} rows: {
ForEach(localeInfos) {
TableRow($0)
}
}
.task {
localeInfos = prepareData()
}
}
}
image-20220620142510240
Compare with List Version of , Not only is there less code 、 More clearly stated , And we can also get a fixed title bar . Same as List equally ,Table It also has construction methods that directly reference data , The above code can be further simplified to :
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
var body: some View {
Table(localeInfos) { // Direct reference to data sources
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
TableColumn(" Price ") {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
}
.task {
localeInfos = prepareData()
}
}
}
stay SwiftUI 4.0 In the first beta version of ( Xcode 14.0 beta (14A5228q) ),Table stay iPad OS Poor performance on , There are many Bug . for example : Header row and data row ( First line ) overlap ; The first column of the header row does not display ; Poor rolling and some performance ( Row height ) And macOS Version inconsistency, etc .
Table And List The approximate point of :
- The declaration logic is close to
- And LazyVGrid( LazyHGrid ) and Grid Tends to place data elements in a cell ( Cell ) Different from , stay Table And List in , More accustomed to data elements as rows ( Row ) In the form of ( Display different attribute contents of data in one row )
- stay Table The data in is loaded lazily , Row view ( TableColumn ) Of onAppear and onDisappear Their behavior is also related to List Agreement
- Table And List Not really a layout container , They are not like LazyVGrid、Grid、VStack Such as the layout container to support the view rendering function ( ImageRenderer )
Column width and row height
Column width
stay Table in , We can set the column width in the column settings :
Table(localeInfos) {
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
.width(min: 200, max: 300) // Set the width range
TableColumn(" Price ") {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
.width(50) // Set the specific width
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
}
image-20220620150114288
Other columns without specified column width ( identifier 、 Currency code 、 Currency symbols ), Will be based on Table Divide the remaining horizontal dimensions in . stay macOS On , Users can change the column spacing by dragging the column spacing line with the mouse .
And List equally ,Table Built in vertical scrolling support . stay macOS On , If Table The content in ( Row width ) More than the Table Width ,Table Horizontal scrolling support will be turned on automatically .
If the amount of data is small, it can be displayed completely , Developers can use scrollDisabled(true) Shield built-in scrolling support .
Row height
stay macOS Next ,Table The row height of is locked . No matter how high the actual height of the content in the cell is required ,Table The default row height given by the system will always be maintained .
TableColumn(" Price ") {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
.font(.system(size: 64))
.frame(height:100)
image-20220620181736770
stay iPadOS Next ,Table According to the height of the cell , Adjust row height automatically .
image-20220620181923446
At present, it is uncertain whether this situation is intentional design or Bug
Spacing and alignment
because Table Not really a grid layout container , Therefore, there is no setting for row and column spacing or row and column alignment .
Developers can use frame Modifier to change the alignment of the contents of a cell ( The title alignment cannot be changed for the time being ):
TableColumn(" Currency code ") {
Text($0.currencyCode)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .trailing)
}
image-20220620182615838
stay Table in , If the attribute type displayed in this column is String, There is no need to add other settings , Can be used based on KeyPath The simplified way of writing :
TableColumn(" Currency code ", value:\.currencyCode)
however , If the attribute type is not String, Or you need to add other settings ( typeface 、 Color, etc. ), Can only be defined as a trailing closure TableColumn ( Such as the currency code above ).
style
SwiftUI by Table Several styles are available , Unfortunately, there are only .inset It can be used for iPadOS .
Table(localeInfos) {
// Definition TableColumn ...
}
.tableStyle(.inset(alternatesRowBackgrounds:false))
- inset Default style ( The screenshots before this article are inset style ), Can be used for macOS and iPadOS. stay mac The following is equivalent to
inset(alternatesRowBackgrounds: true), stay iPadOS The following is equivalent toinset(alternatesRowBackgrounds: false) - inset(alternatesRowBackgrounds: Bool) Only used for macOS, You can set whether to turn on line interleaving background , Easy to distinguish visually
- bordered Only used for macOS, by Table Add border
image-20220620183823794
- bordered(alternatesRowBackgrounds: Bool) Only used for macOS, You can set whether to turn on line interleaving background , Easy to distinguish visually
Maybe in a later beta ,SwiftUI Will expand more styles to iPadOS platform
Row selection
stay Table Enables row selection and List In a very similar way :
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
@State var selection: String?
var body: some View {
Table(localeInfos, selection: $selection) {
// Definition TableColumn ...
}
}
}
It should be noted that ,Table Require bound variable types and data ( The data needs to follow Identifier agreement ) Of id The same type . In this case ,LocaleInfo Of id The type is String.
@State var selection: String? // The radio
@State var selections: Set<String> = [] // multi-select , need LocaleInfo follow Hashable agreement
The following figure shows the scenario after multiple selection is enabled :
image-20220620184638673
Sort
Table Another core function is to efficiently implement multi-attribute sorting .
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
@State var order: [KeyPathComparator<LocaleInfo>] = [.init(\.identifier, order: .forward)] // Sorting conditions
var body: some View {
Table(localeInfos, sortOrder: $order) { // Binding sort conditions
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
.width(min: 200, max: 300)
TableColumn(" Price ",value: \.price) {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
.width(50)
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
}
.onChange(of: order) { newOrder in
withAnimation {
localeInfos.sort(using: newOrder) // Reorder the data when the sorting conditions change
}
}
.task {
localeInfos = prepareData()
localeInfos.sort(using: order) // Initialize sorting
}
.scenePadding()
}
}
table_sort_demo1_2022-06-20_18.55.16.2022-06-20 18_57_13
Table Itself does not modify the data source , When Table After binding the sorting variable , Click the column header that supports sorting ,Table Will automatically change the contents of the sort variable . Developers still need to monitor the changes of sorting variables to sort .
Table The type of sorting variable is required to follow SortComparator Array of , In this example, we directly use Swift Provided KeyPathComparator type .
If you don't want a column to support sorting , Just don't use anything that contains value Parametric TableColumn The construction method is enough , for example :
TableColumn(" Currency code ", value: \.currencyCode) // Enable sorting based on this attribute
TableColumn(" Currency code "){ Text($0.currencyCode) } // Do not enable sorting based on this attribute
// Do not bind a sort variable , Use the following wording . The application will not compile ( And rarely get an error message )
TableColumn(" Price ",value: \.currencyCode) {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
Current beta 14A5228q , When the property type is Bool when , Enabling sorting on this column will cause the application to fail to compile
Although after clicking on the sortable title , Only one column header shows the sort direction , But in fact Table The sorting order of sorting variables will be added or sorted according to the user's click order . The following code clearly demonstrates this :
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
@State var order: [KeyPathComparator<LocaleInfo>] = [.init(\.identifier, order: .forward)]
var body: some View {
VStack {
sortKeyPathView() // Displays the current sort order
.frame(minWidth: 0, maxWidth: .infinity, alignment: .trailing)
Table(localeInfos, sortOrder: $order) {
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
.width(min: 200, max: 300)
TableColumn(" Price ", value: \.price) {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
.width(50)
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
}
}
.onChange(of: order) { newOrder in
withAnimation {
localeInfos.sort(using: newOrder)
}
}
.task {
localeInfos = prepareData()
localeInfos.sort(using: order)
}
.scenePadding()
}
func sortKeyPath() -> [String] {
order
.map {
let keyPath = $0.keyPath
let sortOrder = $0.order
var keyPathString = ""
switch keyPath {
case \LocaleInfo.identifier:
keyPathString = " identifier "
case \LocaleInfo.language:
keyPathString = " Language "
case \LocaleInfo.price:
keyPathString = " Price "
case \LocaleInfo.currencyCode:
keyPathString = " Currency code "
case \LocaleInfo.currencySymbol:
keyPathString = " Currency symbols "
case \LocaleInfo.supported:
keyPathString = " Already supported "
case \LocaleInfo.updateDate:
keyPathString = " date "
default:
break
}
return keyPathString + (sortOrder == .reverse ? "↓" : "↑")
}
}
@ViewBuilder
func sortKeyPathView() -> some View {
HStack {
ForEach(sortKeyPath(), id: \.self) { sortKeyPath in
Text(sortKeyPath)
}
}
}
}
table_sort_demo2_2022-06-20_19.11.48.2022-06-20 19_13_16
If you are worried about the performance of multi-attribute based sorting ( When the data volume is large ), You can only use the last created sort criteria :
.onChange(of: order) { newOrder in
if let singleOrder = newOrder.first {
withAnimation {
localeInfos.sort(using: singleOrder)
}
}
}
Will be SortComparator convert to SortDescription( or NSSortDescription ) be used for Core Data when , Please do not use Core Data Unsupported Compare Algorithm .
Drag and drop
Table Support to conduct... In behavioral units Drag&Drop . Enable Drag When supported , Will not be available Table Simplified definition of :
Table {
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
.width(min: 200, max: 300)
TableColumn(" Price ", value: \.price) {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
.width(50)
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
} rows: {
ForEach(localeInfos){ localeInfo in
TableRow(localeInfo)
.itemProvider { // enable Drap
NSItemProvider(object: localeInfo.identifier as NSString)
}
}
}
table_drag_demo_2022-06-20_19.36.09.2022-06-20 19_37_28
Interaction
In addition to row selection and row dragging ,Table It also supports setting context menus for rows ( macOS 13+、iPadOS 16+ ):
ForEach(localeInfos) { localeInfo in
TableRow(localeInfo)
.contextMenu{
Button(" edit "){}
Button(" Delete "){}
Button(" share "){}
}
}
image-20220620194057400
Create interactive cells , It will greatly improve the user experience of the form .
struct TableDemo: View {
@State var localeInfos = [LocaleInfo]( "LocaleInfo")
var body: some View {
VStack {
Table(localeInfos) {
TableColumn(" identifier ", value: \.identifier)
TableColumn(" Language ", value: \.language)
.width(min: 200, max: 300)
TableColumn(" Price ") {
Text("\($0.price)")
.foregroundColor($0.price > 4 ? .red : .green)
}
.width(50)
TableColumn(" Currency code ", value: \.currencyCode)
TableColumn(" Currency symbols ", value: \.currencySymbol)
TableColumn(" Already supported ") {
supportedToggle(identifier: $0.identifier, supported: $0.supported)
}
}
}
.lineLimit(1)
.task {
localeInfos = prepareData()
}
.scenePadding()
}
@ViewBuilder
func supportedToggle(identifier: String, supported: Bool) -> some View {
let binding = Binding<Bool>(
get: { supported },
set: {
if let id = localeInfos.firstIndex(where: { $0.identifier == identifier }) {
self.localeInfos[id].supported = $0
}
}
)
Toggle(isOn: binding, label: { Text("") })
}
}
image-20220620194359218
Pioneer or martyr ?
If you are in the Xcode Written in Table Code for , The probability is that the automatic prompt will not work . There may even be applications that cannot be compiled , But there is no clear error message ( The error occurs when Table Inside ).
What are the main reasons for the above problems , Apple doesn't use other SwiftUI The common writing method of control ( Native SwiftUI Container or packaging UIKit Control ), Pioneered the use of result builder by Table Write your own DSL .
Maybe because of Table Of DSL Poor efficiency ( Too many generics 、 Too many construction methods 、 One Table There are two Builder ), Current version Xcode Processing Table The code is quite laborious .
in addition , because Table DSL The definition of is not complete ( Lack of similarity Group The container of ), At present, only ten columns of data can be supported ( For reasons, see ViewBuilder Research ( Next ) —— Learn from imitation [3] ).
Maybe Apple has absorbed Table DSL Lesson ,WWDC 2022 The SwiftUI Charts( Is based on result builder ) stay Xcode The performance is obviously better than Table .
I hope Apple will Charts The experience gained in this course feeds back to Table , Avoid turning a pioneer into a martyr .
Create tables on other platforms
although Table Can be in accordance with iOS 16 Of iPhone Up operation , However, only the first column of data can be displayed , Therefore, it has no practical significance .
If you want to Table Not yet supported or support incomplete platforms ( for example iPhone) Realize the table function on , Please choose the appropriate alternative according to your needs :
- Large amount of data , Need lazy loading List、LazyVGrid
- Row based interaction ( Drag and drop 、 Context menu 、 choice ) List( Grid Medium GridRow Not really a line )
- Need views to render ( Save as a picture ) LazyVGrid、Grid
- Fixable header line List、LazyVGrid、Grid( For example, use matchedGeometryEffect )
summary
If you want to SwiftUI Use less code in 、 Create interactive tables for clearer presentation , Might as well try. Table . I also hope apple can improve in the next version Table stay Xcode Development efficiency in , And for Table Add more native functionality .
I hope this article can help you .
Reference material
[1] www.fatbobman.com: https://www.fatbobman.com
[2] Discord channel : https://discord.gg/ApqXmy5pQJ
[3] ViewBuilder Research ( Next ) —— Learn from imitation : https://www.fatbobman.com/posts/viewBuilder2/# Create more _buildBlock
边栏推荐
- SwiftUI 布局 —— 尺寸( 下 )
- 基础架构之日志管理平台及钉钉&邮件告警通知
- Getting started with scottplot tutorial: getting and displaying values at the mouse
- 9、 Uni popup usage popup effect at the bottom of the drop-down box
- Unittest executes runtestcase prompt <_ io. Textiowrapper name= '< stderr>' mode=W encoding=UTF-8 > solution
- Iterator iterator interface
- Brief introduction and use of mqtt entry level
- Floating point data type in C language (did you learn to waste it)
- 力扣解法汇总1331-数组序号转换
- 草料二维码--在线二维码生成器
猜你喜欢

Hcip day 11

Leetcode 0143. rearrange linked list

10、 Timestamp

Bulk Rename Utility

2022高处安装、维护、拆除考试题库及在线模拟考试

文件批量重命名工具Bulk Rename Utility

2022 safety officer-a certificate operation certificate examination question bank simulated examination platform operation

TDengine 助力西门子轻量级数字化解决方案

Metersphere -- Open Source continuous testing platform

Recommended super easy-to-use mobile screen recording software
随机推荐
MeterSphere--开源持续测试平台
Leetcode 0142. circular linked list II
Career planning of Software Test Engineer
&0xffffffff(0x08)
How to reduce the resolution of only 3D camera but not UI camera
爆肝整理JVM十大模块知识点总结,不信你还不懂
Another way of understanding the essence of Hamming code
HCIP第十天
这3款在线PS工具,得试试
468产品策划与推广方案(150份)
面试官:ThreadLocal使用场景有哪些?内存泄露问题如何避免?
2022 melting welding and thermal cutting examination questions and online simulation examination
九、uni-popup用法 下拉框底部弹窗效果
It's so hot that solar power can't take off? Hello, head
How to use the C language library function getchar ()
Three methods to disassemble the rotation array
Force deduction solution summary 1331 array sequence number conversion
力扣解法汇总1331-数组序号转换
Detailed explanation of common commands of vim (VIM use tutorial)
Summarize the knowledge points of the ten JVM modules. If you don't believe it, you still don't understand it