Child UITableViewController does not scroll up when keyboard appears - swift

I have searched as many posts as I found and still I cannot solve the problem. Please keep in mind that I have tried all the solutions provided that I have found and still when the keyboard appears the table does not scroll up, nor does it adjust its content view. The table v.c. is a child of a v.c., it is being pushed by the nav con (modal also does not work), below all of this sits a tab bar controller. Below is the code which I consider to be relevant.
Code inside the v.c.:
override func viewDidLoad() {
super.viewDidLoad()
safeGuide = self.view.safeAreaLayoutGuide
view.backgroundColor = UIColor.customColoursForAllElements(colourName: "background blue")
programMainMenu = self.tabBarController as? ProgramMainMenu
setTopViews()
let msgsArea = ChatMsgsAreaTVC(typeForCurrentInstance: .singleChat(otherMemberName: self.otherMemberName), userUID: self.userUID, searchResultMsg: msgSelectedInSearch != nil ? msgSelectedInSearch : nil, searchTxt: self.searchTxt)
msgsArea.valuesForSingleChat = chatDetails
displayChild(controller: msgsArea)
}
fileprivate func displayChild(controller: UIViewController) {
self.addChild(controller)
self.view.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false
let childConstraints = [
controller.view.topAnchor.constraint(equalTo: detailsContainer.bottomAnchor, constant: 10),
controller.view.bottomAnchor.constraint(equalTo: safeGuide.bottomAnchor, constant: -55),
controller.view.leadingAnchor.constraint(equalTo: backBtn.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: optionBtn.trailingAnchor)
]
NSLayoutConstraint.activate(childConstraints)
controller.didMove(toParent: self)
}
the code below is from the child table view controller:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide), name: UIResponder.keyboardDidHideNotification, object: nil)
configureMsgsTable()
observeAudioRecAccess()
setupAudioRecorder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.resignFirstResponder()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {self.becomeFirstResponder()}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
override var canBecomeFirstResponder: Bool {return true}
override var canResignFirstResponder: Bool {return true}
#objc func keyboardWillShow(notification: Notification) {
// note: changing the content size of the table does not work at all.
}
#objc fileprivate func keyboardDidHide() {
if !bypassForSetup {bypassForSetup = true}
else {
if msgTxtArea.isFirstResponder {msgTxtArea.resignFirstResponder()}
if addFileToMsgBtn.isSelected {addFileToMsgBtn.isSelected = false; addFileToMsgBtn.resignFirstResponder(); alterFileToMsgBtn(selected: false)}
}
}
fileprivate func configureMsgsTable() {
tableView.contentInsetAdjustmentBehavior = .always
tableView.backgroundColor = .clear
tableView.keyboardDismissMode = .interactive
tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
tableView.estimatedRowHeight = 100
tableView.sectionFooterHeight = 0.0
tableView.scrollsToTop = false
tableView.setBottomInset(to: -15)
}
I also have a custom input accessory view that I have not included in this code.

Related

Swift: deallocate modally presented view controller

I have a modally presented SearchviewController that contains a UISearchController.
When swiping down it gets deallocated, but only if the searchControllers searchBar is not in editing mode. Only if I press its cancel button in advance, it gets deallocated.
How can I make sure it gets deallocated, even when in editing mode? There are definitely no strong self references within any closures...
Presenting ViewController:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addButton()
}
func addButton() {
let mediumConfiguration = UIImage.SymbolConfiguration(scale: .large)
var checkButtonImage = UIImage(systemName: "plus", withConfiguration: mediumConfiguration)
checkButtonImage = checkButtonImage?.withTintColor(.label)
let button = UIButton(type: .contactAdd)
button.addTarget(self, action: #selector(onAddViewControllerButtonClicked(sender:)), for: .touchUpInside)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
#objc func onAddViewControllerButtonClicked(sender: UIButton) {
let viewController = SearchViewController()
viewController.view.backgroundColor = .secondarySystemBackground
let navigationController = UINavigationController()
navigationController.viewControllers = [viewController]
self.present(navigationController, animated: true)
}
}
Presented ViewController:
class SearchViewController: UIViewController, UISearchBarDelegate, UISearchResultsUpdating {
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
}
var searchController: UISearchController?
func configureSearchController() {
//search
searchController = UISearchController(searchResultsController: nil)
searchController?.searchResultsUpdater = self
searchController?.searchBar.delegate = self
searchController?.hidesNavigationBarDuringPresentation = false
searchController?.searchBar.searchBarStyle = .minimal
searchController?.searchBar.keyboardType = .webSearch
self.navigationItem.searchController?.searchBar.backgroundColor = .clear
self.navigationItem.searchController = searchController
self.navigationItem.hidesSearchBarWhenScrolling = false
self.definesPresentationContext = true
self.navigationItem.searchController = searchController
}
func updateSearchResults(for searchController: UISearchController) {
return
}
//check deallocation
deinit { print("\(NSStringFromClass(type(of: self))): deallocated") }
}
Can you help with that?
Thank you in advance!
Adding
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.navigationItem.searchController = nil
}
to SearchViewController fixes the problem for me, but admittedly I have no idea as to why this is necessary.

Swift How can I keep only the first page with a large title?

