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

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

Related

Child UITableViewController does not scroll up when keyboard appears

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.

A method is launched more than once in swift

I have a class:
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
...
NotificationCenter.default.addObserver(
self,
selector: #selector(self.onDealLaunched),
name: Notification.Name("newDealLaunched"),
object: nil)
}
#objc func onDealLaunched(notification: NSNotification) {
let deal = notification.object as! SimpleSaveGame.deal
let i = projectCollection.count
let indexPath = IndexPath(row: i, section: 0)
let projectDeal: project = project(...)
projectCollection.append(projectDeal)
activeDeals.append(deal)
projectCollectionView!.numberOfItems(inSection: 0)
projectCollectionView.insertItems(at: [indexPath])
projectCollectionView.reloadData()
}
#IBAction func corpoButtonPressed(_ sender: Any) {
let vcCorpo = UIStoryboard(name: "Corpo", bundle: nil).instantiateViewController(withIdentifier: "CorpoViewController") as! CorpoViewController
vcCorpo.currentRound = second
vcCorpo.yearlyTaxIncome = gameVariables[14].value
NotificationCenter.default.addObserver(
self,
selector: #selector(self.onReturnCorpo),
name: Notification.Name("corpoBackButtonPressed"),
object: nil)
self.present(vcCorpo, animated: true, completion: nil)
}
}
Then I have CorpoViewController which present custom UITableView and if a user clicks button it shows popup
class CorpoViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
#objc func onLaunchButtonPressed(notification: NSNotification) { // it is launched when player presses negotiate button
dealId = notification.object as! Int
NotificationCenter.default.removeObserver(self, name: Notification.Name("dealLaunchButtonPressed"), object:dealId)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.onPopupClosed),
name: Notification.Name("negotiationPopupClosed"),
object: nil)
self.negotiatedDeal = self.deals[self.dealId]
popup = NegotiationPopup(dealId: self.dealId, deal: self.negotiatedDeal, corpo: self.corpos[self.deals[self.dealId].corpo])
self.view.addSubview(popup)
}
#objc func onPopupClosed(notification: NSNotification) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("negotiationPopupClosed"), object:nil)
negotiatedDeal = notification.object as! SimpleSaveGame.deal
if (negotiatedDeal.status == 2) {
addActiveDeal(deal: negotiatedDeal)
negotiatedDeal.status = 4
deals.remove(at: dealId)
}
}
override func viewDidLoad() {
super.viewDidLoad()
corpoTableView.delegate = self
corpoTableView.dataSource = self
corpoTableView.register(CorpoCell.self, forCellReuseIdentifier: "cellId")
NotificationCenter.default.addObserver(
self,
selector: #selector(self.onLaunchButtonPressed),
name: Notification.Name("dealLaunchButtonPressed"),
object: nil)
planTimer()
}
}
A class implementing:
class CorpoCell: UITableViewCell {
...
#IBAction func launchButtonPressed(_ sender: UIButton) {
NotificationCenter.default.post(name: Notification.Name("dealLaunchButtonPressed"), object: launchButton.tag)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
}
And a class representing the popup:
class NegotiationPopup: UIView {
...
#objc fileprivate func animateOut() {
self.removeFromSuperview()
NotificationCenter.default.post(name: Notification.Name("negotiationPopupClosed"), object: self.negotiatedDeal)
}
}
#objc fileprivate func cancelButtonPressed() {
...
animateOut()
}
}
My problem is that when close popup method animateOut() is launched once, but method #objc func onPopupClosed is launched many times. Number of times depends on how many times I close CorpoViewController and then open it again.
Therefore I guess that I have multiple instances of CorpoViewController in memory, but I do not know how to confirm it and how to fix it.
I replaced observer which indicated that popup was closed with delegate and it fixed the problem somehow.

Text view up when keyboard appears swift

I have textField and textView in the table. When editing textFiled, this code works and all text fields rise above the keyboard. When I edit a textView, nothing happens. It will help to edit the code so that for textView this code also works. Please do not offer third-party libraries. Thanks
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
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 viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
}
#objc
fileprivate func keyboardWillShow(_ notification: Notification) {
let keyboardFrame = ((notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardFrame.height, right: 0.0)
self.tableView.contentInset = contentInsets
self.tableView.scrollIndicatorInsets = contentInsets
UIView.animate(withDuration: 0.25) {
self.tableView.layoutIfNeeded()
self.view.layoutIfNeeded()
}
}
#objc
fileprivate func keyboardWillHide(_ notification: Notification) {
tableView.contentInset = .zero
}
yes you can!
all you need to do is knowing where you textfield position,
then set your scroll Offset
write this :
NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(_:)), name: UITextField.textDidBeginEditingNotification, object: nil)
and your function
#objc func yourMethod(_ sender: NSNotification) {
if let tfActive = sender.object as? UITextField {
let frameRelatedToSuper = tfActive.convert(yourScrollView.contentOffset, to: nil)
//[mockTF convertPoint:self.parentScrollView.contentOffset toView:nil];
yourScrollView.setContentOffset(frameRelatedToSuper, animated: true)
}
}
the rest is your logic.
good luck
note: without adding additional height it might stopped on the top of your textfield
you can declare this
var padding: CGFloat = 10
padding = yourTF.bounds.height + padding
frameRelatedToSuper += padding
edit : the rest is your logic what i meant was ,because you need to calculate your keyboard height and add to framerelatedtosuper

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.

