BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

How to let users move rows in a list

Paul Hudson    @twostraws   

Updated for Xcode 14.1

SwiftUI provides two ways to let us move items in a list: a simple way supported on iOS 16.0 or later, and a more advanced way that works on older iOS versions too. Regardless of which approach you choose, you can also selectively disable moving a row using the moveDisabled() modifier.

The simple approach to moving works great if you’re just moving your item around in an array, without adding any additional logic. To use it, use a data binding with your list and pass in the editActions parameter, like this:

struct ContentView: View {
    @State private var users = ["Glenn", "Malcolm", "Nicola", "Terri"]

    var body: some View {
        NavigationStack {
            List($users, id: \.self, editActions: .move) { $user in
                Text(user)
            }
        }
    }
}

Download this as an Xcode project

That immediately lets users drag the list rows around, and the users array will be updated as they do so. If you want to add swipe to delete as well, use .all rather than just .move.

If you want to disable movement for one row, use moveDisabled() with whatever criteria you have. For example, we could say that Glenn must always appear first in our list like this:

struct ContentView: View {
    @State private var users = ["Glenn", "Malcolm", "Nicola", "Terri"]

    var body: some View {
        NavigationStack {
            List($users, id: \.self, editActions: .move) { $user in
                Text(user)
                    .moveDisabled(user == "Glenn")
            }
        }
    }
}

Download this as an Xcode project

For the more complex approach to moving, we can attach an onMove(perform:) modifier to a ForEach inside a list, and have it call a method of our choosing when a move operation happens. That method needs to accept a source IndexSet and a destination Int, like this:

func move(from source: IndexSet, to destination: Int) {
    // move the data here
}

When moving several items it’s always a good idea to move the later ones first so that you avoid moving other items and getting your indexes confused. Fortunately, Swift’s sequences have a built-in way to move index sets for us, so we can just pass the parameters along and have it work correctly.

As an example, we could create a ContentView struct that sets up an array of three username strings, and asks SwiftUI to move them around calling a move() method. In order to activate moving – i.e., to make the drag handles appear – it also adds an edit button to the navigation stack so the user can toggle editing mode.

Here’s the code:

struct ContentView: View {
    @State private var users = ["Paul", "Taylor", "Adele"]

    var body: some View {
        NavigationStack {
            List {
                ForEach(users, id: \.self) { user in
                    Text(user)
                }
                .onMove(perform: move)
            }
            .toolbar {
                EditButton()
            }
        }
    }

    func move(from source: IndexSet, to destination: Int) {
        users.move(fromOffsets: source, toOffset: destination)
    }
}

Download this as an Xcode project

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

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

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.5/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.