我的主视图控制器是通过观察何时发布通知并被多次调用来加载的
My main view controller is loaded by observing when a notification is posted and is being called multiple times
我从显示 gif 的加载屏幕启动我的应用程序,同时在后台加载用户位置和适当的事件
我试图调用视图控制器上的 deinit() 方法来删除观察者,并删除视图控制器作为位置更新的委托,以确保位置更新和随后启动的主屏幕仅调用一次
我的实现可能相当复杂,但我会尽力确保它清晰易懂。我从我的 AppDelegate 文件中调用 startApplication() 来启动加载屏幕
AppDelegate
func startApplication(animated: Bool, pushCategory: String?) {
let mainNC = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let homeVC = storyboard.instantiateViewController(withIdentifier: String(describing: LoadViewController.self)) as! LoadViewController
if pushCategory != nil {
homeVC.pushCategory = pushCategory!
}
mainNC.pushViewController(homeVC, animated: false)
}
在我的 LoadVC 中,我正在加载 gif,然后在从数据库中检索到本地事件时添加一个观察者
override func viewDidLoad() {
super.viewDidLoad()
holdView.frame = UIScreen.main.bounds
holdView.loadGif(name: "ANO_loading_gif")
holdView.contentMode = .scaleAspectFill
showMessageView.layer.cornerRadius = 10.0
showMessageView.clipsToBounds = true
locationService.delegate = self
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
changeLocationMessage()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//get notified when events have been downloaded from API
NotificationCenter.default.addObserver(self, selector: #selector(receivedCloseEvents(_:)), name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}
找到位置后,将调用 LocationService 方法对服务器进行 REST API 调用
func tracingLocation(currentLocation: CLLocation) {
GlobalService.sharedInstance().updateUserLocation(currentLocation)
}
然后在我的 'GlobalService' 文件中,如果调用成功,一系列函数将 运行 在后台运行,因为它们很耗时
func updateUserLocation(_ UserLocation: CLLocation) {
g_userCLLocation = UserLocation
g_userCurrentLocation = nil
// get user location to update Hub name
for location in g_aryLocations {
let coreLocation = CLLocation(latitude: location.location_latitude!, longitude: location.location_longitude!)
let fDistance = coreLocation.distance(from: UserLocation)
// check event distance
if fDistance < Double(Constants.Numbers.EVENT_LIST_DISTANCE) {
g_userCurrentLocation = location
break
}
}
if g_userCLLocation != nil {
// get all valid events
WebService.sharedInstance().getActiveEvents(UserLatitude: UserLocation.coordinate.latitude, UserLongitude: UserLocation.coordinate.longitude)
{ (aryEvents, error) in
if let error = error {
//show issue with network & load from archives if accessible
} else {
//set events to the empty array
//move these time expensive tasks to background thread
self.group.enter()
//execute event recovery on background thread
DispatchQueue.global(qos: .background).async {
self.g_aryEvents = aryEvents!
self.getUsersWithinRange(aryEvents!)
self.updateUserEvent(nil)
}
}
}
}
}
UpdateUserEvent() 通过计算找到用户当前所在的事件,然后一旦找到它就会发布通知以通知 LoadVC 它应该移动到 mainVC
//once user event has been updated send to
private func updateUserEvent(_ liveKey: Int?) {
//operations should happen on the background thread
//ensure live key is not nil
if liveKey != nil {
//clone events array
var swappableEventsArr = g_aryEvents
var eIndex = 0
//30 min start time
let bufferTime = Constants.Numbers.EVENT_LIVE_TIME
var potentialLiveEvent: EventObj?
//search for event with matching event ID to live key
for event in swappableEventsArr {
eIndex += 1
if event.event_id! == liveKey {
potentialLiveEvent = event
break
}
}
//set live event to true
guard let actualEvent = potentialLiveEvent else { return }
if actualEvent.event_time!.timeIntervalSinceNow <= Double(bufferTime) || actualEvent.event_user_id! == 1 {
actualEvent.event_is_live = true
}
//set former live event to false
if swappableEventsArr[0].event_id! != actualEvent.event_id! {
swappableEventsArr[0].event_is_live = false
}
//swap events index
swappableEventsArr.swapAt(0,eIndex - 1)
//remove all previous events
g_aryEvents.removeAll()
//set array to new order with live event in front
g_aryEvents = swappableEventsArr
}
//once value has been achieved exit thread
//set live event
self.g_aryEvents[0].event_is_live = true
self.g_objLiveEvent = self.g_aryEvents[0]
//this call can occur on the background thread
WebService.sharedInstance().updateUserEvent(UserEvent: g_objLiveEvent!.event_id!)
{ (result, error) in
if let error = error {
print(error)
} else {
print(result)
}
}
//move back to main thread
self.group.leave()
//notify main thread that events have finished loading
self.group.notify(queue: .main, execute: {
//call main VC immediately once done prcoessing event ordering
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
})
}
最后,选择器方法被调用,告诉 LoadVC 移动到 MainVC
@objc func receivedCloseEvents(_ notification:Notification) {
locationService.stopUpdatingLocation()
let g_events = GlobalService.sharedInstance().g_aryEvents
self.events = g_events
//send events to Main VC once done
let mainVC = self.storyboard?.instantiateViewController(withIdentifier: String(describing: MainViewController.self)) as! MainViewController
mainVC.arrEvents = self.events
if self.pushCategory != nil {
mainVC.pushCategory = self.pushCategory!
}
self.navigationController?.pushViewController(mainVC, animated: true)
}
然后我调用 deinit() 方法删除观察者并取消分配 LoadVC 作为 LocationService 委托
deinit {
//remove location service delegate
locationService.delegate = nil
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}
通常它的行为符合预期,但有时它会多次推送 MainVC。我还放了一个 属性 观察者 g_usercllocation 并且在 didSet 中它打印了 "location called" 两次。我不确定是不是因为我正在使用 requestLocation() 作为位置,因为我认为它只会在找到位置时被调用一次。
两个可能的问题:
1- 为通知观察不止一次。
2- 多次发布一个通知。
由于您需要对 receivedCloseEvents
方法进行一次分派,因此请将以下行放在 receivedCloseEvents
方法的开头。
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
因此,一旦收到通知,它就会取消注册,直到视图再次出现并重新注册该事件。
我从显示 gif 的加载屏幕启动我的应用程序,同时在后台加载用户位置和适当的事件
我试图调用视图控制器上的 deinit() 方法来删除观察者,并删除视图控制器作为位置更新的委托,以确保位置更新和随后启动的主屏幕仅调用一次
我的实现可能相当复杂,但我会尽力确保它清晰易懂。我从我的 AppDelegate 文件中调用 startApplication() 来启动加载屏幕
AppDelegate
func startApplication(animated: Bool, pushCategory: String?) {
let mainNC = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let homeVC = storyboard.instantiateViewController(withIdentifier: String(describing: LoadViewController.self)) as! LoadViewController
if pushCategory != nil {
homeVC.pushCategory = pushCategory!
}
mainNC.pushViewController(homeVC, animated: false)
}
在我的 LoadVC 中,我正在加载 gif,然后在从数据库中检索到本地事件时添加一个观察者
override func viewDidLoad() {
super.viewDidLoad()
holdView.frame = UIScreen.main.bounds
holdView.loadGif(name: "ANO_loading_gif")
holdView.contentMode = .scaleAspectFill
showMessageView.layer.cornerRadius = 10.0
showMessageView.clipsToBounds = true
locationService.delegate = self
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
changeLocationMessage()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//get notified when events have been downloaded from API
NotificationCenter.default.addObserver(self, selector: #selector(receivedCloseEvents(_:)), name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}
找到位置后,将调用 LocationService 方法对服务器进行 REST API 调用
func tracingLocation(currentLocation: CLLocation) {
GlobalService.sharedInstance().updateUserLocation(currentLocation)
}
然后在我的 'GlobalService' 文件中,如果调用成功,一系列函数将 运行 在后台运行,因为它们很耗时
func updateUserLocation(_ UserLocation: CLLocation) {
g_userCLLocation = UserLocation
g_userCurrentLocation = nil
// get user location to update Hub name
for location in g_aryLocations {
let coreLocation = CLLocation(latitude: location.location_latitude!, longitude: location.location_longitude!)
let fDistance = coreLocation.distance(from: UserLocation)
// check event distance
if fDistance < Double(Constants.Numbers.EVENT_LIST_DISTANCE) {
g_userCurrentLocation = location
break
}
}
if g_userCLLocation != nil {
// get all valid events
WebService.sharedInstance().getActiveEvents(UserLatitude: UserLocation.coordinate.latitude, UserLongitude: UserLocation.coordinate.longitude)
{ (aryEvents, error) in
if let error = error {
//show issue with network & load from archives if accessible
} else {
//set events to the empty array
//move these time expensive tasks to background thread
self.group.enter()
//execute event recovery on background thread
DispatchQueue.global(qos: .background).async {
self.g_aryEvents = aryEvents!
self.getUsersWithinRange(aryEvents!)
self.updateUserEvent(nil)
}
}
}
}
}
UpdateUserEvent() 通过计算找到用户当前所在的事件,然后一旦找到它就会发布通知以通知 LoadVC 它应该移动到 mainVC
//once user event has been updated send to
private func updateUserEvent(_ liveKey: Int?) {
//operations should happen on the background thread
//ensure live key is not nil
if liveKey != nil {
//clone events array
var swappableEventsArr = g_aryEvents
var eIndex = 0
//30 min start time
let bufferTime = Constants.Numbers.EVENT_LIVE_TIME
var potentialLiveEvent: EventObj?
//search for event with matching event ID to live key
for event in swappableEventsArr {
eIndex += 1
if event.event_id! == liveKey {
potentialLiveEvent = event
break
}
}
//set live event to true
guard let actualEvent = potentialLiveEvent else { return }
if actualEvent.event_time!.timeIntervalSinceNow <= Double(bufferTime) || actualEvent.event_user_id! == 1 {
actualEvent.event_is_live = true
}
//set former live event to false
if swappableEventsArr[0].event_id! != actualEvent.event_id! {
swappableEventsArr[0].event_is_live = false
}
//swap events index
swappableEventsArr.swapAt(0,eIndex - 1)
//remove all previous events
g_aryEvents.removeAll()
//set array to new order with live event in front
g_aryEvents = swappableEventsArr
}
//once value has been achieved exit thread
//set live event
self.g_aryEvents[0].event_is_live = true
self.g_objLiveEvent = self.g_aryEvents[0]
//this call can occur on the background thread
WebService.sharedInstance().updateUserEvent(UserEvent: g_objLiveEvent!.event_id!)
{ (result, error) in
if let error = error {
print(error)
} else {
print(result)
}
}
//move back to main thread
self.group.leave()
//notify main thread that events have finished loading
self.group.notify(queue: .main, execute: {
//call main VC immediately once done prcoessing event ordering
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
})
}
最后,选择器方法被调用,告诉 LoadVC 移动到 MainVC
@objc func receivedCloseEvents(_ notification:Notification) {
locationService.stopUpdatingLocation()
let g_events = GlobalService.sharedInstance().g_aryEvents
self.events = g_events
//send events to Main VC once done
let mainVC = self.storyboard?.instantiateViewController(withIdentifier: String(describing: MainViewController.self)) as! MainViewController
mainVC.arrEvents = self.events
if self.pushCategory != nil {
mainVC.pushCategory = self.pushCategory!
}
self.navigationController?.pushViewController(mainVC, animated: true)
}
然后我调用 deinit() 方法删除观察者并取消分配 LoadVC 作为 LocationService 委托
deinit {
//remove location service delegate
locationService.delegate = nil
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
}
通常它的行为符合预期,但有时它会多次推送 MainVC。我还放了一个 属性 观察者 g_usercllocation 并且在 didSet 中它打印了 "location called" 两次。我不确定是不是因为我正在使用 requestLocation() 作为位置,因为我认为它只会在找到位置时被调用一次。
两个可能的问题:
1- 为通知观察不止一次。
2- 多次发布一个通知。
由于您需要对 receivedCloseEvents
方法进行一次分派,因此请将以下行放在 receivedCloseEvents
方法的开头。
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: Constants.Notifications.GET_EVENTS), object: nil)
因此,一旦收到通知,它就会取消注册,直到视图再次出现并重新注册该事件。