swift animation not work in UIKeyboardWillShowNotification event - swift

I don't know why animation work fine in UIKeyboardDidShowNotification event but not work in UIKeyboardWillShowNotification event.
Code looks like this:
anim in the comment code not work good, color will change but duration = 4 is not right
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name:UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidHide:"), name:UIKeyboardDidHideNotification, object: nil)
}
deinit {NSNotificationCenter.defaultCenter().removeObserver(self)}
func keyboardDidShow(notification: NSNotification) {
showAnima1()
}
func keyboardDidHide(notification: NSNotification) {
showAnima2()
}
func keyboardWillShow(notification: NSNotification) {
// showAnima1() // animate not work here
}
func keyboardWillHide(notification: NSNotification) {
// showAnima2() // animate not work here
}
func showAnima1() {
UIView.animateWithDuration(4, delay: 0, options: UIViewAnimationOptions(rawValue: 7), animations: { () -> Void in
self.view.backgroundColor = UIColor.greenColor()
}) { (finish) -> Void in
print("animate state = \(finish)")
}
}
func showAnima2() {
UIView.animateWithDuration(4, delay: 0, options: UIViewAnimationOptions(rawValue: 7), animations: { () -> Void in
self.view.backgroundColor = UIColor.whiteColor()
}) { (finish) -> Void in
print("animate state = \(finish)")
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {self.view.endEditing(false)}
}
Sorry, i see comment in source code says:
// Each notification includes a nil object and a userInfo dictionary containing the
// begining and ending keyboard frame in screen coordinates. Use the various UIView and
// UIWindow convertRect facilities to get the frame in the desired coordinate system.
// Animation key/value pairs are only available for the "will" family of notification.
The last comment maybe it means "did" family of notification???

I had the same problem.
It looks like the animate function has to be called from the main thread. Wrapping the animation in DispatchQueue.main.async worked for me:
#objc func keyboardWillHide(notification: NSNotification) {
DispatchQueue.main.async {
UIView.animate(withDuration: 2.0) {
self.view.frame.origin.y = 0
}
}
}

Related

Swift NSNotification Observers not working

I have 2 view controllers, one with a switch that when toggled should post the following notification. In the other view controller I have Observers which should trigger the following function which just toggles a boolean. I am not able to get the observers to work and make that function call, am I doing something wrong here? I have another Notification (Doesn't trigger with user input) that is being sent in the opposite direction which works fine.
#IBAction func switchAction(_ sender: Any) {
if switchUI.isOn {
print("Collecting Data ")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Collect"), object: self)
}
else
{
print("Not Collecting Data")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Do Not Collect"), object: self)
}
}
func collectDataObserver () {
//Add an Observer
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Collect"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Do Not Collect"), object: nil)
}
#objc func toggleData(notification: NSNotification) {
let isCollectData = notification.name.rawValue == "Collect"
if(isCollectData){
IsCollectingData = true
}
else
{
IsCollectingData = false
}
}
You need to call collectDataObserver() in viewDidLoad() of CentralViewController, i.e.
override func viewDidLoad() {
super.viewDidLoad()
collectDataObserver()
}

Why NotificationCenter doesn't pass NSPoint properly?

I'm trying to pass NSPoint via NotificationCenter. I have build a NSView subclass with defined three methods:
override func mouseDragged(with event: NSEvent) {
post(event: event)
}
override func mouseDown(with event: NSEvent) {
post(event: event)
}
func post(event: NSEvent) {
let point = convert(event.locationInWindow, from: nil)
print ("sending \(point)")
NotificationCenter.default.post (
name: NSNotification.Name.mousePositionChanged,
object: point )
}
I registered observer as:
NotificationCenter.default.addObserver(
self,
selector: #selector(self.mouseMovedTo(_:)),
name: NSNotification.Name.mousePositionChanged,
object: nil)
...and function to receive:
#objc func mouseMovedTo(_ point:NSPoint) {
print ("receive \(point)")
}
Notification works — when I press mouse button AppDelegate knows about it and fires mouseMovedTo but it receives only Point(0,0):
sending (0.350715866416309, 0.308759973404255)
receive (0.0, 0.0)
sending (0.350187768240343, 0.30802384118541)
receive (0.0, 0.0)
sending (0.349039364270386, 0.306409099544073)
receive (0.0, 0.0)
.....
How to pass NSPoint proper values? Is it bug?
I tried to pass NSEvent, but I had an errors when I tried to convert(event.locationInWindow, from: someView) inside AppDelegate. Some ideas?
You need
#objc func mouseMovedTo(_ notification:NSNotification) {
print ("receive \(notification.object)")
}

How is that possible to post notification after 1 second delay or in a dispatch asyn?

