UITapGestureRecognizer in UICollectionViewCell for UIImageView does not work - ios

I'm trying to put a UITapGestureRecognizer on UIImageView in UICollectionViewCell but it does not work. Here is the code:
class ImageGalleryCell : UICollectionViewCell {
#IBOutlet weak var imgPhoto: UIImageView!
let uniqueTag = String.random()
func setup(){
let rec = UITapGestureRecognizer.init(target: self, action: #selector(tap))
imgPhoto.addGestureRecognizer(rec)
}
#objc func tap(){
print("tap")
}
}
What am I doing wrong?

Based on the code snippet you provide, it seems that you should manually set the image view isUserInteractionEnabled property to true -since it is false by default-, in setup:
func setup(){
let rec = UITapGestureRecognizer.init(target: self, action: #selector(tap))
// here we go:
imgPhoto.isUserInteractionEnabled = true
imgPhoto.addGestureRecognizer(rec)
}
Also, I would suggest to call setup method in the cell class itself, in the awakeFromNib() instead of calling it in the view controller - tableView(_:cellForRowAt:) (or any method in the view controller layer), simply like this:
class ImageGalleryCell : UICollectionViewCell {
#IBOutlet weak var imgPhoto: UIImageView!
//let uniqueTag = String.random()
func setup(){
let rec = UITapGestureRecognizer.init(target: self, action: #selector(tap))
// here we go:
imgPhoto.isUserInteractionEnabled = true
imgPhoto.addGestureRecognizer(rec)
}
#objc func tap(){
print("tap")
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
}
That's because such a behavior should be setup only for one time when delivering the cell but not each time it has been shown.

Related

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
}

Interacting with custom UIView inside view controller

I am created a UIView which I want to display in my view controller. I have created the UIView and it shows with other UI components, but the problems I have now is I con not interact with the elements of the on the UIView.
below is my code
class SliderView: CustomView {
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var sliderImage: UIImageView!
#IBOutlet weak var sliderText: UILabel!
override func initialize() {
super.initialize()
let name = String(describing: type(of: self))
let nib = UINib(nibName: name, bundle: .main)
nib.instantiate(withOwner: self, options: nil)
self.addSubview(self.containerView)
self.containerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.containerView.topAnchor.constraint(equalTo: self.topAnchor),
self.containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
])
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return sliderImage.frame.contains(point)
}
#IBAction func clickme(_ sender: UIButton) {
print("SWIPPERD minmax22g")
}
}
in the viewcontroller
weak var sliderView: SliderView!
override func loadView() {
super.loadView()
let sliderView = SliderView()
self.view.addSubview(sliderView)
NSLayoutConstraint.activate([
sliderView.topAnchor.constraint(equalTo: self.view.topAnchor),
sliderView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
sliderView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
self.sliderView = sliderView
}
override func viewDidLoad() {
super.viewDidLoad()
sliderView.sliderText.text = "HOOOOO WORKS"
}
1- You shouldn't add an outlet as a subview again inside the custom view
#IBOutlet weak var containerView: UIView!
self.addSubview(self.containerView)
add this method and use it to get an instance
static func getInstance() -> SliderView {
return Bundle.main.loadNibNamed("SliderView", owner: self, options: nil)![0] as! SliderView
}
2- This will make the imageView the only active frame inside the view
return sliderImage.frame.contains(point)
3- Don 't add the subview inside loadView , add it inside viewDidLoad
let sliderView = SliderView()
to
let sliderView = SliderView.getInstance()

Swift Delegate is not being called

I have completed all the needed code for delegate to work. In my viewcontroller:
class ViewController: UIViewControllerCustomViewDelegate
I also have this:
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
func goToNextScene() {
print("GOTOSCENE2")
}
And in my custom view I have this:
import UIKit
protocol CustomViewDelegate: class { // make this class protocol so you can create `weak` reference
func goToNextScene()
}
#available(iOS 10.0, *)
class ImageHeaderView : UIView {
#IBOutlet weak var followme: UISwitch!
#IBOutlet weak var profileImage : UIImageView!
#IBOutlet weak var backgroundImage : UIImageView!
weak var delegate: CustomViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor(hex: "E0E0E0")
self.profileImage.layer.cornerRadius = self.profileImage.frame.size.height / 2
self.profileImage.clipsToBounds = true
self.profileImage.layer.borderWidth = 1
self.profileImage.layer.borderColor = UIColor.white.cgColor
//self.profileImage.setRandomDownloadImage(80, height: 80)
//self.backgroundImage.setRandomDownloadImage(Int(self.frame.size.width), height: 100)
}
#IBAction func followme(_ sender: AnyObject) {
UserDefaults.standard.set(followme.isOn, forKey: "followme")
}
#IBAction func logout(_ sender: AnyObject) {
delegate?.goToNextScene()
print("GOTOSCENE")
}
}
There is are no errors thrown but when I click/tap the button, nothing happens. It just prints "GOTOSCENE".
What I feel is
Your problem is right here
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
I think you have added imageHeaderview from storyboard and in viewdidload you are creating new object of ImageHeaderView and assigning delegate to newly created object.
Try to outlet your ImageHeaderView and assign delegate to outleted object.
Hope this will fix your issue.

