I want to update the location when the user enters the app again. If a user opens the app you get correct data, but when you close the app (homebutton) and open it again it goes in the refresh function, but does not go in the location function. I can't get it. Here is my code:
import UIKit
import CoreLocation
var request : NSMutableURLRequest = NSMutableURLRequest()
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func refresh(){
println("update")
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
{
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
println("Error: " + error.localizedDescription)
return
}
if (placemarks.count > 0)
{
let pm = placemarks[0] as! CLPlacemark
self.displayLocationInfo(pm)
}
else
{
println("Error with the data.")
}
})
}
in my app delegate i have this:
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
ViewController().refresh();
}
So it goes in the refresh function, but than it does noting.. What can it be?
Thank you!
in each call of applicationWillEnterForeground(application: UIApplication) you create a new object of ViewController, call refresh() on it and let it go away. This is not the view controller object, that is presented to the user.
Instead you must get the presented ViewController object and call refresh() on it.
It could be something like
func applicationWillEnterForeground(application: UIApplication) {
let viewController= self.window.rootViewController as! ViewController
viewController.refresh()
}
But how it looks exactly, depends on details in your code we don't know.
or delete that line altogether and subrcribe for the WillEnterForeground notification in your ViewController's viewDidLoad()
override func viewDidLoad()
{
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillEnterForeground:", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
func applicationWillEnterForeground(notification: NSNotification) {
self.refresh()
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The challenge you have is that when you press the home key, your views do not unload but are merely suspended. Therefore with this, the viewDidLoad method does not get called, when your application resumes...
What you are actually doing is dealing with the application coming into the foreground which you have to register to receive notifications about. Add the following line to your viewDidLoad method:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("applicationWillEnterForeground"), name: UIApplicationWillEnterForegroundNotification, object: nil)
That will add an observer for the UIApplicationWillEnterForegroundNotification event and then call the method applicationWillEnterForeground. Now all you need to do is create the method applicationWillEnterForeground and call your refresh method from within it.
Related
I am trying a very simple code with NotificationCenter. But the addObserver is not getting called. Can any one of you check and let me know what i am missing. There are 2 simple class, one which post notification and another which listens to it. When i run the program, i just see "sending notification" in the console.
Thanks in advance.
Class 1:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("sending notification")
NotificationCenter.default.post(name: Notification.Name("test"), object: nil)
}
}
Class 2:
class secondvc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("second vc")
NotificationCenter.default.addObserver(self,
selector: #selector(doThisWhenNotify(_:)),
name: Notification.Name("test"),
object: nil)
}
#objc func doThisWhenNotify(_ notification: Notification) {
print("inside notification")
}
}
If, at the time ViewController comes into existence, secondvc does not yet exist, then there is no one there to receive the posted notification and that is why you don't see the notification being received later when secondvc does come into existence.
I am using Spotify SDK. I want to change labels in some view controllers when a user changes his/her player state. Here is my scene delegate:
var playerViewController = MatchViewController()
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
playerViewController.stateChanged(playerState)
}
A view controller:
func stateChanged(_ playerState: SPTAppRemotePlayerState) {
// aLabel.text = playerState.track.name
}
The problem is labels or other outlets are nil when the state is changed because the view controllers are not loaded at that time. How can I fix that? (I tried isViewLoaded)
If you have a more than a few places to update according to a change that occurs at one place use observers. Here's how,
Post notification in SceneDelegate like this:
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stateChanged"), object: nil, userInfo: ["playerState": playerState])
}
Observe in ViewControllers like this:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(stateChanged), name: NSNotification.Name("stateChanged"), object: nil)
}
#objc func stateChanged(_ notification: Notification) {
if let playerState = notification.userInfo?["playerState"] as? SPTAppRemotePlayerState {
print(playerState)
}
}
}
If the review popup initiated from a view controller shows up, there isn't a way to switch the window focus back to the view controller when the popup is dismissed due to lack of callback function of SKStoreReviewController.requestReview().
I would like to make a call to becomeFirstResponder() when the review popup is dismissed. Any idea?
Is there a way to extend the SKStoreReviewController and add a callback somehow?
Warning this will probably break at some point.
Step 1: add this code to your didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let windowClass: AnyClass = UIWindow.self
let originalSelector: Selector = #selector(setter: UIWindow.windowLevel)
let swizzledSelector: Selector = #selector(UIWindow.setWindowLevel_startMonitor(_:))
let originalMethod = class_getInstanceMethod(windowClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(windowClass, swizzledSelector)
let didAddMethod = class_addMethod(windowClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
if didAddMethod {
class_replaceMethod(windowClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
return true
}
Step 2: add this class
class MonitorObject: NSObject {
weak var owner: UIWindow?
init(_ owner: UIWindow?) {
super.init()
self.owner = owner
NotificationCenter.default.post(name: UIWindow.didBecomeVisibleNotification, object: self)
}
deinit {
NotificationCenter.default.post(name: UIWindow.didBecomeHiddenNotification, object: self)
}
}
Step 3: Add this UIWindow extension
private var monitorObjectKey = "monitorKey"
private var partialDescForStoreReviewWindow = "SKStore"
extension UIWindow {
// MARK: - Method Swizzling
#objc func setWindowLevel_startMonitor(_ level: Int) {
setWindowLevel_startMonitor(level)
if description.contains(partialDescForStoreReviewWindow) {
let monObj = MonitorObject(self)
objc_setAssociatedObject(self, &monitorObjectKey, monObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Step 4: add this to ViewDidLoad of your controller where you want this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeHiddenNotification(_:)), name: UIWindow.didBecomeHiddenNotification, object: nil)
}
Step 5: add the callback for the notification and check that the associated object is a match
#objc func windowDidBecomeHiddenNotification(_ notification: Notification?) {
if notification?.object is MonitorObject {
print("hello")
}
}
Now when a review dialog is closed the notification is triggered and 'print("hello") will be called.
Sometimes iOS app is losing the responder chain, like in the above example of showing StoreKit prompt. What we can do is to detect such events in UIApplication.sendAction and reactivate the first responder chain via becomeFirstResponder. UIKit will reestablish the first responder chain and we can resend the same event.
class MyApplication: UIApplication {
func reactivateResponderChainWhenFirstResponderEventWasNotHandled() {
becomeFirstResponder()
}
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
let wasHandled = super.sendAction(action, to: target, from: sender, for: event)
if wasHandled == false, target == nil {
reactivateResponderChainWhenFirstResponderEventWasNotHandled()
return super.sendAction(action, to: target, from: sender, for: event)
}
return wasHandled
}
}
This works for me on iOS 13 and does not require any private API access.
when a user receives a push notification and taps the notification, he/she will be brought into my app, where I want a certain view controller to appear. Therefore I use the notification center.
My question is, where do I need to perform the loading of the view controller so it will be shown and pushed on the navigation stack when the user enters the app?
func processReceivedRemoteNotification(userInfo:[NSObject:AnyObject]) {
let notification = userInfo as! Dictionary<String, AnyObject>
let json = JSON(notification)
// Get information from payload
let dispatchType:String = json["dispatch"]["dispatchType"].stringValue
switch dispatchType {
case "alert":
self.notificationCenter.postNotificationName("ALERT_RECEIVED", object: nil, userInfo: userInfo as [NSObject:AnyObject])
break
default:
break
}
}
View Controller to be loaded
class AlertViewController: UIViewController {
let notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter()
override func viewWillAppear(animated: Bool) {
self.notificationCenter.addObserver(self, selector: "alertMessageReceived:", name: "ALERT_RECEIVED", object: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func alertMessageReceived(notification: NSNotification) {
let userInfo = notification.userInfo as! Dictionary<String, AnyObject>
print(userInfo)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc1: AlertViewController = storyboard.instantiateViewControllerWithIdentifier("example1") as! AlertViewController
self.navigationController?.pushViewController(vc1, animated: true)
}
I don't know your app architecture, but from the given context I can see that you have a navigationController. You should not add as observer AlertViewController in this case. Instead move this code to another view controller, which is already pushed to navigationController. Another option is to subclass UINavigationController and observe "ALERT_RECEIVED" notification in it.
I am trying to figure out how to switch over from my banner view iAds to interstitial iAds in order to free up space for a tabbed controller. For some reason I am completely unable to find any resource for even getting started on these ads with swift.
Could anyone please give me some information on interstitial iAds with Swift and how I can implement them in a project.
Here is a relatively cleaner and easier to follow way to implement Interstitial Ads since this way doesn't require the use of NSNotificationCentre
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
loadInterstitialAd()
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
And it may help if you put println("Function Name") in each function just to keep track of your Interstitial Ads process. If you have any questions and or a way to improve this block of code please leave a comment. Thank You
Here is how i use in my project
//adding iAd framework
import iAd
//conform iAd delegate
class ViewController: UIViewController,ADInterstitialAdDelegate
//create instance variable
var interstitial:ADInterstitialAd!
//default iAd interstitials does not provide close button so we need to create one manually
var placeHolderView:UIView!
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//iAD interstitial
NSNotificationCenter.defaultCenter().addObserver(self, selector: ("runAd:"), name:UIApplicationWillEnterForegroundNotification, object: nil)
}
//iAD interstitial
func runAd(notification:NSNotification){
var timer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: Selector("dislayiAdInterstitial"), userInfo: nil, repeats: false)
cycleInterstitial()
}
func cycleInterstitial(){
// Clean up the old interstitial...
// interstitial.delegate = nil;
// and create a new interstitial. We set the delegate so that we can be notified of when
interstitial = ADInterstitialAd()
interstitial.delegate = self;
}
func presentInterlude(){
// If the interstitial managed to load, then we'll present it now.
if (interstitial.loaded) {
placeHolderView = UIView(frame: self.view.frame)
self.view.addSubview(placeHolderView)
closeButton = UIButton(frame: CGRect(x: 270, y: 25, width: 25, height: 25))
closeButton.setBackgroundImage(UIImage(named: "error"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.view.addSubview(closeButton)
interstitial.presentInView(placeHolderView)
}
}
// iAd Delegate Mehtods
// When this method is invoked, the application should remove the view from the screen and tear it down.
// The content will be unloaded shortly after this method is called and no new content will be loaded in that view.
// This may occur either when the user dismisses the interstitial view via the dismiss button or
// if the content in the view has expired.
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
cycleInterstitial()
}
func interstitialAdActionDidFinish(_interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
println("called just before dismissing - action finished")
}
// This method will be invoked when an error has occurred attempting to get advertisement content.
// The ADError enum lists the possible error codes.
func interstitialAd(interstitialAd: ADInterstitialAd!,
didFailWithError error: NSError!){
cycleInterstitial()
}
//Load iAd interstitial
func dislayiAdInterstitial() {
//iAd interstitial
presentInterlude()
}
func close() {
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
}
I know this is old - but I just came across
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
let destination = segue.destinationViewController
as UIViewController
destination.interstitialPresentationPolicy =
ADInterstitialPresentationPolicy.Automatic
}
If you add this to your app delegate, you'll avoid that 3 second delay suggested above.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
UIViewController.prepareInterstitialAds()
return true
}