Swift: Dismiss ViewController without keyboard slider - ios

I present/dismiss ViewController with TextField like a fade. And keyboard do not need to slide on/out of the screen.
For presenting i did that by:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textField.becomeFirstResponder()
How can i do that for dismiss?
It should not move somehow, i fade out VC and it should fade out with it as well. But insted of that it's moving down

You trying to achive an UI effect which is doesn't comply to Human Interface Guidelines. This usualy means that you you have to find out an trick or a hack to achive it.

Related

Accessibility set focus to navigation bar title item

Overview:
I would like to set the accessibility focus to the navigation bar's title item.
By default the focus is set from top left, meaning the back button would be on focus.
I would like the title item to be in focus.
Attempts made so far:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
navigationController?.navigationBar.items?.last)
Problem:
The above code makes no difference, the back button is still in focus.
Possible Cause:
Not able to get the item corresponding to the title to be able to set the focus.
Solution 1
I don't like it, but it was the minimum amount of hacking that does not rely on digging through hidden subviews (internal implementation of UINavigationBar view hierarchy).
First in viewWillAppear, I store a backup reference of the back button item,
and then remove the back button item (leftBarButtonItem):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
backButtonBackup = self.navigationItem.leftBarButtonItem
self.navigationItem.leftBarButtonItem = nil
}
Then I restore the back item, but only after I dispatch the screen changed event in viewDidAppear() :
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.navigationItem.leftBarButtonItem = self?.backButtonBackup
}
}
Solution 2:
Disable all accessibility on the nav bar and view controller up until viewDidAppear() is finished:
self.navigationController.navigationBar.accessibilityElementsHidden = true
self.view.accessibilityElementsHidden = true
, and then in viewDidAppear manually dispatching the layout element accessibility focused event to the label subview of UINavigationBar:
UIAccessibilityPostNotification( UIAccessibilityLayoutChangedNotification, self.navigationController.navigationBar.subviews[2].subviews[1])
// The label buried inside the nav bar. Not tested on all iOS versions.
// Alternately you can go digging for the label by checking class types.
// Then use DispatchAsync, to re-enable accessibility on the view and nav bar again...
I'm not a fan of this method either.
DispatchAsync delay in viewDidAppear seems to be needed in any case - and I think both solutions are still horrible.
I invoked UIAccessibilityScreenChangedNotification on navigation title from viewDidLoad and it worked
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
self.navigationItem.title);
First, we'll need to create an useful extension:
extension UIViewController {
func setAccessibilityFocus(in view: UIView) {
UIAccessibility.post(notification: .screenChanged, argument: view)
}
}
Then, we'll be able to set our focus in the navigation bar title like this:
setAccessibilityFocus(in: self.navigationController!.navigationBar.subviews[2].subviews[1])

Keyboard does not disappear after viewDidDisappear

iOS 11.2, Xcode 9.2
I've tried all the ways to dismiss keyboard on viewDidDisappear of the old UIViewController AFTER a new UIViewController is pushed into UINavigationController stack. But with no luck.
If I dismiss it on viewWillDisappear - it will be dismissed but with animation DURING push animation. It is not the desired behaviour. I want the old UIViewController's keyboard to be dismissed only when the controller is no longer visible.
The behavior should be like in Telegram app:
In any dialog with visible keyboard press on opponents avatar and you'll be pushed to opponents account information. Then, if you press back button, you'll be redirected back to a dialog. But the keyboard will be already dismissed.
Any help is appreciated!
P.S. The question might look like a duplicate, but I have failed to make it work with the solutions I found.
Edit 1.
I have created a small TEST PROJECT which represents a failure to achieve the desired behavior.
In order to reproduce the undesired behavior:
Launch the app.
Tap on UITextField or UITextView and wait for the keyboard to appear.
Tap on "Next" button and wait for a new controller to be pushed.
Tap on "Back" button and wait for a new controller to be popped.
As a result - the initial view controller will have the active keyboard after the push/pop actions. I need the keyboard to be hidden after the push/pop actions. Also, the keyboard should not be dismissed before the initial view controller becomes invisible, it should be dismissed after viewDidDisappear action.
There are cases where no text field is the first responder but the keyboard is on screen. In these cases, the above methods fail to dismiss the keyboard.
Use the property: isEditable of your textView. Here is a tested code:
override func viewWillAppear(_ animated: Bool) {
self.viewTextView.isEditable = false
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.viewTextView.isEditable = true
}
Results:
Comments from #iWheelBuy:
Sometimes, text views will have inputAccessoryView. The way you do it
will make the keyboard disappear, but the inputAccessoryView will
remain... That is why you should also make inputAccessoryView = nil
or inputAccessoryView = UIView() when setting isEditable = false
The problem happens because responders are managed (restored, saved) by UIKit between viewWillAppear and viewDidAppear, just before view has appeared, and between viewWillDisapear: and viewDidDisapear:, just before view has disappeared. That is why any change made to responders is visible during animation.
Instead of removing responders, to get the effect you want, you can prevent views from becoming responders before view appears again.
The simplest way to do this for UITextField and UITextView is to temporary disable interaction just before view will appear, and then restore it after the view did reappeared.
override func viewWillAppear(_ animated: Bool) {
self.viewTextField.isUserInteractionEnabled = false
self.viewTextView.isUserInteractionEnabled = false
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.viewTextField.isUserInteractionEnabled = true
self.viewTextView.isUserInteractionEnabled = true
}
This will give you the same effect Telegram has.

