From 782eb8037ba3b1ecd7a743ac41e98a8c1066319d Mon Sep 17 00:00:00 2001 From: Ivan Zinovyev Date: Thu, 7 Sep 2017 22:59:19 +0300 Subject: [PATCH 1/3] PaginationTableViewWrapper: Fix empty state, Add retry method, Remove infinite scroll for single cursor, Fix visual bug related to placeholder after reload --- .../PaginationTableViewWrapper.swift | 36 +++++++++++++------ .../Pagination/PaginationViewModel.swift | 26 +++++++++++--- Tests/CursorTests.swift | 4 +-- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/Sources/Classes/Pagination/PaginationTableViewWrapper.swift b/Sources/Classes/Pagination/PaginationTableViewWrapper.swift index a879914a..e0b2e591 100644 --- a/Sources/Classes/Pagination/PaginationTableViewWrapper.swift +++ b/Sources/Classes/Pagination/PaginationTableViewWrapper.swift @@ -142,6 +142,11 @@ where Delegate.Cursor == Cursor { paginationViewModel.load(.reload) } + /// Method acts like reload, but shows initial loading view after being invoked. + public func retry() { + paginationViewModel.load(.retry) + } + /// Method that enables placeholders animation due pull-to-refresh interaction. /// /// - Parameter scrollObservable: Observable that emits content offset as CGPoint. @@ -204,7 +209,9 @@ where Delegate.Cursor == Cursor { tableView.support.refreshControl?.endRefreshing() - addInfiniteScroll() + if !(cursor is SingleLoadCursor) { + addInfiniteScroll() + } } else if case .loadingMore = afterState { delegate?.paginationWrapper(wrapper: self, didLoad: newItems, usingCursor: cursor) @@ -214,15 +221,15 @@ where Delegate.Cursor == Cursor { private func onErrorState(error: Error, afterState: PaginationViewModel.State) { if case .loading = afterState { - enterPlaceholderState() + defer { + tableView.support.refreshControl?.endRefreshing() + } guard let errorView = delegate?.errorPlaceholder(forPaginationWrapper: self, forError: error) else { return } - preparePlaceholderView(errorView) - - currentPlaceholderView = errorView + replacePlaceholderViewIfNeeded(with: errorView) } else if case .loadingMore = afterState { removeInfiniteScroll() @@ -244,15 +251,23 @@ where Delegate.Cursor == Cursor { } private func onEmptyState() { - enterPlaceholderState() - + defer { + tableView.support.refreshControl?.endRefreshing() + } guard let emptyView = delegate?.emptyPlaceholder(forPaginationWrapper: self) else { return } + replacePlaceholderViewIfNeeded(with: emptyView) + } - preparePlaceholderView(emptyView) - - currentPlaceholderView = emptyView + private func replacePlaceholderViewIfNeeded(with view: UIView) { + // don't update placeholder view if previous placeholder is the same one + if currentPlaceholderView === view { + return + } + enterPlaceholderState() + preparePlaceholderView(view) + currentPlaceholderView = view } // MARK: - private stuff @@ -322,7 +337,6 @@ where Delegate.Cursor == Cursor { } private func enterPlaceholderState() { - tableView.support.refreshControl?.endRefreshing() tableView.isUserInteractionEnabled = true removeCurrentPlaceholderView() diff --git a/Sources/Classes/Pagination/PaginationViewModel.swift b/Sources/Classes/Pagination/PaginationViewModel.swift index 59d3fa03..d5de6da9 100644 --- a/Sources/Classes/Pagination/PaginationViewModel.swift +++ b/Sources/Classes/Pagination/PaginationViewModel.swift @@ -63,7 +63,11 @@ public final class PaginationViewModel { /// - next: load next batch of items. public enum LoadType { + /// pull-to-refresh case reload + /// retry button inside placeholder + case retry + case next } @@ -94,10 +98,9 @@ public final class PaginationViewModel { public func load(_ loadType: LoadType) { switch loadType { case .reload: - currentRequest?.dispose() - cursor = cursor.reset() - - internalState.value = .loading(after: internalState.value) + reload() + case .retry: + reload(isRetry: true) case .next: if case .exhausted(_) = internalState.value { fatalError("You shouldn't call load(.next) after got .exhausted state!") @@ -118,6 +121,11 @@ public final class PaginationViewModel { } private func onGot(newItems: [C.Element], using cursor: C) { + if newItems.isEmpty { + internalState.value = .empty + return + } + internalState.value = .results(newItems: newItems, inCursor: cursor, after: internalState.value) if cursor.exhausted { @@ -138,4 +146,14 @@ public final class PaginationViewModel { } } + private func reload(isRetry: Bool = false) { + currentRequest?.dispose() + cursor = cursor.reset() + + if isRetry { + internalState.value = .initial + } + internalState.value = .loading(after: internalState.value) + } + } diff --git a/Tests/CursorTests.swift b/Tests/CursorTests.swift index fdb6e08c..cba3e0bd 100644 --- a/Tests/CursorTests.swift +++ b/Tests/CursorTests.swift @@ -94,9 +94,9 @@ class CursorTests: XCTestCase { XCTAssertEqual(loadedItems.count, 40) cursorExpectation.fulfill() - }) { error in + }, onError: { error in XCTFail(error.localizedDescription) - } + }) .addDisposableTo(disposeBag) waitForExpectations(timeout: 10, handler: nil) From b7d6925b988be10060d1baea47a0020a36241f77 Mon Sep 17 00:00:00 2001 From: Ivan Zinovyev Date: Thu, 7 Sep 2017 23:14:09 +0300 Subject: [PATCH 2/3] PaginationTableViewWrapper: Add placeholder view for initial loading state --- Sources/Structures/Views/AnyLoadingIndicator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Structures/Views/AnyLoadingIndicator.swift b/Sources/Structures/Views/AnyLoadingIndicator.swift index 3e5fbbcb..ebdf7422 100644 --- a/Sources/Structures/Views/AnyLoadingIndicator.swift +++ b/Sources/Structures/Views/AnyLoadingIndicator.swift @@ -31,8 +31,8 @@ public struct AnyLoadingIndicator: Animatable { /// Initializer with indicator that should be wrapped. /// /// - Parameter _: indicator for wrapping. - public init (_ base: Indicator) where Indicator: LoadingIndicator { - self.internalView = base.view + public init (_ base: Indicator, backgroundView: UIView? = nil) where Indicator: LoadingIndicator { + self.internalView = backgroundView ?? base.view self.animatableView = base.view } From 45d93e303f8736ad9f6313a92629cf7a1cbcdd1e Mon Sep 17 00:00:00 2001 From: Ivan Zinovyev Date: Thu, 7 Sep 2017 23:20:25 +0300 Subject: [PATCH 3/3] Update version --- LeadKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LeadKit.podspec b/LeadKit.podspec index 63b59d06..389b855d 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.5.4" + s.version = "0.5.5" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0"