原因:'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
我的应用程序在添加检查通知允许功能之前运行良好。当我添加那个函数时,我遇到了这个奇怪的崩溃,它给出了 'Modifications to layout engine must not be performed from a background thread after it has been accessed from the main thread' 的错误。调试时弹出的代码是这样的。有什么想法吗?
我的代码
import UIKit
import GoogleMaps
class HomeViewController: FHBaseViewController {
var devices : [Device]?
var firstLoad = true;
let defaults = UserDefaults.standard;
var icons: [String:UIImage] = [:];
@IBOutlet weak var movingView : UIView!;
@IBOutlet weak var stoppedView : UIView!;
@IBOutlet weak var inYardView : UIView!;
@IBOutlet weak var offlineView : UIView!;
@IBOutlet weak var statsView : UIView!;
@IBOutlet weak var mapBed : UIView!;
@IBOutlet weak var mapView : GMSMapView!;
@IBOutlet weak var morningLabel : UILabel!;
@IBOutlet weak var movingLabel : UILabel!;
@IBOutlet weak var stoppedLabel : UILabel!;
@IBOutlet weak var inYardLabel : UILabel!;
@IBOutlet weak var offlineLabel : UILabel!;
@IBOutlet weak var nameLabel : UILabel!;
override func viewDidLoad() {
super.viewDidLoad();
self.reloadData();
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.tabBarController?.tabBar.isHidden = false;
if firstLoad{
firstLoad = false;
}else{
if UserService.userInfo != nil{
self.reloadData();
}
}
}
override func connectionResume() {
self.reloadData()
}
func checkNotificationAllowed(){
let data = defaults.object(forKey:"mute") as? Bool
print(data!)
if (data != nil) == true {
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
switch permission.authorizationStatus {
case .authorized:
print("User granted permission for notification")
case .denied:
print("User denied notification permission")
let alert = UIAlertController(title: "Turn On Notifications".localized(), message: "Notifications are disabled. Please turn on app notifications to get device alerts.".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Don't Allow".localized(), style: .cancel, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Allow".localized(), style: .destructive, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
case .notDetermined:
print("Notification permission haven't been asked yet")
case .provisional:
// @available(iOS 12.0, *)
print("The application is authorized to post non-interruptive user notifications.")
case .ephemeral:
// @available(iOS 14.0, *)
print("The application is temporarily authorized to post notifications. Only available to app clips.")
@unknown default:
print("Unknow Status")
}
})
}
}
func showLoginVC(){
UserService.clearUser();
UserService.clearLocalCacheUser();
self.firstLoad = true;
if buildType == "GPSTracker" {
let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MarocLoginViewController") as! MarocLoginViewController;
loginVC.successBlock = { (resp, password) in
self.loginSuccess(resp, password: password);
}
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: true, completion: {});
} else {
let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController;
loginVC.successBlock = { (resp, password) in
self.loginSuccess(resp, password: password);
}
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: true, completion: {});
}
}
func loginSuccess(_ resp: LoginResponse?, password: String){
if let un = resp?.userName, let name = resp?.name, let apiToken = resp?.apiToken{
let u = User(username: un, name: name, password: password, apiToken: apiToken, isActive: true, baseUrl: Configuration.getBaseUrl());
UserService.setUser(user: u);
}
self.reloadData();
}
func reloadData(){
self.nameLabel.text = UserService.userInfo?.name;
self.mapView.clear();
let update = GMSCameraUpdate.zoom(to: 1);
self.mapView.moveCamera(update);
self.showHud();
DashBoardService.getDashBoard { (resp) in
self.hideHud();
if resp?.status == 1{
self.movingLabel.text = "\(resp?.summary?.moving ?? 0)";
self.stoppedLabel.text = "\(resp?.summary?.stopped ?? 0)";
self.inYardLabel.text = "\(resp?.summary?.inyard ?? 0)";
self.offlineLabel.text = "\(resp?.summary?.offline ?? 0)";
DispatchQueue.main.async {
self.hideHud();
self.checkNotificationAllowed()
}
}
}
DeviceService.getDevice { (resp) in
if resp?.status == 1{
self.devices = resp?.devices;
self.reloadMap();
}
}
}
}
问题的产生是因为后台线程正在显示警报,因为 getNotificationSettings 中的完成处理程序正在后台线程中 运行。为防止在主线程中出现此崩溃警报,请在您的代码中进行以下更改。
func checkNotificationAllowed(){
let data = defaults.object(forKey:"mute") as? Bool
print(data!)
if (data != nil) == true {
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
switch permission.authorizationStatus {
case .authorized:
print("User granted permission for notification")
case .denied:
print("User denied notification permission")
DispatchQueue.main.async {[weak self] in
guard let weakSelf = self else {return}
let alert = UIAlertController(title: "Turn On Notifications".localized(), message: "Notifications are disabled. Please turn on app notifications to get device alerts.".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Don't Allow".localized(), style: .cancel, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Allow".localized(), style: .destructive, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
weakSelf.present(alert, animated: true, completion: nil)
}
case .notDetermined:
print("Notification permission haven't been asked yet")
case .provisional:
// @available(iOS 12.0, *)
print("The application is authorized to post non-interruptive user notifications.")
case .ephemeral:
// @available(iOS 14.0, *)
print("The application is temporarily authorized to post notifications. Only available to app clips.")
@unknown default:
print("Unknow Status")
}
})
}
}
我的应用程序在添加检查通知允许功能之前运行良好。当我添加那个函数时,我遇到了这个奇怪的崩溃,它给出了 'Modifications to layout engine must not be performed from a background thread after it has been accessed from the main thread' 的错误。调试时弹出的代码是这样的。有什么想法吗?
我的代码
import UIKit
import GoogleMaps
class HomeViewController: FHBaseViewController {
var devices : [Device]?
var firstLoad = true;
let defaults = UserDefaults.standard;
var icons: [String:UIImage] = [:];
@IBOutlet weak var movingView : UIView!;
@IBOutlet weak var stoppedView : UIView!;
@IBOutlet weak var inYardView : UIView!;
@IBOutlet weak var offlineView : UIView!;
@IBOutlet weak var statsView : UIView!;
@IBOutlet weak var mapBed : UIView!;
@IBOutlet weak var mapView : GMSMapView!;
@IBOutlet weak var morningLabel : UILabel!;
@IBOutlet weak var movingLabel : UILabel!;
@IBOutlet weak var stoppedLabel : UILabel!;
@IBOutlet weak var inYardLabel : UILabel!;
@IBOutlet weak var offlineLabel : UILabel!;
@IBOutlet weak var nameLabel : UILabel!;
override func viewDidLoad() {
super.viewDidLoad();
self.reloadData();
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated);
self.tabBarController?.tabBar.isHidden = false;
if firstLoad{
firstLoad = false;
}else{
if UserService.userInfo != nil{
self.reloadData();
}
}
}
override func connectionResume() {
self.reloadData()
}
func checkNotificationAllowed(){
let data = defaults.object(forKey:"mute") as? Bool
print(data!)
if (data != nil) == true {
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
switch permission.authorizationStatus {
case .authorized:
print("User granted permission for notification")
case .denied:
print("User denied notification permission")
let alert = UIAlertController(title: "Turn On Notifications".localized(), message: "Notifications are disabled. Please turn on app notifications to get device alerts.".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Don't Allow".localized(), style: .cancel, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Allow".localized(), style: .destructive, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
case .notDetermined:
print("Notification permission haven't been asked yet")
case .provisional:
// @available(iOS 12.0, *)
print("The application is authorized to post non-interruptive user notifications.")
case .ephemeral:
// @available(iOS 14.0, *)
print("The application is temporarily authorized to post notifications. Only available to app clips.")
@unknown default:
print("Unknow Status")
}
})
}
}
func showLoginVC(){
UserService.clearUser();
UserService.clearLocalCacheUser();
self.firstLoad = true;
if buildType == "GPSTracker" {
let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MarocLoginViewController") as! MarocLoginViewController;
loginVC.successBlock = { (resp, password) in
self.loginSuccess(resp, password: password);
}
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: true, completion: {});
} else {
let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController;
loginVC.successBlock = { (resp, password) in
self.loginSuccess(resp, password: password);
}
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: true, completion: {});
}
}
func loginSuccess(_ resp: LoginResponse?, password: String){
if let un = resp?.userName, let name = resp?.name, let apiToken = resp?.apiToken{
let u = User(username: un, name: name, password: password, apiToken: apiToken, isActive: true, baseUrl: Configuration.getBaseUrl());
UserService.setUser(user: u);
}
self.reloadData();
}
func reloadData(){
self.nameLabel.text = UserService.userInfo?.name;
self.mapView.clear();
let update = GMSCameraUpdate.zoom(to: 1);
self.mapView.moveCamera(update);
self.showHud();
DashBoardService.getDashBoard { (resp) in
self.hideHud();
if resp?.status == 1{
self.movingLabel.text = "\(resp?.summary?.moving ?? 0)";
self.stoppedLabel.text = "\(resp?.summary?.stopped ?? 0)";
self.inYardLabel.text = "\(resp?.summary?.inyard ?? 0)";
self.offlineLabel.text = "\(resp?.summary?.offline ?? 0)";
DispatchQueue.main.async {
self.hideHud();
self.checkNotificationAllowed()
}
}
}
DeviceService.getDevice { (resp) in
if resp?.status == 1{
self.devices = resp?.devices;
self.reloadMap();
}
}
}
}
问题的产生是因为后台线程正在显示警报,因为 getNotificationSettings 中的完成处理程序正在后台线程中 运行。为防止在主线程中出现此崩溃警报,请在您的代码中进行以下更改。
func checkNotificationAllowed(){
let data = defaults.object(forKey:"mute") as? Bool
print(data!)
if (data != nil) == true {
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
switch permission.authorizationStatus {
case .authorized:
print("User granted permission for notification")
case .denied:
print("User denied notification permission")
DispatchQueue.main.async {[weak self] in
guard let weakSelf = self else {return}
let alert = UIAlertController(title: "Turn On Notifications".localized(), message: "Notifications are disabled. Please turn on app notifications to get device alerts.".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Don't Allow".localized(), style: .cancel, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Allow".localized(), style: .destructive, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
weakSelf.present(alert, animated: true, completion: nil)
}
case .notDetermined:
print("Notification permission haven't been asked yet")
case .provisional:
// @available(iOS 12.0, *)
print("The application is authorized to post non-interruptive user notifications.")
case .ephemeral:
// @available(iOS 14.0, *)
print("The application is temporarily authorized to post notifications. Only available to app clips.")
@unknown default:
print("Unknow Status")
}
})
}
}