Search code examples
swiftuiswiftui-list

Is there a better way to delay a variable assignment to delay a sheet presentation


I want to delay the sheet presentation via shouldPresentEditExpense. The reason is to show a selection animation of a cell.

The selectable effect takes place via ViewModifier.

overlay { isSelectedItem == item) ? Color.black.opacity(0.2) : Color.clear }

I am pretty sure that there is a better way to achieve this effect.

The view

VStack(spacing: 15) {
    ForEach(expensesVM.lastExpenses(limit: Constants.maximumNumberOfExpenses)) { expense in
        ExpenseCellView(expense: expense)
            .selectableCell(selectedItem: $selectedItem)
            .listRowSeparator(.hidden)
            .onTapGesture {
                selectedItem = expense
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    shouldPresentEditExpense = true
                }
            }
            .animation(.easeOut, value: selectedItem)
    }
}

The ViewModifier

//MARK: - Selectable Cell ViewModifier

struct ExpenseSelectableCell: ViewModifier {
    @Binding var selectedItem: Expense?
    var expense: Expense

    func body(content: Content) -> some View {
        content
            .overlay {
                Color.black.opacity(isSelected() ? 0.2 : 0)
            }
    }

    func isSelected() -> Bool {
        guard let selectedItem else { return false }
        return selectedItem.id == expense.id
    }
}

extension ExpenseCellView {
    func selectableCell(selectedItem: Binding<Expense?>) -> some View {
        self.modifier(ExpenseSelectableCell(selectedItem: selectedItem, expense: self.expense))
    }
}

Solution

  • Use withAnimation to animate the selection. You can pass a completionHandler to that.

    .onTapGesture {
        withAnimation {
            selectedItem = expense
        } completion: {
            shouldPresentEditExpense = true
        }
    }