How to implement viewWillAppear() instead of deprecated didChangeStatusBarOrientationNotification in this case? - swift

I would like to ask how shall I shall use viewWillTransitionToSize:withTransitionCoordinator: instead of deprecated 'didChangeStatusBarOrientationNotification'. Even though in Xcode https://github.com/Mairoslav/8.2.MeMe.2.0.rev it seems that all works well except of this warning I am curious how to silence this warning or make use of it.
I want that when orientation changes from portrait to landscape and back and forth the constraints do adjust accordingly as done via method #objc func orientationChanged.
Still this warning in question is within the Notification where method #objc func orientationChanged is called.
I tried to silence it via using nil for name: , however with nil for name there is an error.
The code in question:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.orientationChanged), name: UIApplication.didChangeStatusBarOrientationNotification, object: nil)
}

Related

How to determine whether current view controller is active, and execute code if active

In my app, there is a ViewController.swift file and a popupViewController.swift file. Inside the app, when I open the popupViewController with storyboard segue as presentModally and then come back from popupViewController to ViewController with the code dismiss(), the methods viewDidLoad, viewWillAppear, viewDidAppear, ViewWillLayoutSubviews etc. nothing works, they execute just once and don't repeat when I go and return back. So, I want to execute the code every time when viewController.swift is active. I couldn't find a useful info in stackoverflow about this.
Meanwhile, I don't know much about notification and observers(if certainly needed), therefore, can you tell step by step in detail how to do that in Swift (not objective-c)? I mean how to determine if current view controller is active.
Edit: I am navigating from StoryBoard segue, presentModally. There is no Navigation Controller in storyboard.
I tried some codes but nothing happens. The point I came so far is:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(appWillEnterForeground), name:UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appWillEnterForeground() {
print("asdad") //nothing happens
if self.viewIfLoaded?.window != nil {
// viewController is visible
print("CURRENT VİEW CONTROLLER") //nothing happens
}
}
As mention in my comments, I don't use storyboards. There may be a way to create an unwind segue - or maybe not - but [here's a link][1] that may help you with a storyboard-only way of fixing your issue. A quick search on "modal" turned up 9 hits, and the second one starts going into details.
I'm thinking the issue is with what modality is. Basically, your first view controller, which properly executed viewDidAppear, is still visible. So it's effectively not executing viewDidDisappear when your second VC is presented.
You might want to change your concept a bit - an application window (think AppDelegate and/or SceneDelegate become active, where a UIViewController has a is initialized and deinitialized, along with a root UIView that is loaded, appears* and disappears*. This is important, because what you want to do is send your notification from the modal VC's viewDidDisappear override.
First, I find it easiest to put all your notication definitions in an extension:
extension Notification.Name {
static let modalHasDisappeared = Notification.Name("ModalHasDisappeared")
}
This helps not only reduce string typos but also is allows Xcode's code completion to kick in.
Next, in your first view controller, ad an observer to this notification:
init() {
super.init(nibName: nil, bundle: nil)
NotificationCenter.default.addObserver(self, selector: #selector(modalHasDisappeared), name: .modalHasDisappeared, object: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
NotificationCenter.default.addObserver(self, selector: #selector(modalHasDisappeared), name: .modalHasDisappeared, object: nil)
}
#objc func modalHasDisappeared() {
print("modal has disappeared")
}
I've added both forms of init for clarity. Since you are using a storyboard, I'd expect that init(coder:) is the one you need.
Finally, just send the notification when the modal has disappeared:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.post(name: .modalHasDisappeared, object: nil, userInfo: nil)
}
This sends no data, just the fact that the modal has disappeared. If you want to send data - say, a string or a table cell value, change the object parameter to it:
NotificationCenter.default.post(name: .modalHasDisappeared, object: myLabel, userInfo: nil)
And make the following changes in your first VC:
NotificationCenter.default.addObserver(self, selector: #selector(modalHasDisappeared(_:)), name: .modalHasDisappeared, object: nil)
#objc func modalHasDisappeared(_ notification:Notification) {
let label = notification.object as! UILabel!
print(label.text)
}
Last notes:
To repeat, note that by declaring an extension to Notification.Name, I've only have one place where I'm declaring a string.
There is no code in AppDelegate or SceneDelegate, nor any references to `UIApplication(). Try to think of the view (and view controller) as appearing/disappearing, not background/foreground.
While the first view is visually in the background, it's still visible. So the trick is to code against the modal view disappearing instead.

Detect if user copied text from textfield

I want to use the word that user copies from my app textfield.
I found this code:
NotificationCenter.default.addObserver(self, selector: #selector(clipboardChanged), name: UIPasteboard.changedNotification, object: nil)
But 'clipboardChanged' function not calling when copying text from textfield.
I use Swift 4.2
This Code Just Worked Fine For Me:
override func copy(_ sender: Any?) {
super.copy()
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(clipboardChanged), name: UIPasteboard.changedNotification, object: nil)
}
#objc func clipboardChanged(){
print("Cut/Copy Performed")
}
There are many ways of achieving copy Notification
1. UIMenuController
Displays a menu with Copy, Cut, Paste, Select, and Select All commands above or below the selection.
Refer:
https://developer.apple.com/documentation/uikit/uimenucontroller
https://nshipster.com/uimenucontroller/
2. UIResponderStandardEditActions Protocol
Responders implement methods declared in this informal protocol to handle the chosen menu commands (for example, copy: and paste:). Since your UIViewController inherits from UIResponder which indeed conforms to UIResponderStandardEditActions, so it will give you error saying Redundant conformance. So just implement the methods you need directly.
Refer: https://developer.apple.com/documentation/uikit/uiresponderstandardeditactions
3. UIPasteboard changedNotification
class let changedNotification: NSNotification.Name
This happens at the same time the pasteboard’s change count (changeCount property) is incremented. Changes include the addition, removal, and modification of pasteboard items.
Refer: https://developer.apple.com/documentation/uikit/uipasteboard

Observer never called

I have two functions
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserverForName("personalDataDidLoad", object: self, queue: NSOperationQueue.mainQueue()) {_ in
print("Received notification")
self.showPersonalData()
}
loadPersonalData()
}
func loadPersonalData() {
//load data
print("personal data loaded")
NSNotificationCenter.defaultCenter().postNotificationName("personalDataDidLoad", object: nil)
but for some reason this outputs
personal data loaded
instead of the expected
personal data loaded
Received notification
I'm probably missing something obvious, but I don't see it right now....
I also tried addObserver with selector: "showPersonalData:" but this throws an unrecognized selector exception..
The problem is with the 2nd parameter in postNotificationName and addObserverForName: object. When you add an observer, and pass a non-nil object value, this means that the observer block will run when a notification comes from that object, and from that object only.
However, when you fire the notification, you do object: nil. So your notification is ignored.
On the other hand, passing a nil value for object means, "I want to receive this notification regardless of who sends it".
So you need to make sure the object value is the same in both places: either self or nil.
Is there a reason you need to use addObserverForName(_:object:queue:usingBlock:)?
Try this instead:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, "personalDataDidLoadNotification:", name: "personalDataDidLoad" object: nil)
loadPersonalData()
}
func loadPersonalData() {
//load data
print("personal data loaded")
NSNotificationCenter.defaultCenter().postNotificationName("personalDataDidLoad", object: nil)
}
func personalDataDidLoadNotification(notification: NSNotification) {
print("notification recieved")
}
Another answer to the title question (but not this example) but will hopefully help others in the situation I have been in for the last 3 hours:
Make sure your notificationcenter observer is added inside a class that has a persisted instance. I created the observer inside a class that was called as e.g. MyClass().setupNotification inside a local method of another class.
This meant the observer was immediately deleted and didnt persist against any instance.
Schoolboy error - but hope this helps others searching for this.

Swift, Fire segue upon Orientation rotation

Im trying to fire a segue based upon if the orientation is in landscape or portrait. This is what I am using, but its giving me an unrecongized selector error. Can anyone point me in the right direction?
viewDidLoad(){
UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showScoreCard:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}
func showScoreCard {
let deviceOrientation = UIDevice.currentDevice().orientation
if UIDeviceOrientationIsLandscape(deviceOrientation){
self.performSegueWithIdentifier("scorecardSegue", sender: self)
}
Get rid of the colon at the end of your showScoreCard string, because the colon indicates that the method takes one argument.

NSNotification never being sent

I'm trying to use the NSNotificationCenter with Swift and I'm running into a problem. I'm trying the following:
class MyClass {
init() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopTimer", name: UIApplicationDidEnterBackgroundNotification, object: [])
}
func stopTimer() {
println("Entered background!")
}
}
When hitting the home button, the observer is never calling the selector, therefore my message is never being printed out.
Notice that I send an empty object at the end, the method signature for addObserver is expecting an implicitly unwrapped object and setting it to nil results in an EXC_BAD_ACCESS error when the app enters background.
Any ideas?
EDIT
I forgot to mention that this code is being executed from a framework included in my project.
Executing the code gives me a bad access abort signal.
You should be able to pass nil for an optional argument. Can you elaborate more on what happens if you do? This is working for me:
func init() {
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopTimer", name: UIApplicationDidEnterBackgroundNotification, object: nil)
}
func stopTimer() {
println("Entered background!")
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Alright, I figured this out. 2 problems:
There was no strong reference to the instance of my class in my app
My class wasn't extending NSObject
Now, I can set object: nil and everything works fine.
Thanks for all your help! :)
Perhaps you should try implementing applicationDidEnterBackground(application: UIApplication) instead?
To further troubleshoot, you may also want to add a log statement to your app delegate's deinit to see if the delegate gets deallocated after entering the background.