Reputation: 97
I know the code is not the prettiest, I'm still learning ways to condense my code and reduce the amount of lines needed for things such as the way I have for sorting my list. However, I do need help trying to figure out why this list is not showing a highlight over whatever row is selected. For reference, I put a print statement to verify taps are being registered on the rows, and it is working fine.
Some further info: the "onDelete" action for the row is deleting the wrong row. Perhaps this has something to do with the selection? Any advice would be great.
//
// TableView.swift
// TableDemo (iOS)
//
// Created by Kyle Carroll on 7/20/21.
//
//TODO: Delete at "index set" is deleting wrong row
//TODO: Row selection not working
import SwiftUI
struct TableView: View {
@StateObject var bookStore : BookStore = BookStore(books:bookData)
@State var sortBy: String = ""
@State var IDbuttonStatus: IDButtonStatus = .firstPress
@State var titleButtonStatus: TitleButtonStatus = .firstPress
@State var authorButtonStatus: AuthorButtonStatus = .firstPress
@State var pagesButtonStatus: PagesButtonStatus = .firstPress
enum IDButtonStatus {
case secondPress
case firstPress
var nextStatus: IDButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "id2"
case .secondPress: return "id"
}
}
}
enum TitleButtonStatus {
case firstPress
case secondPress
var nextStatus: TitleButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "title2"
case .secondPress: return "title"
}
}
}
enum AuthorButtonStatus {
case firstPress
case secondPress
var nextStatus: AuthorButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "author2"
case .secondPress: return "author"
}
}
}
enum PagesButtonStatus {
case firstPress
case secondPress
var nextStatus: PagesButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "pages2"
case .secondPress: return "pages1"
}
}
}
var body: some View {
VStack {
HStack(alignment: .center, spacing: nil) {
HStack {
Text("ID")
Button {
IDbuttonStatus = IDbuttonStatus.nextStatus
self.sortBy = IDbuttonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 50, alignment: .leading)
Divider()
HStack {
Text("Title")
Button {
titleButtonStatus = titleButtonStatus.nextStatus
self.sortBy = titleButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 200, alignment: .leading)
Divider()
HStack {
Text("Author")
Button {
authorButtonStatus = authorButtonStatus.nextStatus
self.sortBy = authorButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 200, alignment: .leading)
Divider()
HStack {
Text("Pages")
Button {
pagesButtonStatus = pagesButtonStatus.nextStatus
self.sortBy = pagesButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 75, alignment: .leading)
}
.frame(maxWidth: .infinity, maxHeight: 40, alignment: .leading)
.padding(.leading, 20)
List {
switch self.sortBy {
case "title":
ForEach (bookStore.books.sorted {$0.title < $1.title}) { book in
ListCell(book: book)
}
.onDelete(perform: deleteItems)
case "title2":
ForEach (bookStore.books.sorted {$0.title > $1.title}) { book in
ListCell(book: book)
}
case "id":
ForEach (bookStore.books.sorted {$0.id < $1.id}) { book in
ListCell(book: book)
}
case "id2":
ForEach (bookStore.books.sorted {$0.id > $1.id}) { book in
ListCell(book: book)
}
case "author":
ForEach (bookStore.books.sorted {$0.authorsort < $1.authorsort}) { book in
ListCell(book: book)
}
case "author2":
ForEach (bookStore.books.sorted {$0.authorsort > $1.authorsort}) { book in
ListCell(book: book)
}
case "pages":
ForEach (bookStore.books.sorted {$0.pages < $1.pages}) { book in
ListCell(book: book)
}
case "pages2":
ForEach (bookStore.books.sorted {$0.pages > $1.pages}) { book in
ListCell(book: book)
}
default:
ForEach (bookStore.books.sorted {$0.id < $1.id}) { book in
ListCell(book: book)
}
}
}
.listStyle(PlainListStyle())
.frame(maxWidth: .infinity, alignment: .leading)
}
}
func deleteItems(at offsets: IndexSet) {
bookStore.books.remove(atOffsets: offsets)
}
}
struct ListCell: View {
var book : Book
var body: some View {
HStack(alignment: .center, spacing: nil) {
Text("\(book.id)")
.frame(maxWidth: 50, alignment: .leading)
Divider()
Text(book.title)
.frame(maxWidth: 200, alignment: .leading)
Divider()
Text(book.author)
.frame(maxWidth: 200, alignment: .leading)
Divider()
Text("\(book.pages)")
.frame(maxWidth: 75, alignment: .leading)
}
.onTapGesture {
print(book.title)
}
}
}
struct TableView_Previews: PreviewProvider {
static var previews: some View {
if #available(iOS 15.0, *) {
TableView()
.previewInterfaceOrientation(.landscapeLeft)
} else {
// Fallback on earlier versions
}
}
}
Upvotes: 2
Views: 1175
Reputation: 36617
"...why this list is not showing a highlight over whatever row is selected." I don't know for sure, but it's easy to fix. See the test code.
Similarly, "...the "onDelete" action for the row is deleting the wrong row." You must sort the list of books as in the body to get the correct index to delete. You'll need to work on this a bit.
This is the code I used for testing and to get you going:
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct TableView: View {
@StateObject var bookStore : BookStore = BookStore() // <--- for testing
@State var sortBy: String = ""
@State var IDbuttonStatus: IDButtonStatus = .firstPress
@State var titleButtonStatus: TitleButtonStatus = .firstPress
@State var authorButtonStatus: AuthorButtonStatus = .firstPress
@State var pagesButtonStatus: PagesButtonStatus = .firstPress
@State var selectedBook: Book? = nil // <---- here
enum IDButtonStatus {
case secondPress
case firstPress
var nextStatus: IDButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "id2"
case .secondPress: return "id"
}
}
}
enum TitleButtonStatus {
case firstPress
case secondPress
var nextStatus: TitleButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "title2"
case .secondPress: return "title"
}
}
}
enum AuthorButtonStatus {
case firstPress
case secondPress
var nextStatus: AuthorButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "author2"
case .secondPress: return "author"
}
}
}
enum PagesButtonStatus {
case firstPress
case secondPress
var nextStatus: PagesButtonStatus {
switch self {
case .firstPress: return .secondPress
case .secondPress: return .firstPress
}
}
var order: String {
switch self {
case .firstPress: return "pages2"
case .secondPress: return "pages1"
}
}
}
var body: some View {
VStack {
HStack(alignment: .center, spacing: nil) {
HStack {
Text("ID")
Button {
IDbuttonStatus = IDbuttonStatus.nextStatus
self.sortBy = IDbuttonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 50, alignment: .leading)
Divider()
HStack {
Text("Title")
Button {
titleButtonStatus = titleButtonStatus.nextStatus
self.sortBy = titleButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 200, alignment: .leading)
Divider()
HStack {
Text("Author")
Button {
authorButtonStatus = authorButtonStatus.nextStatus
self.sortBy = authorButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 200, alignment: .leading)
Divider()
HStack {
Text("Pages")
Button {
pagesButtonStatus = pagesButtonStatus.nextStatus
self.sortBy = pagesButtonStatus.order
} label: {
Image(systemName: "chevron.up.chevron.down")
.foregroundColor(Color.black)
}
}
.frame(maxWidth: 75, alignment: .leading)
}
.frame(maxWidth: .infinity, maxHeight: 40, alignment: .leading)
.padding(.leading, 20)
List {
switch self.sortBy {
case "title":
ForEach (bookStore.books.sorted {$0.title < $1.title}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
.onDelete(perform: deleteItems)
case "title2":
ForEach (bookStore.books.sorted {$0.title > $1.title}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "id":
ForEach (bookStore.books.sorted {$0.id < $1.id}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "id2":
ForEach (bookStore.books.sorted {$0.id > $1.id}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "author":
ForEach (bookStore.books.sorted {$0.authorsort < $1.authorsort}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "author2":
ForEach (bookStore.books.sorted {$0.authorsort > $1.authorsort}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "pages":
ForEach (bookStore.books.sorted {$0.pages < $1.pages}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
case "pages2":
ForEach (bookStore.books.sorted {$0.pages > $1.pages}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
default:
ForEach (bookStore.books.sorted {$0.id < $1.id}) { book in
ListCell(book: book, selectedBook: $selectedBook) // <---- here
}
}
}
.listStyle(PlainListStyle())
.frame(maxWidth: .infinity, alignment: .leading)
}
}
func deleteItems(at offsets: IndexSet) { // <---- here
// // must sort the bookStore.books as in the body
// let sortedList = ...
// // get the book from the sortedList
// let theBook = sortedList[offsets.first!]
// // get the index of theBook from the bookStore, and remove it
// if let ndx = bookStore.books.firstIndex(of: theBook) {
// withAnimation {
// bookStore.books.remove(at: ndx)
// }
// }
}
}
struct ListCell: View {
var book : Book
@Binding var selectedBook: Book? // <---- here
var body: some View {
HStack(alignment: .center, spacing: nil) {
Text("\(book.id)")
.frame(maxWidth: 50, alignment: .leading)
Divider()
Text(book.title)
.frame(maxWidth: 200, alignment: .leading)
Divider()
Text(book.author)
.frame(maxWidth: 200, alignment: .leading)
Divider()
Text("\(book.pages)")
.frame(maxWidth: 75, alignment: .leading)
}
.background((selectedBook?.id ?? -1) == book.id ? Color.gray : Color.white) // <---- here
.onTapGesture {
print(book.title)
selectedBook = book // <---- here
}
}
}
struct TableView_Previews: PreviewProvider {
static var previews: some View {
if #available(iOS 15.0, *) {
TableView()
.previewInterfaceOrientation(.landscapeLeft)
} else {
// Fallback on earlier versions
}
}
}
struct Book: Identifiable, Hashable {
var id : Int
var title : String
var pages : String
var authorsort : String
var author : String = "author"
}
class BookStore: ObservableObject {
@Published var books: [Book] = [
Book(id: 1, title: "book1", pages: "page1", authorsort: "authorsort1"),
Book(id: 2, title: "book2", pages: "page2", authorsort: "authorsort2"),
Book(id: 3, title: "book3", pages: "page3", authorsort: "authorsort3")]
}
struct ContentView: View {
var body: some View {
TableView()
}
}
Upvotes: 1