// UIViewControllerExtensions.swift - Copyright 2024 SwifterSwift
|
|
#if canImport(UIKit) && !os(watchOS)
|
import UIKit
|
|
// MARK: - Properties
|
|
public extension UIViewController {
|
/// SwifterSwift: Check if ViewController is onscreen and not hidden.
|
var isVisible: Bool {
|
// http://stackoverflow.com/questions/2777438/how-to-tell-if-uiviewcontrollers-view-is-visible
|
return isViewLoaded && view.window != nil
|
}
|
}
|
|
// MARK: - Methods
|
|
public extension UIViewController {
|
/// SwifterSwift: Instantiate UIViewController from storyboard.
|
///
|
/// - Parameters:
|
/// - storyboard: Name of the storyboard where the UIViewController is located.
|
/// - bundle: Bundle in which storyboard is located.
|
/// - identifier: UIViewController's storyboard identifier.
|
/// - Returns: Custom UIViewController instantiated from storyboard.
|
class func instantiate(from storyboard: String = "Main", bundle: Bundle? = nil, identifier: String? = nil) -> Self {
|
let viewControllerIdentifier = identifier ?? String(describing: self)
|
let storyboard = UIStoryboard(name: storyboard, bundle: bundle)
|
guard let viewController = storyboard
|
.instantiateViewController(withIdentifier: viewControllerIdentifier) as? Self else {
|
preconditionFailure(
|
"Unable to instantiate view controller with identifier \(viewControllerIdentifier) as type \(type(of: self))")
|
}
|
return viewController
|
}
|
|
/// SwifterSwift: Assign as listener to notification.
|
///
|
/// - Parameters:
|
/// - name: notification name.
|
/// - selector: selector to run with notified.
|
func addNotificationObserver(name: Notification.Name, selector: Selector) {
|
NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil)
|
}
|
|
/// SwifterSwift: Unassign as listener to notification.
|
///
|
/// - Parameter name: notification name.
|
func removeNotificationObserver(name: Notification.Name) {
|
NotificationCenter.default.removeObserver(self, name: name, object: nil)
|
}
|
|
/// SwifterSwift: Unassign as listener from all notifications.
|
func removeNotificationsObserver() {
|
// swiftlint:disable:next notification_center_detachment
|
NotificationCenter.default.removeObserver(self)
|
}
|
|
/// SwifterSwift: Helper method to display an alert on any UIViewController subclass. Uses UIAlertController to show
|
/// an alert.
|
///
|
/// - Parameters:
|
/// - title: title of the alert.
|
/// - message: message/body of the alert.
|
/// - buttonTitles: (Optional)list of button titles for the alert. Default button i.e "OK" will be shown if this
|
/// parameter is nil.
|
/// - highlightedButtonIndex: (Optional) index of the button from buttonTitles that should be highlighted. If this
|
/// parameter is nil no button will be highlighted.
|
/// - completion: (Optional) completion block to be invoked when any one of the buttons is tapped. It passes the
|
/// index of the tapped button as an argument.
|
/// - Returns: UIAlertController object (discardable).
|
@discardableResult
|
func showAlert(
|
title: String?,
|
message: String?,
|
buttonTitles: [String]? = nil,
|
highlightedButtonIndex: Int? = nil,
|
completion: ((Int) -> Void)? = nil) -> UIAlertController {
|
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
var allButtons = buttonTitles ?? [String]()
|
if allButtons.count == 0 {
|
allButtons.append("OK")
|
}
|
|
for index in 0..<allButtons.count {
|
let buttonTitle = allButtons[index]
|
let action = UIAlertAction(title: buttonTitle, style: .default, handler: { _ in
|
completion?(index)
|
})
|
alertController.addAction(action)
|
// Check which button to highlight
|
if let highlightedButtonIndex, index == highlightedButtonIndex {
|
alertController.preferredAction = action
|
}
|
}
|
present(alertController, animated: true, completion: nil)
|
return alertController
|
}
|
|
/// SwifterSwift: Helper method to add a UIViewController as a childViewController.
|
///
|
/// - Parameters:
|
/// - child: the view controller to add as a child.
|
/// - containerView: the containerView for the child viewController's root view.
|
func addChildViewController(_ child: UIViewController, toContainerView containerView: UIView) {
|
addChild(child)
|
containerView.addSubview(child.view)
|
child.didMove(toParent: self)
|
}
|
|
/// SwifterSwift: Helper method to remove a UIViewController from its parent.
|
func removeViewAndControllerFromParentViewController() {
|
guard parent != nil else { return }
|
|
willMove(toParent: nil)
|
removeFromParent()
|
view.removeFromSuperview()
|
}
|
|
#if os(iOS)
|
/// SwifterSwift: Helper method to present a UIViewController as a popover.
|
///
|
/// - Parameters:
|
/// - popoverContent: the view controller to add as a popover.
|
/// - sourcePoint: the point in which to anchor the popover.
|
/// - size: the size of the popover. Default uses the popover preferredContentSize.
|
/// - delegate: the popover's presentationController delegate. Default is nil.
|
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
/// - completion: The block to execute after the presentation finishes. Default is nil.
|
func presentPopover(
|
_ popoverContent: UIViewController,
|
sourcePoint: CGPoint,
|
size: CGSize? = nil,
|
delegate: (any UIPopoverPresentationControllerDelegate)? = nil,
|
animated: Bool = true,
|
completion: (() -> Void)? = nil) {
|
popoverContent.modalPresentationStyle = .popover
|
|
if let size {
|
popoverContent.preferredContentSize = size
|
}
|
|
if let popoverPresentationVC = popoverContent.popoverPresentationController {
|
popoverPresentationVC.sourceView = view
|
popoverPresentationVC.sourceRect = CGRect(origin: sourcePoint, size: .zero)
|
popoverPresentationVC.delegate = delegate
|
}
|
|
present(popoverContent, animated: animated, completion: completion)
|
}
|
#endif
|
}
|
|
#endif
|