InputBarAccessoryView view does not show after Tab changed and back - ios

I am using InputBarAccessoryView for an input accessory view of my chat controller. And ChatViewController is part of UITabbarViewCntroller.
When open my chat view first time InputBarAccessoryView show but when tab change and back, it is not showing.
Can you pease help to solve that issue?
Edited:
var messageInputBar = InputBarAccessoryView()
override var canBecomeFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
return messageInputBar
}
Kamal

Related

iPhone X hide home indicator on view controller

I have a view controller that takes up the whole screen from top to bottom. I would like to hide the home bar indicator on the bottom of the screen on iPhone X devices.
How can I do this in iOS 11?
You should override prefersHomeIndicatorAutoHidden in your view controller to achieve that:
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
There is another alternative. If you are looking for the behavior where the indicator dims, then when the user swipes up it activates, and when they swipe up again the home action is invoked (I.E., two swipes are needed to invoke), then the answer is here: iPhone X home indicator behavior. The short of it is to override on your UIViewController:
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
return UIRectEdge.bottom
}
prefersHomeIndicatorAutoHidden only hides the indicator, but will not suppress the gesture.
And you will get what you want (If I understand your comments correctly - your question and the selected answer seem to imply the other answer).
If your window?.rootViewController is a UITabBarController or UINavigationController, just inherit it and add two function as follows,
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
//#available(iOS 11, *)
override var childViewControllerForHomeIndicatorAutoHidden: UIViewController? {
return nil
}
Implement -(BOOL)prefersHomeIndicatorAutoHidden in your UIViewController and return YES.
Read more https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden.
I tried to set it and return true only when I am in full-screen :
override var prefersHomeIndicatorAutoHidden: Bool { isNavigationBarAndTabBarHidden }
but it doesn't seem to work... isNavigationBarAndTabBarHidden is a custom variable tied to my fullscreen extension.
Edit: We need to call setNeedsUpdateOfHomeIndicatorAutoHidden every time we update prefersHomeIndicatorAutoHidden's value.
var isNavigationBarAndTabBarHidden = false {
didSet {
setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
override func prefersHomeIndicatorAutoHidden() -> Bool {
return true
}
I suppose you can add this method in your AppDelegate for hide home indicator on all of your ViewControllers.

inputAccessoryView appearing with animation after view appears in iOS

I have an inputAccessoryView for text input in a chat app. I implemented the inputAccessoryView using the following:
override var inputAccessoryView: UIView? {
get {
setupInputToolbar()
return inputToolbar
}
}
override var canBecomeFirstResponder: Bool {
return true
}
This occurs in a viewController which is a childViewController. I use a segmented control to display this chat viewController with the inputAccessoryView.
The problem I'm having is the inputAccessoryView will only display if I place self.becomeFirstResponder() in the viewDidAppear() of the chat viewController (child).
If I omit self.becomeFirstResponder() or place it in viewDidLoad() or viewWillAppear(), the inputAccessoryView does not display.
The problem with having it in viewDidAppear() is that it displays with an animation after the view is already on screen which is not what I want.
I accomplished this by first adding a viewHasPerformedSubviewLayoutAtLeastOnce property to the ViewController.
private var viewHasPerformedSubviewLayoutAtLeastOnce = false
Making canBecomeFirstResponder dependent on the above property.
override var canBecomeFirstResponder: Bool {
return viewHasPerformedSubviewLayoutAtLeastOnce
}
Then updating that property in viewDidLayoutSubviews() and becoming first responder inside a UIView.performWithoutAnimation block.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if viewHasPerformedSubviewLayoutAtLeastOnce == false {
viewHasPerformedSubviewLayoutAtLeastOnce = true
UIView.performWithoutAnimation { becomeFirstResponder() }
}
}
It all feels a little hacky, but it results in the view appearing without any animations.

Slide UIInputView with UIViewController like Slack

I'd like to use the UIViewController's input accessory view like this:
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView! {
return self.bar
}
but the issue is that I have a drawer like view and when I slide the view open, the input view stays on the window. How can I keep the input view on the center view like Slack does it.
Where my input view stays at the bottom, taking up the full screen (the red is the input view in the image below):
There are two ways to do this exactly like Slack doing it, Meiwin has a medium post here A Stickler for Details: Implementing Sticky Input Field in iOS to show how he managed to do this which he actually puts an empty UIView as an inputAccessoryView then track it’s coordinates on screen to know where to put his custom view in relation with the empty view, this way can be helpful if you are going to support SplitViewController on iPad, but if you are not interested in this way, you can see how I managed to do this like this image
Here is before swiping
Here is after
All I did was actually taking a snapshot from the inputAccessoryView window and putting it on the NavigationController of the TableViewController
I am using SideMenu from Jon Kent and it’s pretty easy to do it with the UISideMenuNavigationControllerDelegate
var isInputAccessoryViewEnabled = true {
didSet {
self.inputAccessoryView?.isHidden = !self.isInputAccessoryViewEnabled
if self.isInputAccessoryViewEnabled {self.becomeFirstResponder()}
}
}
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
let inputWindow = UIApplication.shared.windows.filter({$0.className == "UITextEffectsWindow"}).first
self.inputAccessoryViewSnapShot = inputWindow?.snapshotView(afterScreenUpdates: false)
if let snapShotView = self.inputAccessoryViewSnapShot, let navView = self.navigationController?.view {
navView.addSubview(snapShotView)
}
self.isInputAccessoryViewEnabled = false
}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
self.inputAccessoryViewSnapShot?.removeFromSuperview()
self.isInputAccessoryViewEnabled = true
}
I hope that helps :)

