Home > Mobile >  How can I move list item to top when we mark the list item in SwiftUI?
How can I move list item to top when we mark the list item in SwiftUI?

Time:03-08

I have list items in SwiftUI, and I want to move item on top when I set the favorite item with star icon, and I want to repeat it for second item again, is it possible? I know there is example for moving the row, but I do not understand how I will do like that.

Model:

struct Restaurant: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var isFavorite: Bool = false
  
}

BasicImageRow:

    struct BasicImageRow: View {

    var restaurant: Restaurant
    
    var body: some View {
        HStack {
            Image(restaurant.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            Text(restaurant.name)
          
            if restaurant.isFavorite {
                Spacer()
                
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
            }
        }
    }
}

View:

import SwiftUI

struct ContentView: View {
    
  
    @State private var selectedRestaurant: Restaurant?
    
  @State private  var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
                  Restaurant(name: "Homei", image: "homei"),
                  Restaurant(name: "Teakha", image: "teakha"),
                  Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
                  Restaurant(name: "Petite Oyster", image: "petiteoyster"),
                  Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"),

            ]

 
    var body: some View {
        List {
            ForEach(restaurants) { restaurant in
                BasicImageRow(restaurant: restaurant)
                    .contextMenu {
                        
                        Button(action: {
                            // mark the selected restaurant as check-in
                            self.checkIn(item: restaurant)
                        }) {
                            HStack {
                                Text("Check-in")
                                Image(systemName: "checkmark.seal.fill")
                            }
                        }
                        
                        Button(action: {
                            // delete the selected restaurant
                            self.delete(item: restaurant)
                        }) {
                            HStack {
                                Text("Delete")
                                Image(systemName: "trash")
                            }
                        }
                        
                        Button(action: {
                            // mark the selected restaurant as favorite
                            self.setFavorite(item: restaurant)
                        }) {
                            HStack {
                                Text("Favorite")
                                Image(systemName: "star")
                            }
                        }
                    }
                    .onTapGesture {
                        self.selectedRestaurant = restaurant
                    }
                    .actionSheet(item: self.$selectedRestaurant) { restaurant in
                        
                        ActionSheet(title: Text("What do you want to do"), message: nil, buttons: [
                            
                            .default(Text("Mark as Favorite"), action: {
                                self.setFavorite(item: restaurant)
                            }),
                            
                            .destructive(Text("Delete"), action: {
                                self.delete(item: restaurant)
                            }),
                            
                            .cancel()
                        ])
                    }
            }
           
        }
    }
   
    
    private func setFavorite(item restaurant: Restaurant) {
        if let index = self.restaurants.firstIndex(where: { $0.id == restaurant.id }) {
            self.restaurants[index].isFavorite.toggle()
        }
    }
   
}

CodePudding user response:

One way would be to have a FavoriteModel that would keep track of when the item was marked.

struct FavoriteModel{
    var isFavorite: Bool = false
    var timeStamp: Date = Date()
    
    mutating func toogleFavorite(){
        isFavorite.toggle()
        timeStamp = Date()
    }
}

Then sort the items by the timestamp

ForEach(restaurants.sorted(by: {
        $0.isFavorite.timeStamp  > $1.isFavorite.timeStamp
    })) { restaurant in

It some use cases it might even be beneficial to keep track of the people that have selected the item as favorite

struct FavoriteModel{
    var isFavorite: Bool = false
    var timeStamp: Date = Date()
    var userIds: Set<UUID> = []
    mutating func toogleFavorite(userId: UUID){
        isFavorite.toggle()
        timeStamp = Date()
        
        if isFavorite{
            userIds.insert(userId)
        }else{
            userIds.remove(userId)
        }
    }
}

Below is a full set of working code.

struct Restaurant: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var isFavorite: FavoriteModel = .init()
    
}
struct BasicImageRow: View {
    
    var restaurant: Restaurant
    
    var body: some View {
        HStack {
            Image(restaurant.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            Text(restaurant.name)
            
            if restaurant.isFavorite.isFavorite {
                Spacer()
                
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
            }
        }
    }
}
struct RestaurantView: View {
    @State private var selectedRestaurant: Restaurant?
    
    @State private  var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
                                        Restaurant(name: "Homei", image: "homei"),
                                        Restaurant(name: "Teakha", image: "teakha"),
                                        Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
                                        Restaurant(name: "Petite Oyster", image: "petiteoyster"),
                                        Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"),
                                        
    ]
    
    
    var body: some View {
        List {
            ForEach(restaurants.sorted(by: {
                $0.isFavorite.timeStamp  > $1.isFavorite.timeStamp
            })) { restaurant in
                BasicImageRow(restaurant: restaurant)
                    .contextMenu {
                        
                        Button(action: {
                            // mark the selected restaurant as check-in
                            //self.checkIn(item: restaurant)
                        }) {
                            HStack {
                                Text("Check-in")
                                Image(systemName: "checkmark.seal.fill")
                            }
                        }
                        
                        Button(action: {
                            // delete the selected restaurant
                            //self.delete(item: restaurant)
                        }) {
                            HStack {
                                Text("Delete")
                                Image(systemName: "trash")
                            }
                        }
                        
                        Button(action: {
                            // mark the selected restaurant as favorite
                            self.setFavorite(item: restaurant)
                        }) {
                            HStack {
                                Text("Favorite")
                                Image(systemName: "star")
                            }
                        }
                    }
                    .onTapGesture {
                        self.selectedRestaurant = restaurant
                    }
                    .actionSheet(item: self.$selectedRestaurant) { restaurant in
                        
                        ActionSheet(title: Text("What do you want to do"), message: nil, buttons: [
                            
                            .default(Text("Mark as Favorite"), action: {
                                self.setFavorite(item: restaurant)
                            }),
                            
                                .destructive(Text("Delete"), action: {
                                    //self.delete(item: restaurant)
                                }),
                            
                                .cancel()
                        ])
                    }
            }
            
        }
    }
    
    
    private func setFavorite(item restaurant: Restaurant) {
        if let index = self.restaurants.firstIndex(where: { $0.id == restaurant.id }) {
            self.restaurants[index].isFavorite.toogleFavorite()
        }
    }
    
}

struct RestaurantView_Previews: PreviewProvider {
    static var previews: some View {
        RestaurantView()
    }
}

CodePudding user response:

You just need to update restaurants inside withAnimation block, SwiftUI will know how to animate them with their id

ForEach(restaurants) { restaurant in
    Button(action: {
        withAnimation {
            var favoritedRestaurant = restaurant
            favoritedRestaurant.isFavorite = true
            restaurants = [favoritedRestaurant]   restaurants.filter { $0.id != restaurant.id }
        }
    }) {
        BasicImageRow(restaurant: restaurant)
    }
}
  • Related