Memory usage increases with every segue to modal vc

I have two view controllers, one is the timeline the second one is for the creation. In that second view controller I have a sub view. This sub view is an SKView. Now every time I segue to it, it increases the memory usage by 2mb (on a real device), but the memory usage stays the same when I unwind it.
So it is like this: I start with a usage of 12mb, then it gets 14-15mb. After the unwind it stays around 14-15mb. After the second segue to it, it becomes 17mb... and so on.
This is the code used in the timeline controller:
#IBAction func createButtonAct(sender: AnyObject) {
self.performSegueWithIdentifier("create", sender: self)
}
#IBAction func unwindFromCreation(segue: UIStoryboardSegue) {
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "create"{
let vc = segue.destinationViewController as! CreateViewController
if vc.currCountry == nil || vc.currCountry != self.currCountry{
vc.currCountry = self.currCountry
}
}
}
And this is the code in the create View Controller:
class CreateViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var bubbleView: SKView!
#IBOutlet var arrow: UIButton!
var ref: Firebase!
let categories = CatsAndColors.categories
#IBOutlet var doneButton: UIButton!
#IBOutlet var titleField: UITextField!
#IBOutlet var descriptionView: KMPlaceholderTextView!
var choosedCat: String!
var selectedCats: NSMutableArray!
var currCountry:String!
var tap: UITapGestureRecognizer!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
UIApplication.sharedApplication().statusBarStyle = .Default
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.arrow.transform = CGAffineTransformMakeRotation(3.14159)
})
titleField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
titleField.addTarget(self, action: "textFieldDidBegin:", forControlEvents: UIControlEvents.EditingDidBegin)
titleField.addTarget(self, action: "textFieldDidEnd:", forControlEvents: UIControlEvents.EditingDidEnd)
// the targets get removed in viewWillDisappear
selectedCats = NSMutableArray()
}
override func viewDidLoad() {
super.viewDidLoad()
ref = Firebase(url: "https://blabber2.firebaseio.com")
self.doneButton.enabled = false
doneButton.setBackgroundImage(UIImage(named: "Done button inactive"), forState: .Disabled)
doneButton.setTitleColor(UIColor(netHex: 0xF6F6F6), forState: .Disabled)
doneButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
self.setupBubbles()
self.descriptionView.delegate = self
}
func setupBubbles(){
let floatingCollectionScene = ChooseBubblesScene(size: bubbleView.bounds.size)
// floatingCollectionScene.scaleMode = .AspectFit
/*let statusBarHeight = CGRectGetHeight(UIApplication.sharedApplication().statusBarFrame)
let titleLabelHeight = CGRectGetHeight(self.tibleLabel.frame)*/
bubbleView.presentScene(floatingCollectionScene)
for (category, color) in categories {
let node = ChooseBubbleNode.instantiate()
node!.vc = self
node!.fillColor = SKColor(netHex: color)
node!.strokeColor = SKColor(netHex: color)
node!.labelNode.text = category
floatingCollectionScene.addChild(node!)
}
}
...
And the catsAndColors struct looks like this:
struct CatsAndColors{
static var categories = ["Crime":0x5F5068, "Travel":0xFBCB43, "Religion":0xE55555, "Tech":0xAF3151, "Economy":0x955BA5, "Games":0xE76851, "Climate":0x6ED79A, "Books":0xE54242, "History":0x287572, "Clothes":0x515151, "Sports":0x4AB3A7, "Food":0xD87171, "Politics":0x5FA6D6, "Music":0xDD2E63, "Tv-shows":0x77A7FB]
}
Maybe you have created some sort of retain cycle between your view controllers.
If both view controllers hold a reference to each other, then try declaring one of the references as weak.
For more information on the topic read Resolving Strong Reference Cycles Between Class Instances.
I solved the problem, it was strong reference in the sknode file.
Thank you for your answers.

