Длительная асинхронная задача - как получить обратный вызов в любом контроллере представления, к которому пользователь мог перейти - PullRequest
0 голосов
/ 02 июля 2018

В моем приложении iOS Swift я запускаю долгосрочную асинхронную задачу (удерживаемую одноэлементным файлом, существующим на протяжении всего срока службы приложения) для загрузки некоторых данных на сервер. Обычно эта задача может занимать до 10 секунд, в течение которых очень вероятно, что они ушли от контроллера представления, запустившего асинхронную задачу. Когда задача будет выполнена, я бы хотел отобразить сообщение для пользователя, независимо от того, где в приложении находится пользователь. Есть ли простой способ отобразить диалоговое окно / сообщение / что угодно без необходимости регистрировать нового делегата каждый раз, когда создается новый контроллер представления?

В моем приложении для Android, которое делает то же самое, я могу отображать тост (например, диалоговое окно) в любое время, независимо от того, какой фрагмент (например, View Controller) отображается в это время - без необходимости реализовывать специальное поведение внутри фрагмента, так как тост отображается на родительском фрагменте.

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Вы можете использовать NSNotification и NSNotificationCenter, чтобы отправить уведомление о завершении обратного вызова и зарегистрироваться для получения уведомления в любом имеющемся у вас ViewController.

0 голосов
/ 02 июля 2018

Я думаю, что волшебство, которое вы ищете, это

func getTopViewController(_ viewController: UIViewController? = UIApplication.shared.delegate?.window??.rootViewController) -> UIViewController? {
    if let tabBarViewController = viewController as? UITabBarController {
        return getTopViewController(tabBarViewController.selectedViewController)
    } else if let navigationController = viewController as? UINavigationController {
        return getTopViewController(navigationController.visibleViewController)
    } else if let presentedViewController = viewController?.presentedViewController {
        return getTopViewController(presentedViewController)
    } else {
        return viewController
    }
}

Я ненавижу использовать его, потому что он не придерживается лучших практик Apple, но в крайнем случае он подойдет для более простых раскадровок. Вы можете использовать это представление контроллеров представления, чтобы добавить сообщение к

if let viewController = getTopViewController() {
    let toastLabel = UILabel(frame: frame)
    // set up labels background color and other properties...
    toastLabel.text = "your message" 
    // add the subview to the rootViewController
    viewController.view.addSubview(toastLabel)
    // easy animation
    UIView.animate(withDuration: 0.4, delay: duration, options: .curveEaseOut, animations: {
        toastLabel.alpha = 0.0
    }, completion: {(isCompleted) in
        toastLabel.removeFromSuperview()
    })
} else {
    print("Unable to get top view controller.")
}

Убедитесь, что используете это только в основной очереди отправки

DispatchQueue.main.async {
    // ...
}

Ниже приведено несколько приемов, позволяющих сделать этот модуль более ориентированным на модуль и объект.

Один из приемов - использовать статический класс тостов .

import UIKit
public class Toast {
    private init() { }
    public static var frame = CGRect(
        x: UIScreen.main.bounds.size.width/2 - (UIScreen.main.bounds.size.width/2 - 16),
        y: UIScreen.main.bounds.size.height - 100,
        width: UIScreen.main.bounds.size.width - 32,
        height: 35
    )
    public static var backgroundColor = UIColor.black.withAlphaComponent(0.6)
    public static var textColor = UIColor.white
    public static var textAlignment = NSTextAlignment.center
    public static var font = UIFont.systemFont(ofSize: 12.0)
    public static var alpha:CGFloat = 1.0
    public static var cornerRadius:CGFloat = 10.0;
    public static func makeToast(
        message: String,
        duration: TimeInterval = 4.0,
        completion: ((_ complete:Bool)->Void)? = nil
    ) {
        if let viewController = getTopViewController() {
            let toastLabel = UILabel(frame: Toast.frame)
            toastLabel.backgroundColor = Toast.backgroundColor
            toastLabel.textColor = Toast.textColor
            toastLabel.textAlignment = Toast.textAlignment;
            toastLabel.font = Toast.font
            toastLabel.alpha = Toast.alpha
            toastLabel.layer.cornerRadius = Toast.cornerRadius
            toastLabel.clipsToBounds  =  true

            toastLabel.text = message

            viewController.view.addSubview(toastLabel)
            UIView.animate(withDuration: 0.4, delay: duration, options: .curveEaseOut, animations: {
                toastLabel.alpha = 0.0
            }, completion: {(isCompleted) in
                toastLabel.removeFromSuperview()
                completion?(isCompleted)
            })
        } else {
            print("Unable to get top view controller.")
        }
    }
    private static func getTopViewController(_ viewController: UIViewController? = UIApplication.shared.delegate?.window??.rootViewController) -> UIViewController? {
        if let tabBarViewController = viewController as? UITabBarController {
            return getTopViewController(tabBarViewController.selectedViewController)
        } else if let navigationController = viewController as? UINavigationController {
            return getTopViewController(navigationController.visibleViewController)
        } else if let presentedViewController = viewController?.presentedViewController {
            return getTopViewController(presentedViewController)
        } else {
            return viewController
        }
    }
}

