Skip Fuse is now free for indie developers!
skip.tools logo

List

Skip support for SwiftUI.List on Android. Consult the SkipUI module for a complete list of supported SwiftUI.

The following example screens and source code is from SkipUI’s Showcase sample app ListPlayground.swift

Android screenshot for List component (light mode) iPhone screenshot for List component (light mode) iPhone screenshot for List component (dark mode) Android screenshot for List component (dark mode)
import SwiftUI
enum ListPlaygroundType: String, CaseIterable {
 case fixedContent
 case indexedContent
 case collectionContent
 case forEachContent
 case sectioned
 case empty
 case emptyItems
 case plainStyle
 case plainStyleSectioned
 case plainStyleEmpty
 case hiddenBackground
 case hiddenBackgroundPlainStyle
 case editActions
 case observableEditActions
 case sectionedEditActions
 case onMoveDelete
 var title: String {
 switch self {
 case .fixedContent:
 return "Fixed Content"
 case .indexedContent:
 return "Indexed Content"
 case .collectionContent:
 return "Collection Content"
 case .forEachContent:
 return "ForEach Content"
 case .sectioned:
 return "Sectioned"
 case .empty:
 return "Empty"
 case .emptyItems:
 return "Empty Items"
 case .plainStyle:
 return "Plain Style"
 case .plainStyleSectioned:
 return "Plain Style Sectioned"
 case .plainStyleEmpty:
 return "Plain Style Empty"
 case .hiddenBackground:
 return "Hidden Background"
 case .hiddenBackgroundPlainStyle:
 return "Hidden Background Plain Style"
 case .editActions:
 return "EditActions"
 case .observableEditActions:
 return "Observable EditActions"
 case .sectionedEditActions:
 return "Sectioned EditActions"
 case .onMoveDelete:
 return ".onMove, .onDelete"
 }
 }
}
struct ListPlayground: View {
 @StateObject var editActionsModel = ObservableEditActionsListPlayground.Model()
 var body: some View {
 List(ListPlaygroundType.allCases, id: \.self) { type in
 NavigationLink(type.title, value: type)
 }
 .toolbar {
 PlaygroundSourceLink(file: "ListPlayground.swift")
 }
 .navigationDestination(for: ListPlaygroundType.self) {
 switch 0ドル {
 case .fixedContent:
 FixedContentListPlayground()
 .navigationTitle(0ドル.title)
 case .indexedContent:
 IndexedContentListPlayground()
 .navigationTitle(0ドル.title)
 case .collectionContent:
 CollectionContentListPlayground()
 .navigationTitle(0ドル.title)
 case .forEachContent:
 ForEachContentListPlayground()
 .navigationTitle(0ドル.title)
 case .sectioned:
 SectionedListPlayground()
 .navigationTitle(0ドル.title)
 case .empty:
 EmptyListPlayground()
 .navigationTitle(0ドル.title)
 case .emptyItems:
 EmptyItemsListPlayground()
 .navigationTitle(0ドル.title)
 case .plainStyle:
 PlainStyleListPlayground()
 .navigationTitle(0ドル.title)
 case .plainStyleSectioned:
 PlainStyleSectionedListPlayground()
 .navigationTitle(0ドル.title)
 case .plainStyleEmpty:
 PlainStyleEmptyListPlayground()
 .navigationTitle(0ドル.title)
 case .hiddenBackground:
 HiddenBackgroundListPlayground()
 .navigationTitle(0ドル.title)
 case .hiddenBackgroundPlainStyle:
 HiddenBackgroundPlainStyleListPlayground()
 .navigationTitle(0ドル.title)
 case .editActions:
 EditActionsListPlayground()
 .navigationTitle(0ドル.title)
 case .observableEditActions:
 ObservableEditActionsListPlayground(model: editActionsModel)
 .navigationTitle(0ドル.title)
 case .sectionedEditActions:
 SectionedEditActionsListPlayground()
 .navigationTitle(0ドル.title)
 case .onMoveDelete:
 OnMoveDeleteListPlayground()
 .navigationTitle(0ドル.title)
 }
 }
 }
}
struct FixedContentListPlayground: View {
 var body: some View {
 List {
 Group {
 Text("Row 1")
 Text("Row 2")
 }
 VStack {
 Text("Row 3a")
 Text("Row 3b")
 }
 }
 }
}
struct IndexedContentListPlayground: View {
 var body: some View {
 List(100..<200) {
 Text("Row \(0ドル)")
 }
 }
}
struct CollectionContentListPlayground: View {
 struct ListItem {
 let i: Int
 let s: String
 }
 func items() -> [ListItem] {
 var items: [ListItem] = []
 for i in 0..<100 {
 items.append(ListItem(i: i, s: "Item \(i)"))
 }
 return items
 }
 var body: some View {
 List(items(), id: \.i) {
 Text(0ドル.s)
 }
 }
}
struct ForEachContentListPlayground: View {
 struct ListItem {
 let id: UUID // Test ID serialization
 let i: Int
 let s: String
 }
 func items() -> [ListItem] {
 var items: [ListItem] = []
 for i in 0..<10 {
 items.append(ListItem(id: UUID(), i: i, s: "Foreach object row \(i)"))
 }
 return items
 }
 var body: some View {
 List {
 Text("Standalone row 1")
 ForEach(0..<10) { index in
 Text("ForEach index row: \(index)")
 }
 Text("Standalone row 2")
 ForEach(items(), id: \.id) {
 Text(0ドル.s)
 }
 Text("Standalone row 3")
 ForEach(0..<10) { index1 in
 ForEach(1..<3) { index2 in
 Text("Nested ForEach row: \(index1).\(index2)")
 }
 }
 }
 }
}
struct SectionedListPlayground: View {
 var body: some View {
 List {
 Section("Section 1") {
 Text("Row 1.1")
 ForEach(0..<10) { index in
 Text("ForEach row: 1.\(index)")
 }
 }
 Section {
 Text("Row 2.1")
 ForEach(0..<10) { index in
 Text("ForEach row: 2.\(index)")
 }
 } header: {
 Text("Section 2")
 } footer: {
 Text("Footer 2")
 }
 ForEach(0..<2) { index1 in
 Section("ForEach section \(index1)") {
 ForEach(0..<5) { index2 in
 Text("ForEach row: \(index1).\(index2)")
 }
 }
 }
 }
 }
}
struct EmptyListPlayground: View {
 var body: some View {
 List {
 }
 }
}
struct EmptyItemsListPlayground: View {
 var body: some View {
 List {
 EmptyView()
 Text("After initial empty row")
 Section("Section 1") {
 EmptyView()
 Text("After initial section empty row")
 EmptyView()
 Text("After another section empty row")
 }
 Section("Section 2") {
 Text("Before final section empty row")
 EmptyView()
 }
 Text("Before final empty row")
 EmptyView()
 }
 }
}
struct PlainStyleListPlayground: View {
 var body: some View {
 List(0..<100) {
 Text("Row \(0ドル)")
 }
 .listStyle(.plain)
 }
}
struct PlainStyleSectionedListPlayground: View {
 var body: some View {
 List {
 Section("Section 1") {
 Text("Row 1.1")
 ForEach(0..<30) { index in
 Text("ForEach row: 1.\(index)")
 }
 }
 Section {
 Text("Row 2.1")
 ForEach(0..<30) { index in
 Text("ForEach row: 2.\(index)")
 }
 } header: {
 Text("Section 2")
 } footer: {
 Text("Footer 2")
 }
 Section {
 ForEach(0..<30) { index in
 Text("ForEach row: 3.\(index)")
 }
 } footer: {
 Text("Footer 3")
 }
 }
 .listStyle(.plain)
 }
}
struct PlainStyleEmptyListPlayground: View {
 var body: some View {
 List {
 }
 .listStyle(.plain)
 }
}
struct HiddenBackgroundListPlayground: View {
 var body: some View {
 ZStack {
 Color.yellow.opacity(0.5)
 List {
 Section("Standard Row Background") {
 ForEach(0..<30) { index in
 Text("Row: 1.\(index)")
 }
 }
 Section {
 ForEach(0..<30) { index in
 Text("Row: 2.\(index)")
 .listRowBackground(Color.pink)
 .listRowSeparator(.hidden)
 }
 } header: {
 Text("Pink Row Background")
 } footer: {
 Text("... and hidden separators")
 }
 }
 .scrollContentBackground(.hidden)
 }
 }
}
struct HiddenBackgroundPlainStyleListPlayground: View {
 var body: some View {
 ZStack {
 Color.yellow.opacity(0.5)
 List {
 Section("Section 1") {
 ForEach(0..<30) { index in
 Text("Row: 1.\(index)")
 }
 }
 Section {
 ForEach(0..<30) { index in
 Text("Row: 2.\(index)")
 .listRowBackground(Color.pink)
 .listRowSeparator(.hidden)
 }
 } header: {
 Text("Section 2")
 } footer: {
 Text("Footer")
 }
 }
 .listStyle(.plain)
 .scrollContentBackground(.hidden)
 }
 }
}
struct EditActionsListPlayground: View {
 struct ListItem {
 let i: Int
 let s: String
 var toggled = false
 }
 @State var items = {
 var items: [ListItem] = []
 for i in 0..<50 {
 items.append(ListItem(i: i, s: "Item \(i)"))
 }
 return items
 }()
 var body: some View {
 List($items, id: \.i, editActions: .all) { $item in
 if item.i % 5 == 0 {
 Text("\(item.s) .deleteDisabled")
 .deleteDisabled(true)
 } else if item.i % 4 == 0 {
 Text("\(item.s) .moveDisabled")
 .moveDisabled(true)
 } else {
 HStack {
 Text(item.s)
 Spacer()
 Toggle("isOn", isOn: $item.toggled)
 .labelsHidden()
 }
 }
 }
 }
}
struct ObservableEditActionsListPlayground: View {
 class Model: ObservableObject {
 @Published var items: [ListItem] = {
 var items: [ListItem] = []
 for i in 0..<50 {
 items.append(ListItem(i: i, s: "Item \(i)"))
 }
 return items
 }()
 }
 struct ListItem {
 let i: Int
 let s: String
 var toggled = false
 }
 @ObservedObject var model: Model
 var body: some View {
 List($model.items, id: \.i, editActions: .all) { $item in
 HStack {
 Text(item.s)
 Spacer()
 Toggle("isOn", isOn: $item.toggled)
 .labelsHidden()
 }
 }
 }
}
struct SectionedEditActionsListPlayground: View {
 @State var items0 = {
 var items: [Int] = []
 for i in 0..<10 {
 items.append(i)
 }
 return items
 }()
 @State var items1 = {
 var items: [Int] = []
 for i in 11..<20 {
 items.append(i)
 }
 return items
 }()
 @State var items2 = {
 var items: [Int] = []
 for i in 21..<30 {
 items.append(i)
 }
 return items
 }()
 var body: some View {
 List {
 Section("Section 0 (Fixed)") {
 ForEach(items0, id: \.self) { item in
 Text("Item \(item)")
 }
 }
 Section("Section 1") {
 ForEach($items1, id: \.self, editActions: .all) { $item in
 Text("Item \(item)")
 }
 }
 Section("Section 2") {
 ForEach($items2, id: \.self, editActions: .all) { $item in
 Text("Item \(item)")
 }
 }
 }
 }
}
struct OnMoveDeleteListPlayground: View {
 @State var items0 = {
 var items: [Int] = []
 for i in 0..<10 {
 items.append(i)
 }
 return items
 }()
 @State var items1 = {
 var items: [Int] = []
 for i in 11..<20 {
 items.append(i)
 }
 return items
 }()
 @State var items2 = {
 var items: [Int] = []
 for i in 21..<30 {
 items.append(i)
 }
 return items
 }()
 @State var actionString = ""
 @State var action: () -> Void = {}
 @State var actionIsPresented = false
 var body: some View {
 List {
 Section(".onMove") {
 ForEach(items0, id: \.self) { item in
 Text("Item \(item)")
 }
 .onMove { fromOffsets, toOffset in
 actionString = "Move \(fromOffsets.count) item(s)"
 action = {
 withAnimation { items0.move(fromOffsets: fromOffsets, toOffset: toOffset) }
 action = {}
 }
 actionIsPresented = true
 }
 }
 Section(".onDelete") {
 ForEach(items1, id: \.self) { item in
 Text("Item \(item)")
 }
 .onDelete { offsets in
 actionString = "Delete \(offsets.count) item(s)"
 action = {
 withAnimation { items1.remove(atOffsets: offsets) }
 action = {}
 }
 actionIsPresented = true
 }
 }
 Section(".onMove, .onDelete") {
 ForEach(items2, id: \.self) { item in
 Text("Item \(item)")
 }
 .onMove { fromOffsets, toOffset in
 actionString = "Move \(fromOffsets.count) item(s)"
 action = {
 withAnimation { items2.move(fromOffsets: fromOffsets, toOffset: toOffset) }
 action = {}
 }
 actionIsPresented = true
 }
 .onDelete { offsets in
 actionString = "Delete \(offsets.count) item(s)"
 action = {
 withAnimation { items2.remove(atOffsets: offsets) }
 action = {}
 }
 actionIsPresented = true
 }
 }
 }
 .confirmationDialog(actionString, isPresented: $actionIsPresented) {
 Button(actionString, role: .destructive, action: action)
 }
 }
}

AltStyle によって変換されたページ (->オリジナル) /