I am writing a UI text in swift under the new Xcode 7 UI test framework.
the requirement is to test whether the system keyboard is shown in an app.
can someone give me a clue on how to do that? thanks
Try this check:
let app = XCUIApplication()
XCTAssert(app.keyboards.count > 0, "The keyboard is not shown")
Or check for specific keyboard keys like:
let app = XCUIApplication()
XCTAssert(app.keyboards.buttons["Next:"].exists, "The keyboard has no Next button")
You can also control interactions on the keyboard:
let app = XCUIApplication()
app.keyboards.buttons["Next:"].tap()
Add two observers
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardVisible:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardHidden:", name: UIKeyboardDidHideNotification, object: nil)
func keyboardVisible(notif: NSNotification) {
print("keyboardVisible")
}
func keyboardHidden(notif: NSNotification) {
print("keyboardHidden")
}
Whenever the keyboard is visible keyboardVisible will be called and whenever the keyboard is hidden keyboardHidden will be called.
I found the keyboard count check didnt work on one of my apps (it returned a count of 1 even when the keyboard was hidden), so amended it slightly:
private func isKeyboardShown() -> Bool {
return XCUIApplication().keyboards.keys.count > 0
}
Related
I have Login screen and I would like to present it or just add it to navigation, it doesn't matter right now, still not sure which way I would like it to be, everytime my app comes back to foreground. Code below partially works, it does start face id, but actually whole login screen isn't shown, I can still see my last screen that was opened before entering background. I would like to hide everything with login screen. When I print childrens in appMovedToBackground function, correct view controller is printed, which means its added, but as I said, its not showing, only icon from face id can be seen. Code below is on one view controller only, currently, I would like this to be applied to all my view controllers, should I move functionality to appdelegate? Thanks in advance.
let notificationCenter = NotificationCenter.default
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appMovedToBackground() {
print("its in background")
let authVC = AuthViewController()
navigationController?.topViewController?.addChild(authVC)
}
#objc func appCameToForeground() {
print("app enters foreground")
if let authVC = navigationController?.topViewController?.children.first as? AuthViewController {
print("willEnterForegroundNotification")
authVC.checkBiometricsAndStart()
}
}
Here you need to correctly add the vc
#objc func appMovedToBackground() {
print("its in background")
guard let added = navigationController?.topViewController else { return }
let authVC = AuthViewController()
authVC.view.frame = UIScreen.main.bounds
added.view.addSubview(authVC.view)
added.addChild(authVC)
}
Regarding that you need it for all vcs , yes implementing it inside AppDelegate is the easiest way
So I have about four notification observers added in a particular view controller, but I want to remove one in particular if a condition is met and then add it back for a different condition. I was able to remove the particular observer but I have had trouble adding it back. the other ones work except the one a removed and added back and I know it's working cos when the VC loads and all the notification observers are added but then it stops working after adding it back. here's my code:
// registered the observer in ViewDIdAppear and this registers my bluetooth device and whenever i tap on the bluetooth device button this function gets called
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// i removed the observer here
if condition == false {
NotificationCenter.default.removeObserver(self, name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
}
// and then use this to add it back again
NotificationCenter.default.addObserver(
self, selector: #selector(Myclass.deviceTapped), name: Notification.Name("bluetoothRecievedNoticicationName"), object: nil)
// and after adding this ^ back the deviceTapped function doesn't get called anymore
The thing to do in these situations is prove to yourself that this does normally work. That way, you can track down what you are doing that prevents it from working.
For example, perhaps self is going out of existence before any notifications have a chance to arrive...? Perhaps some other code, that you have not shown, is unregistering you again...? I've certainly experienced confusions like that! And they can be very hard to debug.
But whatever the explanation, you need first to assure yourself that unregistering for a notification and then re-registering for it is not a problem.
Here's a test app. It has three buttons: Register, Unregister, and Notify. Here's the code:
let notif : Notification.Name = .init("howdy")
#objc func gotNotified() {
print("got notified")
}
#IBAction func doRegister(_ sender: Any) {
NotificationCenter.default.addObserver(self, selector: #selector(gotNotified), name: notif, object: nil)
}
#IBAction func doUnregister(_ sender: Any) {
NotificationCenter.default.removeObserver(self, name: notif, object: nil)
}
#IBAction func doNotify(_ sender: Any) {
NotificationCenter.default.post(name: notif, object: nil)
}
So configure that with the buttons hooked up, and then:
Tap Register.
Tap Notify.
Tap Unregister.
Tap Notify.
Tap Register.
Tap Notify.
You will see that on 2 and 6, but not 4, we print to the console to prove we were notified — just as you would expect.
How to notify the message when manually changes from device notifications from setting
. notifications changes, what method will be invoke inside app, to know that notification was changes
override func awakeFromNib() {
super.awakeFromNib()
let center = UNUserNotificationCenter.current()
center.getNotificationSettings { (settings) in
if(settings.authorizationStatus == .authorized)
{
print(" authorized")
DispatchQueue.main.async {
self.onOffSwitch.isOn = true
}
}
else
{
print(" not authorized")
DispatchQueue.main.async {
self.onOffSwitch.isOn = false
}
}
}
I got tableview inside SettingViewcontroller customize my cell where added the swift onOffSwift.
Based on the setting manually user changes the notifications from setting I wants my app sync accordingly.
SettingViewController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tbl_SettingView.reloadData()
}
I expected this will work for me? but not
How to handle when user changes there notifications from setting screen should reflect on mobile app screen.
Your app doesn't get an automatic notification when the settings are changed. But the user can make this change only in the Settings app, so you can check the settings when your app comes back to the foreground.
The reason your
override func viewWillAppear(_ animated: Bool) {
doesn't help is that viewWillAppear is not called when the app comes back to the foreground.
There might be better ways but the one I know is this:
Store Settings.authorizationStatus as value in a singleton class or as an entry in UserDefaults
Whenever the app becomes active compare the new value to the stored value. If the they are not the same than you know the settings were changed
I believe you want to get your updated settings when your app enters foreground and not when viewWillAppear called. Just add the observer to your viewDidLoad() (and don't forget to remove it once you're done with the controller):
NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { _ in
UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { settings in
DispatchQueue.main.async {
// update your view with current settings.authorizationStatus
}
})
}
Xcode 9.2, iOS 10.0+ , swift4.
I'm working on a project where user inputs english characters in UITextField and it converted to Japanese language characters. and it is working perfect. now, i want to allow user to input Japanese language characters direct from Japanese Keyboard.In this situation i want to know that the keyboard is changed from default to another type/language.
So, is there any function or Notification is available that can help me?
You can use the UITextInputCurrentInputModeDidChange notification to detect when the current keyboard language changes.
NotificationCenter.default.addObserver(self, selector: #selector(inputModeDidChange), name: .UITextInputCurrentInputModeDidChange, object: nil)
#objc func inputModeDidChange(_ notification: Notification) {
if let inputMode = notification.object as? UITextInputMode {
if let lang = inputMode.primaryLanguage {
// do something
}
}
}
In newer Swift versions the notification has been renamed to UITextInputMode.currentInputModeDidChangeNotification
Register to receive the following notification:
UITextInputCurrentInputModeDidChangeNotification
And anytime the keyboard language changes, you'll receive a notification. You can get more info from UITextInputMode docs.
With recent changes to iOS 12, swift 5.0 you can try:
func prepareForKeyboardChangeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(changeInputMode), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil)
}
#objc
func changeInputMode(notification: NSNotification) {
let inputMethod = txtInput.textInputMode?.primaryLanguage
//perform your logic here
}
in swift 4.2
//keyboard type change
NotificationCenter.default.addObserver(self, selector: #selector(inputModeDidChange), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil)
#objc func inputModeDidChange(_ notification: Notification) {
if let inputMode = notification.object as? UITextInputMode {
if let lang = inputMode.primaryLanguage {
print("langueage:: \(lang)")
}
}
}
I need to check if the app is moved to the background. Why?
Well because my app works with bluetooth and only one person can be connected to this device at a time. Therefore if they are not using it and the app is in the background, disconnect them and send them to the connect main page.
Now I have accomplished this. I have a selector in the main first class and a function to disconnect and send to first page. But what I didn't realise is that if the control panel is dragged up, the app is in the 'background'.
From looking around there doesn't seem to be a way to detect if the control panel is brought up. So does anyone have any ideas on how I can do this differently?
Realistically I just want it so if the app is moved to the background for any other reason than the control panel being brought up, disconnect from the device.
Selector:
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
Function:
#objc func appMovedToBackground() {
if (ViewController.connectedPeripheral != nil) {
print("App moved to background!")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "connectView") as! ViewController
self.navigationController?.pushViewController(nextViewController, animated: true)
ViewController.centralManager.cancelPeripheralConnection(ViewController.connectedPeripheral!)
}
else {
print("App moved to background but no device is connected so no further action taken")
}
}
This is not a duplicate of other questions. I know how to check if app is in background state. I just don't want to disconnect when the control panel is brought up...
In Swift:
if UIApplication.shared.applicationState == .background {
// Add code here...
}
In Objective-C:
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
// Add code here...
}
Hope it works!
Have you tried with adding observer to willResignActive in your view controller?
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive, object: nil)
func willResignActive(_ notification: Notification) {
// code to execute
}
Then you can use this also. After entering in background state , app will be moved to inactive state.
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.shared
//Register for the applicationWillResignActive anywhere in your app.
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.applicationWillResignActive(notification:)), name: NSNotification.Name.UIApplicationWillResignActive, object: app)
}
func applicationWillResignActive(notification: NSNotification) {
}