The large title of navigation item comes out when the app just describes execution. However, when I move to another page and come back, that mode is turned off. Create a viewDidLoad for each page as follows: What's the problem?
class CollectionViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
}
next page
class AssetCollectionViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.largeTitleDisplayMode = .never
}
Possibly adding code didn't work.
override func viewDidDisappear(_ animated: Bool) {
self.navigationItem.largeTitleDisplayMode = .always
}
Change titleDisplay mode in viewWillppear function as it will always execute
class CollectionViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
}
How about doing all the implementation in your CollectionViewController like so:
override func viewWillDisappear(_ animated: Bool) {
navigationController?.navigationBar.prefersLargeTitles = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = true
}
When you're setting navigationItem.largeTitleDisplayMode in viewDidDisappear(_:), you're doing it for the current controller's navigationItem.
I suggest you to create UINavigationController subclass with:
override func viewDidLoad() {
super.viewDidLoad()
navigationBar.prefersLargeTitles = true
}
Then set navigationItem.largeTitleDisplayMode to .never in controllers, where you don't want large title.

I am creating Keyboard Extension (swift) But unable to get height of keyboard

import UIKit
class KeyboardViewController: UIInputViewController {
var heightKeyboard : CGFloat?
#IBOutlet var nextKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
// Perform custom UI setup here
self.nextKeyboardButton = UIButton(type: .system)
self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), for: [])
self.nextKeyboardButton.sizeToFit()
self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false
self.nextKeyboardButton.addTarget(self, action: #selector(handleInputModeList(from:with:)), for: .allTouchEvents)
self.view.addSubview(self.nextKeyboardButton)
self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(notification:)), name: UIResponder.keyboardDidShowNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(true)
NotificationCenter.default.removeObserver(self)
}
#objc func keyboardWillAppear(notification: NSNotification) {
//Do something here
print("keyboardWillAppear()")
print("keyboardShown")
if let infoKey = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey],let rawFrame = (infoKey as AnyObject).cgRectValue {
let keyboardFrame = view.convert(rawFrame, from: nil)
self.heightKeyboard = keyboardFrame.size.height
print(self.heightKeyboard)
}
}
#objc func keyboardWillDisappear() {
print("keyboardWillDisappear()")
}
override func viewWillLayoutSubviews() {
self.nextKeyboardButton.isHidden = !self.needsInputModeSwitchKey
super.viewWillLayoutSubviews()
}
override func textWillChange(_ textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(_ textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
textColor = UIColor.white
} else {
textColor = UIColor.black
}
self.nextKeyboardButton.setTitleColor(textColor, for: [])
}
}
I am creating Keyboard Extension (swift) But unable to get height of keyboard . I am using storyboard for keyboard creation. func keyboardWillAppear(),func keyboardWillDisappear() never getting called.
So unable to get keyboard size based on different sizes and orientation

View above keyboard don't show up

I want to have a View with a textField an a send button above my keyboard, when the keyboard is shown. But this doesn't work.
I already implemented this in my code:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_ :)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tabBarController?.tabBar.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
tabBarController?.tabBar.isHidden = false
}
// MARK: - Keyboard stuff
#objc func keyboardWillShow(_ notification: NSNotification) {
let keyboardFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
print("hier ist \(keyboardFrame!)")
UIView.animate(withDuration: 0.1) {
self.bottomConstraint.constant = keyboardFrame!.height
self.view.layoutIfNeeded()
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
UIView.animate(withDuration: 0.1) {
self.bottomConstraint.constant = 0
self.view.layoutIfNeeded()
}
}
Check out textField inputAccessoryView. There's a tutorial here that explains what you're trying to do.

UISearchController - Black Rectangle

I'm implementing a simple tableViewController with UISearchcontroller.
The problem is that every time that I press the search field then a black rectangle appear right after the keyboard shows up.
I also tried to use definesPresentationContext and searchBarStyle but it keep showing the rectangle. On the other hand, it looks like doesn't happen in the simulator since there is no keyboard.
Update: Below some photos.
ViewController:
class ListGlobalViewController: UITableViewController, StoryboardSceneBased, ViewModelBased {
static var sceneStoryboard: UIStoryboard = UIStoryboard(name: "DataSelectorViewController", bundle: Bundle.main)
// --------------------
// MARK: - Properties
// --------------------
var viewModel: ListGlobalViewModel!
private let disposeBag = DisposeBag()
private let searchController: UISearchController = {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.searchBarStyle = .minimal
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = true
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
configure(with: viewModel)
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.searchController.isActive = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
// --------------------
// MARK: - Functions
// --------------------
func configure(with viewModel: ListGlobalViewModel) {
self.tableView.delegate = nil
self.tableView.dataSource = nil
viewModel.outputs.listDataObservable.bind(to: self.tableView.rx.items(cellIdentifier: "listGlobalCell", cellType: ListGlobalCell.self)) { (index, model, cell) in
cell.model = model
}.disposed(by: disposeBag)
searchController.searchBar.rx.text.filterNil().throttle(1, scheduler: MainScheduler.instance).distinctUntilChanged().subscribe(viewModel.inputs.searchBarObservable).disposed(by: disposeBag)
}
}
Navigation Default Values:
let controller = UINavigationController()
controller.navigationBar.isTranslucent = false
controller.navigationBar.prefersLargeTitles = true
controller.definesPresentationContext = true
controller.navigationBar.titleTextAttributes = [ NSAttributedString.Key.font: UIFont.bold(size: 24), NSAttributedString.Key.foregroundColor: UIColor.black ]
if #available(iOS 11, *) {
controller.navigationBar.largeTitleTextAttributes = [ NSAttributedString.Key.font: UIFont.bold(size: 33), NSAttributedString.Key.foregroundColor: UIColor.black ]
}
return controller
Pictures:
Bugged
Use UITextField instead if UISearchBar. You can give any design to uitextfield. UISearch bar has limited designable attributes. Also you can reach any search function with textfield.