SWIFT4: How to have two tap gestureRecognizers work together - ios

NB: My gesture recognizers are not different and are in different viewControllers.
TLDR: How can I do to have both my tap gesture recognisers to work?
I have created a side menu from some container views and I added to it a gesture recognizer that allows me to dismiss it on tap. Then I created another ViewController where i have some textfields. Since I wanted the keyboard to dismiss on tap I even added a gesture recognizer that allows me to hide the keyboard whenever i tap the view. Now I noticed that having the gesture that hides the keyboard doesn't let my side menu hide on tap:
SIDE MENU VIEW CONTROLLER
#IBOutlet weak var bigContainer: UIView!
#IBOutlet weak var sideMenuConstraint: NSLayoutConstraint!
var sideMenuOpen = false
var gesture : UITapGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(toggleSideMenu), name: NSNotification.Name("ToggleSideMenu"), object: nil)
gesture = UITapGestureRecognizer(target: self, action: #selector(ContainerViewController.toggleSideMenu))
}
#objc func toggleSideMenu() {
if sideMenuOpen {
sideMenuOpen = false
sideMenuConstraint.constant = -240
self.bigContainer.removeGestureRecognizer(gesture!)
} else {
sideMenuOpen = true
sideMenuConstraint.constant = 0
self.bigContainer.addGestureRecognizer(gesture!)
}
}
MAIN VIEW VIEW CONTROLLER
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var menuOutlet: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let endEditingTapGesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
endEditingTapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(endEditingTapGesture)
}
#IBAction func toggleSideMenu(_ sender: Any) {
print("Toggle side menu")
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
}
I tried removing the tap gesture to hide the keyboard in my toggleSideMenu button but it didn't work.

In your MAIN VIEW CONTROLLER Replace with below code:
var endEditingTapGesture:UIGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
endEditingTapGesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
endEditingTapGesture.cancelsTouchesInView = false
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notication:)), name: UIResponder.keyboardWillHideNotification, object: nil) //Add keyboard notification
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notication:)), name: UIResponder.keyboardWillShowNotification, object: nil)
}
#IBAction func toggleSideMenu(_ sender: Any) {
print("Toggle side menu")
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
//Keyboard method
#objc func keyboardWillHide(notication:Notification) {
view.removeGestureRecognizer(endEditingTapGesture)
}
#objc func keyboardWillShow(notication:Notification) {
view.addGestureRecognizer(endEditingTapGesture)
}

Related

Why i am getting gap between view and scrollview while moving keyboard up in swift?

I have arranged views like below
I am getting gap like below Why?
when view is down it looks good, when i tap on textfield then total view goes up and when i scrool tableview i am getting gap between like below.
this is my code:
import UIKit
class ChatViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var belowtextFieldHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var catViewBottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboard(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboard(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboard(notification:Notification) {
guard let keyboardReact = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
return
}
if notification.name == UIResponder.keyboardWillShowNotification || notification.name == UIResponder.keyboardWillChangeFrameNotification {
self.view.frame.origin.y = -keyboardReact.height
} else {
self.view.frame.origin.y = 0
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
Text fields have a property called inputAccessoryView, make sure you are not setting the view anywhere and that it is nil when the keyboard is invoked.
It looks like you have one set up.

How to remove popView if we tap anywhere in the background except tapping in popView in swift

I have created one popView with textfield and button in ViewController. if i click button then popView is appearing, and i am able to enter text in textfield and submit is working, and if i tap anywhere in view also i am able to remove popView, but here i want if i tap on anywhere in popView i don't want to dismiss popView, Please help me in the code.
here is my code:
import UIKit
class PopUPViewController: UIViewController {
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
popView.isHidden = true
// Do any additional setup after loading the view.
}
#IBAction func butnAct(_ sender: Any) {
view?.backgroundColor = UIColor(white: 1, alpha: 0.9)
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView))
view.addGestureRecognizer(tap)
}
#objc func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
}
In my code if i tap anywhere in the view popView is removing even if i tap on popView also its removing, i don't need that, if i tap on popView then popView need not to be remove.
Please help me in the code
You can override the touchesBegan method which is triggered when a new touch is detected in a view or window. By using this method you can check a specific view is touched or not.
Try like this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view != self.popView {
dismissView()
}
}
func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
It's not the way I would have architected this, but to get around the problem you face you need to adapt your dismissView method so that it only dismisses the view if the tap is outside the popView.
To do this modify your selector to include the sender (the UITapGestureRecogniser )as a parameter:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView(_:)))
and then in your function accept that parameter and test whether the tap is inside your view, and if so don't dismiss the view:
#objc func dismissView(_ sender: UITapGestureRegognizer) {
let tapPoint = sender.location(in: self.popView)
if self.popView.point(inside: tapPoint, with: nil)) == false {
self.popView.isHidden = true
view?.backgroundColor = .white
}
}
Your Popup view is inside the parent view of viewcontroller that's why on tap of popview also your popview is getting hidden.
So to avoid just add a view in background and name it bgView or anything what you want and replace it with view. And it will work fine .
Code:
#IBOutlet weak var bgView: UIView!//Add this new outlet
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
popView.isHidden = true
}
#IBAction func butnAct(_ sender: Any) {
bgView.backgroundColor = UIColor(white: 1, alpha: 0.9)//change view to bgView[![enter image description here][1]][1]
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissView))
bgView.addGestureRecognizer(tap)//change view to bgView
}
#objc func dismissView() {
self.popView.isHidden = true
bgView.backgroundColor = .white//change view to bgView
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}

