-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extensions from new project template (#133)
* ✨ Add frontmostController extensions * ✨ Add UIView+Spacer * ✨ Add Combine+Concurrency
- Loading branch information
Showing
5 changed files
with
186 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import UIKit | ||
|
||
// not sure this doesn't crash on iOS 11, so make it unavailable as it cannot be really tested | ||
@available(iOS 12, *) | ||
public extension UIView { | ||
private final class Spacer: UIView { | ||
fileprivate var observation: NSKeyValueObservation? | ||
|
||
init( | ||
size: CGFloat, | ||
axis: NSLayoutConstraint.Axis, | ||
priority: Float | ||
) { | ||
super.init(frame: .init( | ||
origin: .zero, | ||
size: .init( | ||
width: axis == .horizontal ? size : 0, | ||
height: axis == .vertical ? size : 0 | ||
) | ||
)) | ||
|
||
translatesAutoresizingMaskIntoConstraints = false | ||
|
||
switch axis { | ||
case .horizontal: | ||
let constraint = widthAnchor.constraint(equalToConstant: size) | ||
constraint.priority = .init(priority) | ||
constraint.isActive = true | ||
case .vertical: | ||
let constraint = heightAnchor.constraint(equalToConstant: size) | ||
constraint.priority = .init(priority) | ||
constraint.isActive = true | ||
default: assertionFailure("Unknown axis \(axis)") | ||
} | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
} | ||
|
||
private func createSpacer(_ size: CGFloat, axis: NSLayoutConstraint.Axis, priority: Float) -> UIView { | ||
let spacer = Spacer(size: size, axis: axis, priority: priority) | ||
spacer.isHidden = isHidden | ||
spacer.observation = observe(\.isHidden) { [weak spacer] sender, _ in | ||
spacer?.isHidden = sender.isHidden | ||
} | ||
return spacer | ||
} | ||
|
||
/// Create vertical spacer whose `isHidden` is tied to `self.isHidden` | ||
func createVSpacer(_ height: CGFloat, priority: Float = 999) -> UIView { | ||
createSpacer(height, axis: .vertical, priority: priority) | ||
} | ||
|
||
/// Create horizontal spacer whose `isHidden` is tied to `self.isHidden` | ||
func createHSpacer(_ width: CGFloat, priority: Float = 999) -> UIView { | ||
createSpacer(width, axis: .horizontal, priority: priority) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import UIKit | ||
|
||
/// Extend you container controllers with this protocol, to make sure `frontmostChild` and `frontmostController` properties | ||
/// can work correctly | ||
public protocol FrontmostContainerViewController { | ||
/// Goes through view controller hierarchy and returns view controller on top | ||
var frontmostChild: UIViewController? { get } | ||
} | ||
|
||
extension UISplitViewController: FrontmostContainerViewController { | ||
public var frontmostChild: UIViewController? { viewControllers.last } | ||
} | ||
|
||
extension UINavigationController: FrontmostContainerViewController { | ||
public var frontmostChild: UIViewController? { topViewController } | ||
} | ||
|
||
extension UITabBarController: FrontmostContainerViewController { | ||
public var frontmostChild: UIViewController? { selectedViewController } | ||
} | ||
|
||
public extension UIViewController { | ||
/// Returns frontmost controller that can be used e.g. for modal presentations | ||
var frontmostController: UIViewController { | ||
presentedViewController?.frontmostController ?? (self as? FrontmostContainerViewController)?.frontmostChild?.frontmostController ?? self | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import Combine | ||
|
||
@available(macOS 10.15, iOS 13.0, *) | ||
public extension Future where Failure == Error { | ||
convenience init(operation: @escaping () async throws -> Output) { | ||
self.init { promise in | ||
Task { | ||
do { | ||
try await promise(.success(operation())) | ||
} catch { | ||
promise(.failure(error)) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@available(macOS 10.15, iOS 13.0, *) | ||
public extension AnyPublisher where Failure == Error { | ||
init(operation: @escaping () async throws -> Output) { | ||
self = Future { try await operation() }.eraseToAnyPublisher() | ||
} | ||
} | ||
|