List Section with Swipe Action - glitches

Overview

  • I have an iOS project where I have a list with sections.
  • Each cell in the section can be swiped to have some action

What needs to be done

When swipe button is pressed the cell needs to move from one section to the other without a UI glitch.

Problem

When I press the swipe action button, there is a UI glitch and some warnings are thrown.

UICollectionView internal inconsistency: unexpected removal of the current swipe occurrence's mask view. Please file a bug against UICollectionView. Reusable view: <SwiftUI.ListCollectionViewCell: 0x10700c200; baseClass = UICollectionViewListCell; frame = (16 40.3333; 370 52); hidden = YES; layer = <CALayer: 0x600000c12fa0>>; Collection view: <SwiftUI.UpdateCoalescingCollectionView: 0x106820800; baseClass = UICollectionView; frame = (0 0; 402 874); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000c13330>; backgroundColor = <UIDynamicSystemColor: 0x60000173a9c0; name = systemGroupedBackgroundColor>; layer = <CALayer: 0x600000c3a070>; contentOffset: {0, -62}; contentSize: {402, 229}; adjustedContentInset: {62, 0, 34, 0}; layout: <UICollectionViewCompositionalLayout: 0x10590edb0>; dataSource: <_TtGC7SwiftUI31UICollectionViewListCoordinatorGVS_28CollectionViewListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___: 0x106822a00>>; Swipe occurrence: <UISwipeOccurrence: 0x103c161f0; indexPath: <NSIndexPath: 0xab1f048608f3828b> {length = 2, path = 0 - 0}, state: .triggered, direction: left, offset: 0>

Test environment:

  • Xcode 26.0.1 (17A400)
  • iOS 26 Simulator (iPhone 17 Pro)

Feedback filed:

  • FB20890361

Code

I have pasted below the minimum reproducible code

ContentView

import SwiftUI

struct ContentView: View {
    @State private var dataStore = DataStore()
    
    var body: some View {
        List {
            ToDoSection(status: .notStarted, toDos: notStartedToDos)
            ToDoSection(status: .inProgress, toDos: inProgressToDos)
            ToDoSection(status: .completed, toDos: completedTodos)
        }
    }
    
    var notStartedToDos: [Binding<ToDoItem>] {
        $dataStore.todos.filter { $0.wrappedValue.status == .notStarted }
    }
    
    var inProgressToDos: [Binding<ToDoItem>] {
        $dataStore.todos.filter { $0.wrappedValue.status == .inProgress }
    }
    
    var completedTodos: [Binding<ToDoItem>] {
        $dataStore.todos.filter { $0.wrappedValue.status == .completed }
    }
}

ToDoSection

import SwiftUI

struct ToDoSection: View {
    let status: ToDoItem.Status
    let toDos: [Binding<ToDoItem>]
    
    var body: some View {
        if !toDos.isEmpty {
            Section(status.title) {
                ForEach(toDos) { toDo in
                    Text(toDo.wrappedValue.title)
                        .swipeActions(edge: .trailing) {
                            if status == .notStarted {
                                Button("Start") {
                                    toDo.wrappedValue.status = .inProgress
                                }
                            }
                            
                            if status != .completed {
                                Button("Complete") {
                                    toDo.wrappedValue.status = .completed
                                }
                                
                                Button("Move back") {
                                    toDo.wrappedValue.status = .notStarted
                                }
                            }
                        }
                }
            }
        }
    }
}

ToDoItem

import Foundation

struct ToDoItem: Identifiable {
    let id: UUID
    let title: String
    var status: Status
}

extension ToDoItem {
    enum Status: Equatable {
        case notStarted
        case inProgress
        case completed
        
        var title: String {
            switch self {
            case .notStarted:
                "Not Started"
            case .inProgress:
                "In Progress"
            case .completed:
                "Completed"
            }
        }
    }
}

DataStore

import Foundation

@Observable
class DataStore {
    var todos: [ToDoItem]
    
    init() {
        todos = [
            ToDoItem(id: UUID(), title: "aaa", status: .notStarted),
            ToDoItem(id: UUID(), title: "bbb", status: .notStarted),
            ToDoItem(id: UUID(), title: "ccc", status: .notStarted)
        ]
    }
}

Following Gif shows the UI glitch

Possibly the same issue here. Sections not needed to reproduce issue with SwiftUI List in iOS 26, 26.0.1, and 26.1.

Filed back in mid-August FB19602925

Minimal code to repro:

struct Item: Identifiable, Hashable {
    var id: Int
    var title: String
}

struct ContentView: View {
    @State var items: [Item] = [
        Item(id: 0, title: "One"),
        Item(id: 1, title: "Two"),
        Item(id: 2, title: "Three"),
    ]

    var body: some View {
        List(items) { item in
            Text(item.title)
                .swipeActions(edge: .trailing) {
                    Button("Pin", systemImage: "pin") {
                        pinItem(item)
                    }
                    .tint(.green)

                    Button("Copy", systemImage: "document.on.document") {
                        duplicateItem(item)
                    }
                    .tint(.blue)
                }
        }
        .listStyle(.plain)
    }

    func pinItem(_ item: Item) {
        if let idx = items.firstIndex(of: item) {
            items.move(fromOffsets: IndexSet(integer: idx), toOffset: 0)
        }
    }

    func duplicateItem(_ item: Item) {
        let newId = (items.map(\.id).max() ?? 0) + 1
        items.insert(Item(id: newId, title: "Copy of \(item.title)"), at: 0)
    }
}

Thanks @j_salling, seems related.

I have filed a feedback as well.

It would be great to have this fixed at the earliest as possible as it affects production apps.

List Section with Swipe Action - glitches
 
 
Q