Make keyboard disappear when clicking outside of Search Bar - SWIFT

I have a UIViewController and I have embedded a Search Bar and a Collection View. When I press on the searchbar, the keyboard appears. I would like to hide this keyboard if the user decides to change his mind by tapping any where on the screen but the search bar. I have tried the following without success:
Adding a Tap Gesture Recognizer
using 'self.mySearchBar.endEditing(true)'
class CollectionViewFolder: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate ,UICollectionViewDelegateFlowLayout, UISearchBarDelegate{
/*** OUTLETS ***/
#IBOutlet weak var mySearchBar: UISearchBar!
// 1. I have tried adding a Tap Gesture Recognizer
// TAP ON SCREEN LISTENER
#IBAction func tapOnScreen(_ sender: UITapGestureRecognizer) {
print("tap tap ...")
self.mySearchBar.resignFirstResponder()
}
// 2. Added the following to the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.mySearchBar.endEditing(true)
}
}
You can use this extension.
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
Usage. In your viewController:
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
}

This keyboard won't leave

I'm having a weird behavior from a keyboard. But only on one of the view controllers. I have another view controller with a UITextField and that one works charms (as soon as I press the return button it disappears). And the weird part is that that one isnt even setup at all. it only has becomefirstresponder on viewdidload and resignfirstresponder on viewwilldissapear.
THIS keyboard however, VIDEO OF NASTY KEYBOARD is quite the nasty booger.
NOTE: In the video I do the following:
* Tap the UITextfield
* Keyboard Appears (view shifts up)
* I type lol and then press send (nothing happens)
* I start tapping frantically all around the screen (except the UITextField)
* Keyboard dissapears
Here's the code for this View:
class ChatScreenVC: UIViewController, UITextFieldDelegate {
var gameDelegate: GameManagerDelegate?
var chatLogString: String = ""
var offsetY:CGFloat = 0
#IBOutlet weak var chatView: UIView!
#IBOutlet weak var closeBtn: UIButton!
#IBOutlet weak var titleBtn: UILabel!
#IBOutlet weak var chatLog: UITextView!
#IBOutlet weak var chatInput: UITextField!
#IBOutlet weak var sendBtn: UIButton!
#IBOutlet weak var viewCenterConstraint: NSLayoutConstraint!
#IBAction func inputTapped(_ sender: Any) {
chatInput.becomeFirstResponder()
}
#IBAction func closeTapped(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func sendTapped(_ sender: Any) {
if chatInput.text != nil && chatInput.text != "" && chatInput.text != " " {
gameDelegate?.sendChat(message: chatInput.text!)
}
chatInput.text = ""
chatInput.resignFirstResponder()
self.view.endEditing(true)
}
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
viewCenterConstraint.constant = -keyboardHeight
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print("THIS IS HAPPENING!")
chatInput.resignFirstResponder()
self.view.endEditing(true)
return true
}
#objc func keyboardWillHide(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
viewCenterConstraint.constant = 0
}
}
#objc func reloadData() {
chatLogString = (gameDelegate?.updateChatString())!
chatLog.text = chatLogString
}
override func viewDidLoad() {
super.viewDidLoad()
chatLog.text = chatLogString
chatLog.isEditable = false
chatInput.autocorrectionType = .no
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
setupView()
animateView()
}
override func viewWillDisappear(_ animated: Bool) {
chatInput.resignFirstResponder()
}
override func viewDidAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(self.reloadData), name: Notification.Name(rawValue: SMACK_TALK), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: SMACK_TALK), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
}
Now before you go and tell me "not to use both the resignfirstresponder and the endediting(true)" I gotta clear out that I tried both individually and together and still don't get any response.
You can resign keyboard before sendChat(). I think it is sync call and it will block main thread for sometimes thats why this weird behaviour occurs.
chatInput.resignFirstResponder() before you are making API call.
#IBAction func sendTapped(_ sender: Any) {
if chatInput.text != nil && chatInput.text != "" && chatInput.text != " " {
chatInput.resignFirstResponder()
self.view.endEditing(true)
gameDelegate?.sendChat(message: chatInput.text!)
}
}