i just want to call a method after loading my view. i have set notification to one view and postnotification to other view you will get more idea from my code.. here is my code..
in a one.swift file
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "InsertNote:", name: "insert", object: nil)
}
func InsertNote(notification:NSNotification)
{
println("blablalbla")
}
in a second.swift file
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
}
func getData()
{
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
i have try also in a dispatch_asynch but still it is not work.
Try this
let delayInSeconds: Double = 1
override func viewDidLoad() {
super.viewDidLoad()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue()){
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
}
The only reason I can come up with that this doesn't work, is that your first ViewController A is already deallocated when you send the notification with the second ViewController B. So, you are sending the notification with B correctly, but A isn't around to receive it.
You can test this by adding an observer in B as well.
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "insertNote:", name: "insert", object: nil)
}
func getData() {
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
func insertNote:(note: NSNotification) {
print("Banana")
}
// Should print 'Banana' after one second.

Spritekit Supporting iAd

I have been looking everywhere on the web for tutorials on how to include iad banners in my spritekit game. For example I looked at this: Swift SpriteKit iAd but I got some errors, probaly due to the new swift 2 and swift 1. But could you explain what to do to include IAD in my spritekit game. Thank you in advance.
Make sure you have this code in your GameViewController. I just tested this out and it works for landscape and portrait mode.
class GameViewController: UIViewController, ADBannerViewDelegate {
//--------------------
//-----iAd Banner-----
//--------------------
var SH = UIScreen.mainScreen().bounds.height
let transition = SKTransition.fadeWithDuration(0)
var UIiAd: ADBannerView = ADBannerView()
override func viewWillAppear(animated: Bool) {
let BV = UIiAd.bounds.height
UIiAd.delegate = self
UIiAd.frame = CGRectMake(0, SH + BV, 0, 0)
self.view.addSubview(UIiAd)
}
override func viewWillDisappear(animated: Bool) {
UIiAd.delegate = nil
UIiAd.removeFromSuperview()
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.5) // Time it takes the animation to complete
UIiAd.alpha = 1 // Fade in the animation
UIView.commitAnimations()
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.5)
UIiAd.alpha = 0
UIView.commitAnimations()
}
func showBannerAd() {
UIiAd.hidden = false
let BV = UIiAd.bounds.height
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.5) // Time it takes the animation to complete
UIiAd.frame = CGRectMake(0, SH - BV, UIScreen.mainScreen().bounds.width, 0)
UIView.commitAnimations()
}
func hideBannerAd() {
UIiAd.hidden = true
let BV = UIiAd.bounds.height
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.5) // Time it takes the animation to complete
UIiAd.frame = CGRectMake(0, SH + BV, UIScreen.mainScreen().bounds.width, 0) //Beginning position of the ad
UIView.commitAnimations()
}
//--------------------
//-----View Loads-----
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
//iAd Control
self.UIiAd.hidden = true
self.UIiAd.alpha = 0
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.hideBannerAd), name: "hideadsID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showBannerAd), name: "showadsID", object: nil)
}
Add the following outside your class declarations. It doesn't matter what class you go to, as long as it's global so you can access them from any game scene.
//-------------
//-----Ads-----
//-------------
func showAds() {
NSNotificationCenter.defaultCenter().postNotificationName("showadsID", object: nil)
}
func hideAds() {
NSNotificationCenter.defaultCenter().postNotificationName("hideadsID", object: nil)
}
And then whenever you want to show ads in the specific scene or whenever, just call the "showAds()" function or "hideAds()" to hide them.

How can I move the whole view up when the keyboard pop up? (Swift)

The gray box at the bottom is a text view. When I tap the text view, the keyboard will pop up from bottom. However, the text view has been covered by the pop up keyboard.
What functions should I add in order to move up the whole view when the keyboard pops up?
To detect when a keyboard shows up you could listen to the NSNotificationCenter
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: “keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
That will call keyboardWillShow and keyboardWillHide. Here you can do what you want with your UITextfield
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
//use keyboardSize.height to determine the height of the keyboard and set the height of your textfield accordingly
}
}
}
func keyboardWillHide(notification: NSNotification) {
//pull everything down again
}
As Milo says, to do this yourself you register for for keyboard show/hide notifications.
You then need to write code that figures out how much of the screen the keyboard is hiding, and how high on the screen the field in question is located, so you know how much to shift your views.
Once you've done that what you do depends on whether you're using AutoLayout or autoresizing masks (a.k.a. "Struts and springs" style layout.)
I wrote a developer blog post about a project that includes working code for shifting the keyboard. See this link:
http://wareto.com/animating-shapes-using-cashapelayer-and-cabasicanimation
In that post, look for the link titled "Sliding your views to make room for the keyboard" at the bottom.
Swift 4 complete solution.
I use this in all of my projects that need it.
on viewWillAppear register to listen for the keyboard showing/hiding and use the functions below to move the view up and back down.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribeToKeyboardNotifications()
}
// stop listening for changes when view is dissappearing
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
// listen for keyboard show/show events
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillHide(_ notification: Notification) {
view.frame.origin.y = 0
}
In my case I have a text field at the bottom which gets hidden by the keyboard so if this is in use then I move the view up
#objc func keyboardWillShow(_ notification: Notification) {
if bottomTextField.isFirstResponder {
view.frame.origin.y = -getKeyboardHeight(notification: notification)
}
}
func getKeyboardHeight(notification: Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.cgRectValue.height
}
Swift 4
This code is not so perfect but worth a try!
First embed the whole view in a scrollView.
Add this little cute function to your class:
#objc func keyboardNotification(_ notification: Notification) {
if let userInfo = (notification as NSNotification).userInfo {
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions().rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if (endFrame?.origin.y)! >= UIScreen.main.bounds.size.height {
scrollViewBottomConstraint?.constant = 0
} else {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight:Int = Int(keyboardSize.height)
scrollViewBottomConstraint?.constant = CGFloat(keyboardHeight)
scrollView.setContentOffset(CGPoint(x: 0, y: (scrollViewBottomConstraint?.constant)! / 2), animated: true)
}
}
UIView.animate(withDuration: duration, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
.
Add this one to your ViewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardNotification(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
Don't forget to connect bottom constraint of your scrollView to your class (with name: "scrollViewBottomConstraint").