From a723a634f0a1ca2732a92af5e113df4021366a72 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Mon, 22 Jun 2020 17:08:30 +0700 Subject: [PATCH 1/3] fix typo in comment --- Sources/Extensions/AppKitExtension.swift | 6 +++--- Sources/Extensions/UIKitExtension.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Extensions/AppKitExtension.swift b/Sources/Extensions/AppKitExtension.swift index 0570448..0fe8362 100644 --- a/Sources/Extensions/AppKitExtension.swift +++ b/Sources/Extensions/AppKitExtension.swift @@ -11,7 +11,7 @@ public extension NSTableView { /// - Parameters: /// - stagedChangeset: A staged set of changes. /// - animation: An option to animate the updates. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSTableView. @@ -43,7 +43,7 @@ public extension NSTableView { /// - deleteRowsAnimation: An option to animate the row deletion. /// - insertRowsAnimation: An option to animate the row insertion. /// - reloadRowsAnimation: An option to animate the row reload. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSTableView. @@ -108,7 +108,7 @@ public extension NSCollectionView { /// /// - Parameters: /// - stagedChangeset: A staged set of changes. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSCollectionView. diff --git a/Sources/Extensions/UIKitExtension.swift b/Sources/Extensions/UIKitExtension.swift index 206ac7c..9c75547 100644 --- a/Sources/Extensions/UIKitExtension.swift +++ b/Sources/Extensions/UIKitExtension.swift @@ -11,7 +11,7 @@ public extension UITableView { /// - Parameters: /// - stagedChangeset: A staged set of changes. /// - animation: An option to animate the updates. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UITableView. @@ -48,7 +48,7 @@ public extension UITableView { /// - deleteRowsAnimation: An option to animate the row deletion. /// - insertRowsAnimation: An option to animate the row insertion. /// - reloadRowsAnimation: An option to animate the row reload. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UITableView. @@ -133,7 +133,7 @@ public extension UICollectionView { /// /// - Parameters: /// - stagedChangeset: A staged set of changes. - /// - interrupt: A closure that takes an changeset as its argument and returns `true` if the animated + /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UICollectionView. From 75e463b018ff23a58bc4191756d103e5e556d6a1 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Mon, 22 Jun 2020 19:04:22 +0700 Subject: [PATCH 2/3] Added animations completionHandler to UIKit/AppKit Extensions --- Sources/Extensions/AppKitExtension.swift | 41 ++++++++++++++-- Sources/Extensions/UIKitExtension.swift | 59 ++++++++++++++++++++---- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/Sources/Extensions/AppKitExtension.swift b/Sources/Extensions/AppKitExtension.swift index 0fe8362..762f218 100644 --- a/Sources/Extensions/AppKitExtension.swift +++ b/Sources/Extensions/AppKitExtension.swift @@ -13,6 +13,8 @@ public extension NSTableView { /// - animation: An option to animate the updates. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSTableView. @@ -20,6 +22,7 @@ public extension NSTableView { using stagedChangeset: StagedChangeset, with animation: @autoclosure () -> NSTableView.AnimationOptions, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { reload( @@ -28,6 +31,7 @@ public extension NSTableView { insertRowsAnimation: animation(), reloadRowsAnimation: animation(), interrupt: interrupt, + completion: completion, setData: setData ) } @@ -45,6 +49,8 @@ public extension NSTableView { /// - reloadRowsAnimation: An option to animate the row reload. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSTableView. func reload( @@ -53,17 +59,22 @@ public extension NSTableView { insertRowsAnimation: @autoclosure () -> NSTableView.AnimationOptions, reloadRowsAnimation: @autoclosure () -> NSTableView.AnimationOptions, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { if case .none = window, let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } for changeset in stagedChangeset { if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } beginUpdates() @@ -95,6 +106,8 @@ public extension NSTableView { endUpdates() } + + completion?(true) } } @@ -110,26 +123,41 @@ public extension NSCollectionView { /// - stagedChangeset: A staged set of changes. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of NSCollectionView. func reload( using stagedChangeset: StagedChangeset, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { if case .none = window, let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } + let dispatchGroup: DispatchGroup? = completion != nil + ? DispatchGroup() + : nil + let completionHandler: ((Bool) -> Void)? = completion != nil + ? { _ in dispatchGroup!.leave() } + : nil + for changeset in stagedChangeset { if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } animator().performBatchUpdates({ setData(changeset.data) + dispatchGroup?.enter() if !changeset.elementDeleted.isEmpty { deleteItems(at: Set(changeset.elementDeleted.map { IndexPath(item: $0.element, section: $0.section) })) @@ -146,7 +174,10 @@ public extension NSCollectionView { for (source, target) in changeset.elementMoved { moveItem(at: IndexPath(item: source.element, section: source.section), to: IndexPath(item: target.element, section: target.section)) } - }) + }, completionHandler: completionHandler) + } + dispatchGroup?.notify(queue: .main) { + completion!(true) } } } diff --git a/Sources/Extensions/UIKitExtension.swift b/Sources/Extensions/UIKitExtension.swift index 9c75547..c3d6ae6 100644 --- a/Sources/Extensions/UIKitExtension.swift +++ b/Sources/Extensions/UIKitExtension.swift @@ -13,12 +13,15 @@ public extension UITableView { /// - animation: An option to animate the updates. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UITableView. func reload( using stagedChangeset: StagedChangeset, with animation: @autoclosure () -> RowAnimation, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { reload( @@ -30,6 +33,7 @@ public extension UITableView { insertRowsAnimation: animation(), reloadRowsAnimation: animation(), interrupt: interrupt, + completion: completion, setData: setData ) } @@ -50,6 +54,8 @@ public extension UITableView { /// - reloadRowsAnimation: An option to animate the row reload. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UITableView. func reload( @@ -61,21 +67,34 @@ public extension UITableView { insertRowsAnimation: @autoclosure () -> RowAnimation, reloadRowsAnimation: @autoclosure () -> RowAnimation, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { if case .none = window, let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } + let dispatchGroup: DispatchGroup? = completion != nil + ? DispatchGroup() + : nil + let completionHandler: ((Bool) -> Void)? = completion != nil + ? { _ in dispatchGroup!.leave() } + : nil + for changeset in stagedChangeset { if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } - _performBatchUpdates { + _performBatchUpdates({ setData(changeset.data) + dispatchGroup?.enter() if !changeset.sectionDeleted.isEmpty { deleteSections(IndexSet(changeset.sectionDeleted), with: deleteSectionsAnimation()) @@ -108,18 +127,22 @@ public extension UITableView { for (source, target) in changeset.elementMoved { moveRow(at: IndexPath(row: source.element, section: source.section), to: IndexPath(row: target.element, section: target.section)) } - } + }, completionHandler: completionHandler) + } + dispatchGroup?.notify(queue: .main) { + completion!(true) } } - private func _performBatchUpdates(_ updates: () -> Void) { + private func _performBatchUpdates(_ updates: () -> Void, completionHandler: ((Bool) -> Void)? = nil) { if #available(iOS 11.0, tvOS 11.0, *) { - performBatchUpdates(updates) + performBatchUpdates(updates, completion: completionHandler) } else { beginUpdates() updates() endUpdates() + completionHandler?(true) } } } @@ -135,26 +158,41 @@ public extension UICollectionView { /// - stagedChangeset: A staged set of changes. /// - interrupt: A closure that takes a changeset as its argument and returns `true` if the animated /// updates should be stopped and performed reloadData. Default is nil. + /// - completion: A closure that is called when the animated updates have finished. + /// The argument is` true` if the animation ran to completion before it stopped or `false` if it did not. /// - setData: A closure that takes the collection as a parameter. /// The collection should be set to data-source of UICollectionView. func reload( using stagedChangeset: StagedChangeset, interrupt: ((Changeset) -> Bool)? = nil, + completion: ((Bool) -> Void)? = nil, setData: (C) -> Void ) { if case .none = window, let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } + let dispatchGroup: DispatchGroup? = completion != nil + ? DispatchGroup() + : nil + let completionHandler: ((Bool) -> Void)? = completion != nil + ? { _ in dispatchGroup!.leave() } + : nil + for changeset in stagedChangeset { if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data { setData(data) - return reloadData() + reloadData() + completion?(false) + return } performBatchUpdates({ setData(changeset.data) + dispatchGroup?.enter() if !changeset.sectionDeleted.isEmpty { deleteSections(IndexSet(changeset.sectionDeleted)) @@ -187,7 +225,10 @@ public extension UICollectionView { for (source, target) in changeset.elementMoved { moveItem(at: IndexPath(item: source.element, section: source.section), to: IndexPath(item: target.element, section: target.section)) } - }) + }, completion: completionHandler) + } + dispatchGroup?.notify(queue: .main) { + completion!(true) } } } From a74c6362988a1e8d244256f76d69c02fb172c8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Gregoric=CC=8C?= Date: Wed, 31 May 2023 10:41:28 +0200 Subject: [PATCH 3/3] Added NSCollectionView sections updates in AppKit extension. --- Sources/Extensions/AppKitExtension.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/Extensions/AppKitExtension.swift b/Sources/Extensions/AppKitExtension.swift index 762f218..bb4df19 100644 --- a/Sources/Extensions/AppKitExtension.swift +++ b/Sources/Extensions/AppKitExtension.swift @@ -159,6 +159,22 @@ public extension NSCollectionView { setData(changeset.data) dispatchGroup?.enter() + if !changeset.sectionDeleted.isEmpty { + deleteSections(IndexSet(changeset.sectionDeleted)) + } + + if !changeset.sectionInserted.isEmpty { + insertSections(IndexSet(changeset.sectionInserted)) + } + + if !changeset.sectionUpdated.isEmpty { + reloadSections(IndexSet(changeset.sectionUpdated)) + } + + for (source, target) in changeset.sectionMoved { + moveSection(source, toSection: target) + } + if !changeset.elementDeleted.isEmpty { deleteItems(at: Set(changeset.elementDeleted.map { IndexPath(item: $0.element, section: $0.section) })) }