Slather logo

Coverage for "SplitControllerAdapter.swift" : 71.43%

(25 of 35 relevant lines covered)

RouteComposer/Classes/Adapters/SplitControllerAdapter.swift

1
//
2
// RouteComposer
3
// SplitControllerAdapter.swift
4
// https://github.com/ekazaev/route-composer
5
//
6
// Created by Eugene Kazaev in 2018-2022.
7
// Distributed under the MIT license.
8
//
9
// Become a sponsor:
10
// https://github.com/sponsors/ekazaev
11
//
12
13
import Foundation
14
import UIKit
15
16
/// Default `ContainerAdapter` for `UISplitViewController`
17
public struct SplitControllerAdapter<VC: UISplitViewController>: ConcreteContainerAdapter {
18
19
    // MARK: Properties
20
21
    weak var splitViewController: VC?
22
23
    // MARK: Methods
24
25
    public init(with splitViewController: VC) {
41x
26
        self.splitViewController = splitViewController
41x
27
    }
41x
28
29
    public var containedViewControllers: [UIViewController] {
50x
30
        splitViewController?.viewControllers ?? []
50x
31
    }
50x
32
33
    /// ###NB
34
    /// `UISplitViewController` does not support showing primary view controller overlay programmatically out of the box in `primaryOverlay`
35
    /// mode. So all the contained view controllers are considered as visible in the default implementation.
36
    public var visibleViewControllers: [UIViewController] {
36x
37
        containedViewControllers
36x
38
    }
36x
39
40
    /// ###NB
41
    /// `UISplitViewController` does not support showing primary view controller overlay programmatically out of the box in `primaryOverlay`
42
    /// mode, so default implementation of `makeVisible` method wont be able to serve it.
43
    public func makeVisible(_ viewController: UIViewController, animated: Bool, completion: @escaping (_: RoutingResult) -> Void) {
3x
44
        guard splitViewController != nil else {
3x
45
            completion(.failure(RoutingError.compositionFailed(.init("\(String(describing: VC.self)) has been deallocated"))))
1x
46
            return
1x
47
        }
2x
48
        guard contains(viewController) else {
2x
49
            completion(.failure(RoutingError.compositionFailed(.init("\(String(describing: splitViewController)) does not contain \(String(describing: viewController))"))))
1x
50
            return
1x
51
        }
1x
52
        completion(.success)
1x
53
    }
1x
54
55
    /// Replacing of the child view controllers is not fully supported by the implementation of `UISplitViewController`.
56
    /// Only some common cases are covered by this method.
57
    ///
58
    /// ###NB
59
    /// [https://developer.apple.com/documentation/uikit/uisplitviewcontroller](https://developer.apple.com/documentation/uikit/uisplitviewcontroller):
60
    /// **Quote:** When designing your split view interface, it is best to install primary and secondary view controllers that do not change.
61
    /// A common technique is to install navigation controllers in both positions and then push and pop new content as needed.
62
    public func setContainedViewControllers(_ containedViewControllers: [UIViewController], animated: Bool, completion: @escaping (_: RoutingResult) -> Void) {
1x
63
        guard let splitViewController else {
1x
64
            completion(.failure(RoutingError.compositionFailed(.init("\(String(describing: VC.self)) has been deallocated"))))
1x
65
            return
1x
66
        }
1x
67
        if containedViewControllers.count > 1,
!
68
           let primaryViewController = self.containedViewControllers.first,
!
69
           primaryViewController === containedViewControllers.first,
!
70
           let detailsViewController = containedViewControllers.last {
!
71
            splitViewController.showDetailViewController(detailsViewController, sender: self)
!
72
        } else {
!
73
            splitViewController.viewControllers = containedViewControllers
!
74
        }
!
75
        completion(.success)
!
76
    }
!
77
78
}