Swift - Tap gesture to dismiss keyboard UITableView

I have an identical question to this one , but since I'm new to programming and only really know swift I was wondering if someone could give me its equivalent in swift. Or point me to another question that I may have missed that is in swift.
Thanks!
UPDATE: here's the basic jist of my view controller after I've cut some of the fat away to deal with only the relevant topic. To restate the problem. Not until I have clicked my 'doneButton' to run the createClient() function and navigate back to the client page to edit the freshly created client will the the tap gesture work to dismiss the keyboard.
import UIKit
import CoreData
import Foundation
class NewClientTableViewController: UITableViewController, UINavigationControllerDelegate, UITextFieldDelegate {
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
#IBOutlet weak var nameTxt: UITextField!
#IBOutlet weak var ageTxt: UITextField!
#IBOutlet weak var phoneTxt: UITextField!
#IBOutlet weak var emailTxt: UITextField!
#IBOutlet weak var heightTxt: UITextField!
#IBOutlet weak var notesTxt: UITextView!
var client: Client? = nil
override func viewDidLoad() {
super.viewDidLoad()
if client != nil {
nameTxt.text = client?.name
ageTxt.text = client?.age
heightTxt.text = client?.height
phoneTxt.text = client?.phone
emailTxt.text = client?.email
notesTxt.text = client?.notes
self.title = client?.name
phoneTxt.delegate = self
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
}
func hideKeyboard() {
tableView.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func doneButton(sender: AnyObject) {
if client != nil {
editClient()
} else {
createClient()
}
dismissViewController()
}
func editClient() {
client?.name = nameTxt.text
client?.age = ageTxt.text
client?.height = heightTxt.text
client?.phone = phoneTxt.text
client?.email = emailTxt.text
client?.notes = notesTxt.text
client?.clientImage = UIImageJPEGRepresentation(contactImage.image, 1)
managedObjectContext?.save(nil)
}
func createClient() {
let entityDescription = NSEntityDescription.entityForName("Client", inManagedObjectContext: managedObjectContext!)
let client = Client(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
if nameTxt.text == "" {
client.name = "Untitled Client"
} else {
client.name = nameTxt.text
}
client.age = ageTxt.text
client.height = heightTxt.text
client.phone = phoneTxt.text
client.email = emailTxt.text
client.notes = notesTxt.text
client.clientImage = UIImageJPEGRepresentation(contactImage.image, 1)
managedObjectContext?.save(nil)
}
func dismissViewController() {
navigationController?.popToRootViewControllerAnimated(true)
}
}
You can also do it from Storyboard:
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
func hideKeyboard() {
tableView.endEditing(true)
}
Translating Objective-C code to Swift is not really hard. You just require a basic knowledge in both languages. If you're new to programming I guess you should familiarise with yourself with the basics first.
As #Ben already recommended, you can also do this programmatically.
Just put this in your viewDidLoad()
tableView.keyboardDismissMode = .onDrag
Swift 4:
On ViewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.yourTableView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))))
}
On corresponding function:
#objc func handleTap(_ sender: UITapGestureRecognizer) {
if sender.state == .ended {
// Do your thang here!
self.view.endEditing(true)
for textField in self.view.subviews where textField is UITextField {
textField.resignFirstResponder()
}
}
sender.cancelsTouchesInView = false
}
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
tapGesture.cancelsTouchesInView = true
self.view.addGestureRecognizer(tapGesture)
}
func hideKeyboard()
{
self.view.endEditing(true)
}
this will work fine compared to the above which didnt work for me
For some reason, #Isuru's answer crashes in my app (XCode 7.2.1), and thankfully it's just a minor change - just remove the Selector() call:
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: "hideKeyboard")
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
func hideKeyboard() {
tableView.endEditing(true)
}
Accepted answer doesn't work for me on XCode 13.1, so I changed the code a little bit:
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.hideKeyboard))
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
#objc func hideKeyboard() {
tableView.endEditing(true)
}

Resources