Black screen at bottom in landscape mode when dismiss keyboard IQKeyboardManager

I am using the IQKeyboardManagerSwift latest version I am having issue black screen at bottom --> When I tap on text field in landscape mode of device keyboard comes up and view of controller and background image goes up black screen of window can be seen at bottom View does not come back to original position after keyboard dismiss.
I want to move container view of the text field up, not the view of the controller and background image, the container view is horizontally and vertically center in auto layout. I tried to add IQLayoutGuideConstraint to container view but no luck. it would be great if you can provide some information to achieve it. please seen storyboard screenshot below
try to remove your firstResponder from viewDidLoad, and put it in viewDidAppear instead, like this
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
yourTextField.becomeFirstResponder()
}
P.S. Do not set your textfield as firstResponder in your viewController.
For ex:- (in ViewDidLoad Method)
[self.yourTextField becomeFirstResponder];
If you have above set up. Please comment(Delete) this line and then try.
All the best.
Have you tried to update your pods?
pod IQKeyboardManagerSwift
workround, place in your VC viewillappear:
override func viewWillAppear(_ animated: Bool) {
if let window = (UIApplication.shared.delegate?.window)! as UIWindow? {
window.backgroundColor = UIColor.white
}
}

ios swift - navigation item background turns black when going back to a screen where the navigation bar is hidden

I thought showing a screenshot would help understand the issue a bit better.
So the context is the following:
I'm in a navigation controller, on the settings screen of the app (which has a navigation item) and when we tap on the back button, we go back to the main screen of the app (for which I've hidden the navigation bar in the viewWillAppear of the main screen because I'm building a custom header view myself).
At soon as I tap on the back button, the navigation bar disappears immediately and I see a black rectangle appears instead until the animation to display the main screen is completed.
Do you know how I can avoid having this black rectangle appear?
Hope the questions makes sense.
Screenshots
Here is the initial settings screen:
When we tape on the back button, this happens... help :D
I know this piece of code is most likely responsible for the error, but I absolutely need to have the navigationBar hidden on the previous screen.
override func viewWillAppear(_ animated: Bool) {
navigationController?.isNavigationBarHidden = true
}
Have you tried the animated method of hiding the navigation bar setNavigationBarHidden(_ hidden: Bool, animated: Bool)?
For Swift3.0
Add below code in First ViewController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
Add below code in Second ViewController
func backButtonPressed() {
navigationController?.setNavigationBarHidden(false, animated: false)
navigationController?.popViewController(animated: true)
}
Add below code in Second ViewController
the color can corresponding your custom
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.view.backgroundColor = UIColor.white
}

Make UINavigationBar transparent animated during transition

I can do translucent UINavigationBar transparent using this code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController!.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
navigationController!.navigationBar.shadowImage = UIImage()
}
But if you try this, you'll notice bad effect during push animation transition. Navigation bar becomes transparent immediately, before left screen completely disappears.
But on the other side, if you try this code....
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController!.setNavigationBarHidden(true: animated: animated)
}
... animation will be perfect: navigation bar disappears only on the right screen, so transition animation looks good.
Is there any way to make UINavigationBar transparent like in the first snippet with animation effect like in the second snippet?
yes, you need to make a method in your code which makes a delay for how many seconds your app is opened for example: [self performSelector:#selector(delay) withObject:nil afterDelay:5.0]; this would make when your app opens, after 5 seconds it will run everything you put in the delay method. So, I would find the second or mess around when you want the UINavigationBar to be transparent and you could easily find the code to make the UINavigationBar transparent but I don't know it off the top of my head.

Resources