How to pass the variable from a ViewController Class to other Struct?

I have no idea how to pass the data from the class to another struct. This is my ViewController.swift file. And I have another file called Meme.swift which is used to save the struct. I tried to put the struct in ViewController.swift file as well as Meme.swift file but I cannot access the value like topTextField.text and lowTextField.text and use them in the struct. Can anyone help me with this?
ViewController:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var cameraButton: UIBarButtonItem!
#IBOutlet weak var bottomTextField: UITextField!
#IBOutlet weak var topTextField: UITextField!
#IBOutlet weak var imagePickerView: UIImageView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var actionButton: UIBarButtonItem!
let bottomTextFieldDelegate = BottomTextFieldDelegate()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
topTextField.text = "TOP"
bottomTextField.text = "BOTTOM"
topTextField.delegate = self
bottomTextField.delegate = self.bottomTextFieldDelegate
}
func textFieldDidBeginEditing(_ textField: UITextField) {
topTextField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
textField.resignFirstResponder()
actionButton.isEnabled = true
return true
}
#IBAction func shareAction(_ sender: Any) {
let image = generateMemedImage()
let controller = UIActivityViewController(activityItems: [image as Any], applicationActivities: nil)
self.present(controller, animated: true, completion: nil)
controller.completionWithItemsHandler = {(activityType: UIActivityType?, completed:Bool, returnedItems:[Any]?, error: Error?) in
if !completed {
debugPrint("cancelled")
return
}else{
self.save()
self.dismiss(animated: true, completion: nil)
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
configureTextField(textField: topTextField)
configureTextField(textField: bottomTextField)
topTextField.textAlignment = .center
bottomTextField.textAlignment = .center
subscribeToKeyboardNotifications()
actionButton.isEnabled = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
func configureTextField(textField: UITextField) {
textField.defaultTextAttributes = memeTextAttributes
}
func save() -> Meme {
// Create the meme
let memedImage = generateMemedImage()
let meme = Meme(topText: topTextField.text!, bottomText: bottomTextField.text!, originalImage: imageView.image!, memedImage: memedImage)
return meme
}
func generateMemedImage() -> UIImage {
// TODO: Hide toolbar and navbar
navigationController?.setToolbarHidden(true, animated: true)
self.navigationController?.isNavigationBarHidden = true
// Render view to an image
UIGraphicsBeginImageContext(self.view.frame.size)
view.drawHierarchy(in: self.view.frame, afterScreenUpdates: true)
let memedImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
// TODO: Show toolbar and navbar
navigationController?.setToolbarHidden(false, animated: false)
self.navigationController?.isNavigationBarHidden = false
return memedImage
}
func keyboardWillShow(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
func keyboardWillHide(_ notification:Notification) {
view.frame.origin.y = 0
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat { //getting the height of keyboard and use it for func keyboardWillShow to relocate
//the keyboard using keyboardWillShow function
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications() { //setting up the obeserver to be notified when keyboard is shown or not, then execute keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
}
func unsubscribeFromKeyboardNotifications() { //unsubscribe the notification
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
//setting the arributes of the text
let memeTextAttributes:[String:Any] = [
NSStrokeColorAttributeName: UIColor.black,
NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: UIFont(name: "HelveticaNeue-CondensedBlack", size: 40)!,
NSStrokeWidthAttributeName: 3,]
#IBAction func pickAnImageFromAlbum(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true, completion: nil)
}
#IBAction func pickAnImageFromCamera(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
imagePickerView.image = image
imagePickerView.contentMode = .scaleAspectFit
dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController){
dismiss(animated: true, completion: nil)
}
}
BottomFieldDelegate:
import Foundation
import UIKit
class BottomTextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let viewController = ViewController()
textField.resignFirstResponder()
viewController.view.endEditing(true)
let actionButton = viewController.actionButton
actionButton?.isEnabled = true
return true
}
}
First u need to move this function like save() and generateMemedImage() into your ViewController class and then you can access topTextField.text and lowTextField.text
Modify your struct like below, and do not put nsobject in struct
struct Meme {
var topText: String
var bottomText: String
var memedImage: UIImage
var originalImage: UIImage
init(topText: String, bottomText: String, originalImage: UIImage, memedImage: UIImage) {
self.topText = topText
self.bottomText = bottomText
self.originalImage = originalImage
self.memedImage = memedImage
}
}
Remove save() from Meme struct and put that code in your Viewcontroller file.
Modify your function which return Meme struct object.
func save() -> Meme {
// Create the meme
let meme = Meme(topText: topTextField.text!, bottomText: bottomTextField.text!, originalImage: imageView.image!, memedImage: memedImage)
return meme
}
And you can store in Array like below.
let array:[Meme] = []
array.append(save())