Fullscreen video in UIWebView interferes with UIViewController's input accessory view

I have a view which I set up as input accessory view for view controller the following way:
#IBOutlet private weak var bottomPane: UIView!
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView {
return bottomPane
}
Everything works just fine until I try to view YouTube video in fullscreen mode (video is loaded in UIWebView). When video enters fullscreen mode, keyboard and my input accessory view disappear (which is normal, I guess), but when I exit fullscreen mode, they do not appear. If I keep the reference to bottomPane weak, it becomes nil and application crashes, if I change it to strong, input accessory view remains hidden until the keyboard appears next time.
Can anybody explain what's going on and how to fix this?
Here's what's going on.
When user interacts with UIWebView, it becomes first responder and inputAccessoryView provided by view controller disappears (no idea why behavior in this case is different from, say, UITextField). Subclassing UIWebView and overriding inputAccessoryView property does not work (never gets called). So I block interaction with UIWebView until user loads video.
private func displayVideo(URL: String) {
if let video = Video(videoURL: URL) {
// load video in webView
webView.userInteractionEnabled = true
} else {
webView.userInteractionEnabled = false
}
}
When user loads video, the only way to detect that user has entered/exited fullscreen mode is to listen to UIWindowDidBecomeKeyNotification and UIWindowDidResignKeyNotification and detect when our window loses/gains key status:
//in view controller:
private func windowDidBecomeKey(notification: NSNotification!) {
let isCurrentWindow = (notification.object as! UIWindow) == view.window
if isCurrentWindow {
// this restores our inputAccessoryView
becomeFirstResponder()
}
}
private func windowDidResignKey(notification: NSNotification!) {
let isCurrentWindow = (notification.object as! UIWindow) == view.window
if isCurrentWindow {
// this hides our inputAccessoryView so that it does not obscure video
resignFirstResponder()
}
}
And, of course, since inputAccessoryView can be removed at some point, we should recreate it if needed:
//in view controller:
override var inputAccessoryView: UIView {
if view == nil {
// load view here
}
return view
}

Prevent a view from being loaded or appearing

I have a tab bar controller as the starting point of my app, where one of the tab and what happens subsequently is meant for admins only. So I was password protecting a tab. I thought of adding a little modal dialogue in the viewDidLoad function of my view controller (which by the way is a UITableViewController),
Suppose I can get the text that the user entered in the dialogue box in the variable inputTextField.
The relevant section of the code from viewDidLoad():
if inputTextField?.text != "secret" {
return
}
super.viewDidLoad()
But it does not work. Any hint appreciated. Sorry if it is too basic, I am completely new to iOS and Swift programming, so pardon my ignorance folks.
Here is a simple example. Lots of ways. I dropped two UIViews in Storyboard on the first tab's VC. The one in the back I gave a dark color to simulate the hidden secret view (secretView). Inside of the view on top (entryView) I dragged a label "Enter Passcode" and a text field (passCode). I just hid the back view unless the secret code was correct.
import UIKit
class FirstViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.passCode.delegate = self
entryView.hidden = false
secretView.hidden = true
}
#IBOutlet weak var entryView: UIView!
#IBOutlet weak var secretView: UIView!
let secretCode = "X"
#IBOutlet weak var passCode: UITextField!
func textFieldShouldReturn(textField: UITextField!) -> Bool {
if textField.text == secretCode {
entryView.hidden = true
secretView.hidden = false
} else {
self.passCode.text = "Try Again!"
}
textField.resignFirstResponder()
return true
}
}

Resources