NSNotificationCenter. Send data from one to others ViewControllers Swift

I'm trying to show some data in a LastVC depending of the choose of the user in each view. I Let you the image of our App
I am using NSNotificationCenter but the labels in the lastVC do not change.
What is wrong?
#IBOutlet weak var colorLabelSelected: UILabel!
#IBOutlet weak var nextOutlet: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
nextOutlet.hidden = true
}
#IBAction func greenButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setGreenColorID", object: nil)
colorLabelSelected.text = "You have selected a green BG color"
nextOutlet.hidden = false
}
#IBAction func blueButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setGreenColorID", object: nil)
colorLabelSelected.text = "You have selected a blue BG color"
nextOutlet.hidden = false
}
#IBAction func pinkButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setGreenColorID", object: nil)
colorLabelSelected.text = "You have selected a pink BG color"
nextOutlet.hidden = false
}
}
class ViewController2: UIViewController {
#IBOutlet weak var nextOutlet: UIButton!
#IBOutlet weak var titleLabelSelected: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
nextOutlet.hidden = true
}
#IBAction func newsButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setNewsThemeID", object: nil)
titleLabelSelected.text = "You have selected News as title"
nextOutlet.hidden = false
}
#IBAction func sportButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setSportThemeID", object: nil)
titleLabelSelected.text = "You have selected Sport as title"
nextOutlet.hidden = false
}
#IBAction func wealthButton(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("setWealthThemeID", object: nil)
titleLabelSelected.text = "You have selected Wealth as title"
nextOutlet.hidden = false
}
}
The same with VC3
class lastViewController: UIViewController {
#IBOutlet weak var finalColorLabel: UILabel!
#IBOutlet weak var finalThemeLabel: UILabel!
#IBOutlet weak var finalRelevanceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setBlueColor:", name: "setBlueColorID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setGreenColor:", name: "setGreenColorID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setPinkColor:", name: "setPinkColorID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setNewsTheme:", name: "setNewsThemeID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setSportTheme:", name: "setSportThemeID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setWealthTheme:", name: "setWealthThemeID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setTenPoints:", name: "setTenPointsID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setSevenPoints:", name: "setSevenPointsID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setFivePoints:", name: "setFivePointsID", object: nil)
}
//setColors
func setBlueColor(notification: NSNotification) {
finalColorLabel.text = "blue"
}
func setGreenColor(notification: NSNotification) {
finalColorLabel.text = "green"
}
func setPinkColor(notification: NSNotification) {
finalColorLabel.text = "pink"
}
//setTheme
func setNewsTheme(notification: NSNotification) {
finalThemeLabel.text = "news"
}
func setSportTheme(notification: NSNotification) {
finalThemeLabel.text = "sport"
}
func setWealthTheme(notification: NSNotification) {
finalThemeLabel.text = "wealth"
}
//setRelevance
func setTenPoints(notification: NSNotification){
finalRelevanceLabel.text = "ten"
}
func setSevenPoints(notification: NSNotification){
finalRelevanceLabel.text = "seven"
}
func setFivePoints(notification: NSNotification){
finalRelevanceLabel.text = "five"
}
Are you sure the lastVC even exist when you post notifications from the other ViewControllers? If you segue to it as a part of a normal show segue the view controller get's recreated every time you segue to it.
If you want to pass data from one viewController to the other you should probably do it in prepareForSegue. I'd approach it this way
1) create an object:
struct userSettings {
var backgroundColor: NSColor?
var title: String?
var relevance: Int?
}
2) fill appropriate field in each ViewController
3) pass it in prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case segueToController:
if let viewController = segue.destinationViewController as? myControllerClass {
viewController.userSettings = userSettings
}
default: break
}
}
}

Resources