Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(MultipleSelection): Improve animations #764

Merged
merged 6 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Mail/Components/CheckboxView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import SwiftUI

struct CheckboxView: View {
@AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor

let isSelected: Bool
let size: CGFloat

Expand All @@ -33,14 +34,15 @@ struct CheckboxView: View {
var body: some View {
ZStack {
Circle()
.strokeBorder(Color.accentColor, lineWidth: 2)
.strokeBorder(Color.accentColor, lineWidth: isSelected ? 0 : 2)
.background(Circle().fill(isSelected ? Color.accentColor : Color.clear))
.frame(width: size, height: size)
MailResourcesAsset.check.swiftUIImage
.foregroundColor(accentColor.onAccent.swiftUIColor)
.frame(height: UIConstants.checkmarkSize)
.opacity(isSelected ? 1 : 0)
}
.animation(nil, value: isSelected)
}
}

Expand Down
2 changes: 2 additions & 0 deletions Mail/Components/SelectionBackground.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct SelectionBackground: View {

let selectionType: SelectionBackgroundKind
var paddingLeading: CGFloat = 8
var withAnimation = true

var body: some View {
Rectangle()
Expand All @@ -68,6 +69,7 @@ struct SelectionBackground: View {
.padding(.leading, paddingLeading)
.padding(.vertical, selectionType.verticalPadding)
.opacity(selectionType.opacity)
.animation(withAnimation ? .default : nil, value: selectionType.opacity)
}
}

Expand Down
49 changes: 25 additions & 24 deletions Mail/Components/ThreadCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,8 @@ import MailResources
import SwiftUI

extension Animation {
static func threadListCheckbox(isMultipleSelectionEnabled isEnabled: Bool) -> Animation {
.default.delay(isEnabled ? 0.5 : 0)
}

static func threadListSlide(density: ThreadDensity, isMultipleSelectionEnabled isEnabled: Bool) -> Animation {
if density == .large {
return .default
}
return .default.speed(2).delay(isEnabled ? 0 : 0.22)
static var threadListSlide: Animation {
.default.speed(2)
}
}

Expand Down Expand Up @@ -81,6 +74,10 @@ struct ThreadCellDataHolder {

struct ThreadCell: View {
@EnvironmentObject private var mailboxManager: MailboxManager

/// With normal or compact density, the checkbox should appear and disappear at different times of the cell offset.
@State private var shouldDisplayCheckbox = false

let thread: Thread

let dataHolder: ThreadCellDataHolder
Expand All @@ -104,12 +101,7 @@ struct ThreadCell: View {
return label
}

init(
thread: Thread,
density: ThreadDensity,
isMultipleSelectionEnabled: Bool = false,
isSelected: Bool = false
) {
init(thread: Thread, density: ThreadDensity, isMultipleSelectionEnabled: Bool = false, isSelected: Bool = false) {
self.thread = thread

dataHolder = ThreadCellDataHolder(thread: thread)
Expand All @@ -124,10 +116,6 @@ struct ThreadCell: View {
var body: some View {
HStack(spacing: 8) {
UnreadIndicatorView(hidden: !thread.hasUnseenMessages)
.animation(
.threadListSlide(density: density, isMultipleSelectionEnabled: isMultipleSelectionEnabled),
value: isMultipleSelectionEnabled
)
.accessibilityLabel(additionalAccessibilityLabel)
.accessibilityHidden(additionalAccessibilityLabel.isEmpty)

Expand All @@ -137,14 +125,13 @@ struct ThreadCell: View {
AvatarView(avatarDisplayable: recipient, size: 40)
CheckboxView(isSelected: isSelected, density: density)
.opacity(isSelected ? 1 : 0)
.animation(nil, value: isSelected)
}
.accessibility(hidden: true)
} else if isMultipleSelectionEnabled {
CheckboxView(isSelected: isSelected, density: density)
.animation(
.threadListCheckbox(isMultipleSelectionEnabled: isMultipleSelectionEnabled),
value: isMultipleSelectionEnabled
)
.opacity(shouldDisplayCheckbox ? 1 : 0)
.animation(.default.speed(1.5), value: shouldDisplayCheckbox)
}
}
.padding(.trailing, 4)
Expand All @@ -159,7 +146,7 @@ struct ThreadCell: View {
}
}
.animation(
.threadListSlide(density: density, isMultipleSelectionEnabled: isMultipleSelectionEnabled),
isMultipleSelectionEnabled ? .threadListSlide : .threadListSlide.delay(UIConstants.checkboxDisappearOffsetDelay),
value: isMultipleSelectionEnabled
)
}
Expand All @@ -168,6 +155,20 @@ struct ThreadCell: View {
.padding(.vertical, density.cellVerticalPadding)
.clipped()
.accessibilityElement(children: .combine)
.onChange(of: isMultipleSelectionEnabled) { isEnabled in
guard density != .large else { return }

withAnimation {
if isEnabled {
// We should wait a bit before showing the checkbox
DispatchQueue.main.asyncAfter(deadline: .now() + UIConstants.checkboxAppearDelay) {
self.shouldDisplayCheckbox = true
}
} else {
self.shouldDisplayCheckbox = false
}
}
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion Mail/Utils/FloatingActionButtonModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ struct FloatingActionButtonModifier: ViewModifier {
}

extension View {
func floatingActionButton(isEnabled: Bool = true, icon: MailResourcesImages, title: String, action: @escaping () -> Void) -> some View {
func floatingActionButton(isEnabled: Bool = true, icon: MailResourcesImages, title: String,
action: @escaping () -> Void) -> some View {
modifier(FloatingActionButtonModifier(isEnabled: isEnabled, icon: icon, title: title, action: action))
}
}
21 changes: 11 additions & 10 deletions Mail/Views/Thread List/ThreadListCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ import SwiftUI
struct ThreadListCell: View {
@EnvironmentObject var splitViewManager: SplitViewManager

let thread: Thread

let viewModel: ThreadListViewModel
let multipleSelectionViewModel: ThreadListMultipleSelectionViewModel
@ObservedObject var multipleSelectionViewModel: ThreadListMultipleSelectionViewModel

@Binding var editedMessageDraft: Draft?

let thread: Thread

let threadDensity: ThreadDensity

let isSelected: Bool
let isMultiSelected: Bool

@Binding var editedMessageDraft: Draft?

private var selectionType: SelectionBackgroundKind {
if multipleSelectionViewModel.isEnabled {
return isMultiSelected ? .multiple : .none
Expand All @@ -52,9 +52,10 @@ struct ThreadListCell: View {
isMultipleSelectionEnabled: multipleSelectionViewModel.isEnabled,
isSelected: isMultiSelected
)
.background(SelectionBackground(selectionType: selectionType, paddingLeading: 4))
.background(SelectionBackground(selectionType: selectionType, paddingLeading: 4, withAnimation: false))
.contentShape(Rectangle())
.onTapGesture { didTapCell() }
.onLongPressGesture(minimumDuration: 0.3) { didLongPressCell() }
.onLongPressGesture { didLongPressCell() }
.swipeActions(thread: thread, viewModel: viewModel, multipleSelectionViewModel: multipleSelectionViewModel)
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowSeparator(.hidden)
Expand Down Expand Up @@ -100,15 +101,15 @@ struct ThreadListCell: View {
struct ThreadListCell_Previews: PreviewProvider {
static var previews: some View {
ThreadListCell(
thread: PreviewHelper.sampleThread,
viewModel: ThreadListViewModel(mailboxManager: PreviewHelper.sampleMailboxManager,
folder: PreviewHelper.sampleFolder,
isCompact: false),
multipleSelectionViewModel: ThreadListMultipleSelectionViewModel(mailboxManager: PreviewHelper.sampleMailboxManager),
editedMessageDraft: .constant(nil),
thread: PreviewHelper.sampleThread,
threadDensity: .large,
isSelected: false,
isMultiSelected: false,
editedMessageDraft: .constant(nil)
isMultiSelected: false
)
}
}
8 changes: 4 additions & 4 deletions Mail/Views/Thread List/ThreadListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ struct ThreadListView: View {
ForEach(viewModel.sections) { section in
Section {
ForEach(section.threads) { thread in
ThreadListCell(thread: thread,
viewModel: viewModel,
ThreadListCell(viewModel: viewModel,
multipleSelectionViewModel: multipleSelectionViewModel,
editedMessageDraft: $editedMessageDraft,
thread: thread,
threadDensity: threadDensity,
isSelected: viewModel.selectedThread?.uid == thread.uid,
isMultiSelected: multipleSelectionViewModel.selectedItems
.contains { $0.id == thread.id },
editedMessageDraft: $editedMessageDraft)
.contains { $0.id == thread.id })
.id(thread.id)
}
} header: {
Expand Down
3 changes: 3 additions & 0 deletions MailCore/UI/UIConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public enum UIConstants {
public static let checkmarkSize: CGFloat = 14
public static let checkboxLargeSize: CGFloat = 40

public static let checkboxAppearDelay = 0.2
public static let checkboxDisappearOffsetDelay = 0.35

public static let buttonsRadius: CGFloat = 16
public static let buttonsIconSize: CGFloat = 16

Expand Down