mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge pull request #5781 from vector-im/andy/5780_indicator_context
Replace user indicator presenting view controller with context
This commit is contained in:
commit
9b922a3bc6
8 changed files with 116 additions and 40 deletions
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
/// The presentation context is used by `UserIndicatorViewPresentable`s to display content
|
||||
/// on the screen and it serves two primary purposes:
|
||||
///
|
||||
/// - abstraction on top of UIKit (passing context instead of view controllers)
|
||||
/// - immutable context passed at init with variable presenting view controller
|
||||
/// (e.g. depending on collapsed / uncollapsed iPad presentation that changes
|
||||
/// at runtime)
|
||||
public protocol UserIndicatorPresentationContext {
|
||||
var indicatorPresentingViewController: UIViewController? { get }
|
||||
}
|
||||
|
||||
/// A simple implementation of `UserIndicatorPresentationContext` that uses a weak reference
|
||||
/// to the passed-in view controller as the presentation context.
|
||||
public class StaticUserIndicatorPresentationContext: UserIndicatorPresentationContext {
|
||||
// The presenting view controller will be the parent of the user indicator,
|
||||
// and the indicator holds a strong reference to the context, so the view controller
|
||||
// must be decleared `weak` to avoid a retain cycle
|
||||
public private(set) weak var indicatorPresentingViewController: UIViewController?
|
||||
|
||||
public init(viewController: UIViewController) {
|
||||
self.indicatorPresentingViewController = viewController
|
||||
}
|
||||
}
|
|
@ -47,26 +47,19 @@ protocol UserIndicatorTypePresenterProtocol {
|
|||
}
|
||||
|
||||
class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
|
||||
private weak var viewController: UIViewController?
|
||||
|
||||
// In the existing app architecture it is often view controllers which instantiate
|
||||
// various presenters (errors, alerts ... ) and present on self. Since the presenting view controller
|
||||
// needs to be passed on init, it must be declared as weak, otherwise a retain cycle would occur.
|
||||
private var presentingViewController: UIViewController {
|
||||
guard let viewController = viewController else {
|
||||
MXLog.error("[UserIndicatorTypePresenter]: Presenting view controller is not available")
|
||||
return UIViewController()
|
||||
}
|
||||
return viewController
|
||||
}
|
||||
|
||||
private let presentationContext: UserIndicatorPresentationContext
|
||||
let queue: UserIndicatorQueue
|
||||
|
||||
init(presentingViewController: UIViewController) {
|
||||
self.viewController = presentingViewController
|
||||
init(presentationContext: UserIndicatorPresentationContext) {
|
||||
self.presentationContext = presentationContext
|
||||
self.queue = UserIndicatorQueue()
|
||||
}
|
||||
|
||||
convenience init(presentingViewController: UIViewController) {
|
||||
let context = StaticUserIndicatorPresentationContext(viewController: presentingViewController)
|
||||
self.init(presentationContext: context)
|
||||
}
|
||||
|
||||
func present(_ type: UserIndicatorType) -> UserIndicator {
|
||||
let request = userIndicatorRequest(for: type)
|
||||
return queue.add(request)
|
||||
|
@ -91,7 +84,7 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
|
|||
style: .loading,
|
||||
label: label
|
||||
),
|
||||
presentingViewController: presentingViewController
|
||||
presentationContext: presentationContext
|
||||
)
|
||||
return UserIndicatorRequest(
|
||||
presenter: presenter,
|
||||
|
@ -102,7 +95,7 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
|
|||
private func fullScreenLoadingRequest(label: String) -> UserIndicatorRequest {
|
||||
let presenter = FullscreenLoadingViewPresenter(
|
||||
label: label,
|
||||
presentingViewController: presentingViewController
|
||||
presentationContext: presentationContext
|
||||
)
|
||||
return UserIndicatorRequest(
|
||||
presenter: presenter,
|
||||
|
@ -116,7 +109,7 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
|
|||
style: .success,
|
||||
label: label
|
||||
),
|
||||
presentingViewController: presentingViewController
|
||||
presentationContext: presentationContext
|
||||
)
|
||||
return UserIndicatorRequest(
|
||||
presenter: presenter,
|
||||
|
|
|
@ -22,18 +22,18 @@ import UIKit
|
|||
/// It is managed by a `UserIndicator`, meaning the `present` and `dismiss` methods will be called when the parent `UserIndicator` starts or completes.
|
||||
class FullscreenLoadingViewPresenter: UserIndicatorViewPresentable {
|
||||
private let label: String
|
||||
private weak var viewController: UIViewController?
|
||||
private let presentationContext: UserIndicatorPresentationContext
|
||||
private weak var view: UIView?
|
||||
private var animator: UIViewPropertyAnimator?
|
||||
|
||||
init(label: String, presentingViewController: UIViewController) {
|
||||
init(label: String, presentationContext: UserIndicatorPresentationContext) {
|
||||
self.label = label
|
||||
self.viewController = presentingViewController
|
||||
self.presentationContext = presentationContext
|
||||
}
|
||||
|
||||
func present() {
|
||||
// Find the current top navigation controller
|
||||
var presentingController: UIViewController? = viewController
|
||||
var presentingController: UIViewController? = presentationContext.indicatorPresentingViewController
|
||||
while presentingController?.navigationController != nil {
|
||||
presentingController = presentingController?.navigationController
|
||||
}
|
||||
|
|
|
@ -27,17 +27,17 @@ class ToastViewPresenter: UserIndicatorViewPresentable {
|
|||
}
|
||||
|
||||
private let viewState: ToastViewState
|
||||
private weak var viewController: UIViewController?
|
||||
private let presentationContext: UserIndicatorPresentationContext
|
||||
private weak var view: UIView?
|
||||
private var animator: UIViewPropertyAnimator?
|
||||
|
||||
init(viewState: ToastViewState, presentingViewController: UIViewController) {
|
||||
init(viewState: ToastViewState, presentationContext: UserIndicatorPresentationContext) {
|
||||
self.viewState = viewState
|
||||
self.viewController = presentingViewController
|
||||
self.presentationContext = presentationContext
|
||||
}
|
||||
|
||||
func present() {
|
||||
guard let viewController = viewController else {
|
||||
guard let viewController = presentationContext.indicatorPresentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import Foundation
|
||||
import MatrixSDK
|
||||
import CommonKit
|
||||
|
||||
/// SplitViewCoordinatorParameters input parameters
|
||||
class SplitViewCoordinatorParameters {
|
||||
|
@ -104,7 +105,13 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
|
|||
// Setup split view controller
|
||||
self.splitViewController.viewControllers = [tabBarCoordinator.toPresentable(), detailNavigationController]
|
||||
|
||||
updateUserIndicatorPresenter()
|
||||
// Setup detail user indicator presenter
|
||||
let context = SplitViewUserIndicatorPresentationContext(
|
||||
splitViewController: splitViewController,
|
||||
tabBarCoordinator: tabBarCoordinator,
|
||||
detailNavigationController: detailNavigationController
|
||||
)
|
||||
detailUserIndicatorPresenter = UserIndicatorTypePresenter(presentationContext: context)
|
||||
|
||||
self.add(childCoordinator: tabBarCoordinator)
|
||||
|
||||
|
@ -260,16 +267,6 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateUserIndicatorPresenter() {
|
||||
guard let tabBarCoordinator = tabBarCoordinator, let detailNavigationController = detailNavigationController else {
|
||||
MXLog.debug("[SplitViewCoordinator]: Missing tab bar or detail coordinator, cannot update user indicator presenter")
|
||||
return
|
||||
}
|
||||
|
||||
let presentingViewController = splitViewController.isCollapsed ? tabBarCoordinator.toPresentable() : detailNavigationController
|
||||
detailUserIndicatorPresenter = UserIndicatorTypePresenter(presentingViewController: presentingViewController)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISplitViewControllerDelegate
|
||||
|
@ -299,8 +296,6 @@ extension SplitViewCoordinator: UISplitViewControllerDelegate {
|
|||
// Restore detail navigation controller with placeholder as root
|
||||
self.resetDetailNavigationController(animated: false)
|
||||
|
||||
updateUserIndicatorPresenter()
|
||||
|
||||
// Return up to date detail navigation controller
|
||||
// In any cases `detailNavigationController` will be used as secondary view of the split view controller.
|
||||
return self.detailNavigationController
|
||||
|
@ -311,7 +306,6 @@ extension SplitViewCoordinator: UISplitViewControllerDelegate {
|
|||
/// or true to indicate that you do not want the split view controller to do anything with the secondary view controller.
|
||||
/// Sample case: large iPhone goes from landscape to portrait.
|
||||
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
|
||||
updateUserIndicatorPresenter()
|
||||
|
||||
// If the secondary view is the placeholder screen do not merge the secondary into the primary.
|
||||
// Note: In this case, the secondaryViewController will be automatically discarded.
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CommonKit
|
||||
|
||||
class SplitViewUserIndicatorPresentationContext: UserIndicatorPresentationContext {
|
||||
private weak var splitViewController: UISplitViewController?
|
||||
private weak var tabBarCoordinator: TabBarCoordinator?
|
||||
private weak var detailNavigationController: UINavigationController?
|
||||
|
||||
init(
|
||||
splitViewController: UISplitViewController,
|
||||
tabBarCoordinator: TabBarCoordinator,
|
||||
detailNavigationController: UINavigationController
|
||||
) {
|
||||
self.splitViewController = splitViewController
|
||||
self.tabBarCoordinator = tabBarCoordinator
|
||||
self.detailNavigationController = detailNavigationController
|
||||
}
|
||||
|
||||
var indicatorPresentingViewController: UIViewController? {
|
||||
guard
|
||||
let splitViewController = splitViewController,
|
||||
let tabBarCoordinator = tabBarCoordinator,
|
||||
let detailNavigationController = detailNavigationController
|
||||
else {
|
||||
MXLog.debug("[SplitViewCoordinator]: Missing tab bar or detail coordinator, cannot update user indicator presenter")
|
||||
return nil
|
||||
}
|
||||
return splitViewController.isCollapsed ? tabBarCoordinator.toPresentable() : detailNavigationController
|
||||
}
|
||||
}
|
1
changelog.d/5780.change
Normal file
1
changelog.d/5780.change
Normal file
|
@ -0,0 +1 @@
|
|||
Activity Indicators: Replace user indicator presenting view controller with context
|
Loading…
Reference in a new issue