Saltar al contenido

Cómo hacer una lista con una sola selección con SwiftUI 5

Solución:

La forma más fácil de lograr esto sería tener @State en la Vista que contiene la lista con la selección y pasarla como @Binding a las celdas:

struct SelectionView: View 

    let fruit = ["apples", "pears", "bananas", "pineapples"]
    @State var selectedFruit: String? = nil

    var body: some View 
        List 
            ForEach(fruit, id: .self)  item in
                SelectionCell(fruit: item, selectedFruit: self.$selectedFruit)
            
        
    


struct SelectionCell: View 

    let fruit: String
    @Binding var selectedFruit: String?

    var body: some View 
        HStack 
            Text(fruit)
            Spacer()
            if fruit == selectedFruit 
                Image(systemName: "checkmark")
                    .foregroundColor(.accentColor)
            
           .onTapGesture 
                self.selectedFruit = self.fruit
            
    

Selección

SwiftUI actualmente no tiene una forma incorporada para seleccionar una fila de una lista y cambiar su apariencia en consecuencia. Pero en realidad estás muy cerca de tu respuesta. De hecho, su selección ya está funcionando, pero simplemente no se está utilizando de ninguna manera.

Para ilustrar, agregue la siguiente línea justo después ModuleCell(...) en tus ForEach:

.background(i == self.selectionKeeper ? Color.red : nil)

En otras palabras, “Si mi fila actual (i) coincide con el valor almacenado en selectionKeeper, colorea la celda de rojo, de lo contrario, usa el color predeterminado. “Verás que a medida que tocas en diferentes filas, el color rojo sigue a tus toques, lo que muestra que la selección subyacente está cambiando de hecho.

Deselección

Si desea habilitar la deselección, puede pasar un Binding como su selección, y configúrelo en nil cuando se toca la fila actualmente seleccionada:

struct ModuleList: View {
    var modules: [Module] = []
    // this is new ------------------v
    @Binding var selectionKeeper: Int?
    var Action: () -> Void

    // this is new ----------------------------v
    init(list: [Module], selection: Binding, action: @escaping () -> Void) 

    ...

    func changeSelection(index: Int)
        if selectionKeeper != index 
            selectionKeeper = index
         else 
            selectionKeeper = nil
        
        self.Action()
    

Estado de deduplicación y separación de preocupaciones

En un nivel más estructural, realmente desea una única fuente de verdad cuando usa un marco de interfaz de usuario declarativo como SwiftUI, y para separar claramente su vista de su modelo. En la actualidad, tiene un estado duplicado: selectionKeeper en ModuleList y isSelected en Module ambos realizan un seguimiento de si se selecciona un módulo determinado.

Además, isSelected debería ser realmente una propiedad de su vista (ModuleCell), no de tu modelo (Module), porque tiene que ver con cómo aparece su vista, no con los datos intrínsecos de cada módulo.

Por lo tanto, tu ModuleCell debería verse algo como esto:

struct ModuleCell: View 
    var module: Module
    var isSelected: Bool // Added this
    var Action: () -> Void

    // Added this -------v
    init(module: Module, isSelected: Bool, action: @escaping () -> Void) 
        UITableViewCell.appearance().backgroundColor = .clear
        self.module = module
        self.isSelected = isSelected  // Added this
        self.Action = action
    

    var body: some View 
        Button(module.name, action: 
            self.Action()
        )
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
            .modifier(Constants.CellSelection(isSelected: isSelected))
            // Changed this ------------------------------^
    

Y tu ForEach se vería como

ForEach(0..

Aquí hay un enfoque más genérico, aún puede extender la respuesta según sus necesidades;

TLDR

https://gist.github.com/EnesKaraosman/d778cdabc98ca269b3d162896bea8aac


Detalle

struct SingleSelectionList: View 
    
    var items: [Item]
    @Binding var selectedItem: Item?
    var rowContent: (Item) -> Content
    
    var body: some View 
        List(items)  item in
            rowContent(item)
                .modifier(CheckmarkModifier(checked: item.id == self.selectedItem?.id))
                .contentShape(Rectangle())
                .onTapGesture 
                    self.selectedItem = item
                
        
    


struct CheckmarkModifier: ViewModifier 
    var checked: Bool = false
    func body(content: Content) -> some View 
        Group 
            if checked 
                ZStack(alignment: .trailing) 
                    content
                    Image(systemName: "checkmark")
                        .resizable()
                        .frame(width: 20, height: 20)
                        .foregroundColor(.green)
                        .shadow(radius: 1)
                
             else 
                content
            
        
    

Y para demostrar;

struct PlaygroundView: View 
    
    struct Koko: Identifiable 
        let id = UUID().uuidString
        var name: String
    
    
    var mock = Array(0...10).map  Koko(name: "Item - ($0)") 
    @State var selectedItem: Koko?
    
    
    var body: some View 
        VStack 
            Text("Selected Item: (selectedItem?.name ?? "Select one")")
            Divider()
            SingleSelectionList(items: mock, selectedItem: $selectedItem)  (item) in
                HStack 
                    Text(item.name)
                    Spacer()
                
            
        
    
    

Resultado final
ingrese la descripción de la imagen aquí

Sección de Reseñas y Valoraciones

Recuerda dar visibilidad a este escrito si lograste el éxito.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *