UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Displaying a dynamic list of Sliders

Forums > SwiftUI

Hi, hopefully someone can point me in the right direction with solving this problem.

I want to display a table with each row containing some text and integer value. This is all straight forward. What I want now, is a Slider, in each row that will allow the user to control the integer value.

My data for the row's are in custom classes, having properties for a UUID and the text and integer values. I can get to the data in an Array form for easy display using List but I can also access elements directly using a dictionary.

My problem is, the number of rows is dynamic, the user can add/remove them. How do I setup the value of the Slider and have it reference a particular row's data.

In my mind i would like something like @State private var rows: [UUID,Integer]

and then in the Slider be able to get the UUID for the specific data row and updates its Integer . . .

Any thoughts or idea's how to solve this one. Thanks

4      

UPDATE: I have found a solution here The trick is to use Binding(...) directly in the value property of the Slider. This gives access to the get and set, within the scope of each row, enabling access to my Row data. I can then update my properties.

The only issue at the moment is the view is not being re-drawn because no State has changed. Have a hacky boolean flag to force it but will keep looking for a cleaner solution.

4      

Have you tried keeping it simple.....?

struct Line : View {
        @State private var number = 0.0
        @State private var name = ""
        var body: some View {
            HStack {
                Text("name \(name)")
                Text("num \(number)")
                Slider(value: $number, in: 0...1){_ in 
                    // do something 
                }
            }
        }
    }

Don't forget to make it identifiable, as I know you already know, but anyone else reading this might not!

Best of luck

3      

Thanks for the reply @markbooth5457. The problem i had is that I have more the one slider and related $number to display. The number of numbers is dynamic, I don't know how many will be required in advance of displaying the view so defining individual @State for each was not option.

The trick I have learned is replacing the traditional $number with Binding.

So rather than $number for the Slider value param I have something like this . . .

Binding(
    get: {
        nodes.first { (n) -> Bool in
            n.id == currentRowId
            }?.value ?? 0.0
},
    set: {(newValue) in
        self.nodes.first { (n) -> Bool in
            n.id == currentRowId
            }?.value = newValue
        self.update.toggle()
}

nodes being my Array of custom data which i loop over with a List in the normal way. I can then identify the record i want to update using an Id value in the object.

3      

Not sure I understand this any better, I'm afraid. Do you mean that each line has more than one number? Has the Binding code sorted out your problem? If so I will try to understand it better so that I can add it to the old toolbox ;-) Thanks

3      

Sure, I'm probaby not explaining very well. I have a class like this . .

class Node {
   var id: UUID
   var sortPosition: Int
   var name: String
   var weight: Double
}

The user can add as many of these as they like to the nodes array. Typically 10 to 30 (reasonable max 100), these can be added and removed at any time. My view is displaying these in a List. With each cell/row showing the name, weight and Slider to update the weight value for the specific node. The slider is to control the weight value. So on a typical view i will have 10 to 30 of these Sliders.

The Binding code has sorted the issue as it allows me to access and update the weight value in the appropriate Node in my array of nodes and does not require potentially 100+ @State var's

:) probably should have started with this explanation . . .

3      

Now I seeeeee!

I was thinking of something else, but the following is something similar - perhaps the key part is using a struct instead of a class for the node:

//
//  ContentView.swift
//

import SwiftUI

struct MyNode : Identifiable {
    var id: UUID
    var sortPosition: Int
    var name : String
    var weight: Double
    init(sortPosition: Int, name: String, weight: Double ) {
        self.id = UUID()
        self.sortPosition = sortPosition
        self.name = name
        self.weight = weight

    }
}

struct MyNodeView : View {
    @State var myNode : MyNode
    var body: some View {
        VStack {
            Text("Name: \(myNode.name)")
            HStack {
                Text("Weight: \(myNode.weight)")
                Slider(value: $myNode.weight, in: 100 ... 400)
            }
        }
    }

}

struct ContentView: View {
    @State private var nodes : [MyNode] = [
        MyNode(sortPosition: 23, name: "John", weight: 128.5),
        MyNode(sortPosition: 24, name: "Paul", weight: 128.5),
        MyNode(sortPosition: 25, name: "George", weight: 128.5),
        MyNode(sortPosition: 26, name: "Ringo", weight: 128.5)

    ]
    var body: some View {
        List(nodes, rowContent: MyNodeView.init)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Not sure if this helps with the sortPosition, though. Not quite sure what you meant to do with that.

But it only uses one State var for any number of lines, so maybe it fits?

3      

oh I like that solution! Its much cleaner. I will try that out.

Many thanks

3      

I hope it is useful. Binding is the old way, and probably underlies how the $ works, but the $ is much cleaner once we manage to work out how it fits in.

All best wishes Mark

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.