Использование:

Toast.makeToast(message: "This is a test", duration: 4.0) { (isCompleted) in
    print("completed: \(isCompleted)")
}
// or
Toast.makeToast(message: "This is a test", duration: 4.0)
// or just
Toast.makeToast(message: "This is a test")

Вы можете установить frame, backgroundColor, textColor, textAlignment, font, alpha и cornerRadius, используя статические переменные следующим образом:

Toast.frame = CGRect(
    x: UIScreen.main.bounds.size.width/2 - (UIScreen.main.bounds.size.width/2 - 16),
    y: UIScreen.main.bounds.size.height - 100,
    width: UIScreen.main.bounds.size.width - 32,
    height: 35
)
Toast.backgroundColor = UIColor.blue
Toast.textColor = UIColor.green
Toast.textAlignment = .left
Toast.font = UIFont.systemFont(ofSize: 14.0)
Toast.alpha = 0.8
Toast.cornerRadius = 8.0

Другой метод заключается в расширении UIApplication .

import UIKit
extension UIApplication {
    public func makeToast(
        message: String,
        duration: TimeInterval = 4.0,
        frame:CGRect = CGRect(
            x: UIScreen.main.bounds.size.width/2 - (UIScreen.main.bounds.size.width/2 - 16),
            y: UIScreen.main.bounds.size.height - 100,
            width: UIScreen.main.bounds.size.width - 32,
            height: 35
        ),
        backgroundColor:UIColor = UIColor.black.withAlphaComponent(0.6),
        textColor:UIColor = UIColor.white,
        textAlignment:NSTextAlignment = .center,
        font:UIFont = UIFont.systemFont(ofSize: 12.0),
        alpha:CGFloat = 1.0,
        cornerRadius:CGFloat = 10,
        completion: ((_ complete:Bool)->Void)? = nil
    ) {
        if let viewController = self.getTopViewController(self.delegate?.window??.rootViewController) {
            let toastLabel = UILabel(frame: frame)
            toastLabel.backgroundColor = backgroundColor
            toastLabel.textColor = textColor
            toastLabel.textAlignment = textAlignment;
            toastLabel.font = font
            toastLabel.alpha = alpha
            toastLabel.layer.cornerRadius = cornerRadius
            toastLabel.clipsToBounds = true

            toastLabel.text = message

            viewController.view.addSubview(toastLabel)
            UIView.animate(withDuration: 0.4, delay: duration, options: .curveEaseOut, animations: {
                toastLabel.alpha = 0.0
            }, completion: {(isCompleted) in
                toastLabel.removeFromSuperview()
                completion?(isCompleted)
            })
        } else {
            print("Unable to get top view controller.")
        }
    }

    private func getTopViewController(_ viewController: UIViewController?) -> UIViewController? {
        if let tabBarViewController = viewController as? UITabBarController {
            return getTopViewController(tabBarViewController.selectedViewController)
        } else if let navigationController = viewController as? UINavigationController {
            return getTopViewController(navigationController.visibleViewController)
        } else if let presentedViewController = viewController?.presentedViewController {
            return getTopViewController(presentedViewController)
        } else {
            return viewController
        }
    }
}

Использование:

UIApplication.shared.makeToast(message: "This is another test", duration: 4.0) { (isCompleted) in
    print("completed: \(isCompleted)")
}

Вы можете установить frame, backgroundColor, textColor, textAlignment, font, alpha и cornerRadius, передав их в функцию с их тегами:

UIApplication.shared.makeToast(
    message: "This is another test",
    duration: 4.0,
    frame: CGRect(
        x: UIScreen.main.bounds.size.width/2 - (UIScreen.main.bounds.size.width/2 - 16),
        y: UIScreen.main.bounds.size.height - 100,
        width: UIScreen.main.bounds.size.width - 32,
        height: 35
    ),
    backgroundColor: UIColor.blue.withAlphaComponent(0.6),
    textColor: UIColor.red,
    textAlignment: NSTextAlignment.left,
    font: UIFont.systemFont(ofSize: 16.0),
    alpha: 1.0,
    cornerRadius: 10
) { (isCompleted) in
    print("completed: \(isCompleted)")
}

Скачать пример

Быстрый гист

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...