Tap on UIWebView to transition to different viewController - ios

I have a webview inside view controller "A", is it possible to tap on web view and seaque to view controller "B"?
I have tried do following
#IBOutlet weak var mainWebView: UIWebView!
let tapUIWebView = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapUIWebView.addTarget(self, action: "webViewTapped")
mainWebView.userInteractionEnabled = true
mainWebView.addGestureRecognizer(tapUIWebView)
func webViewTapped(){
println("Tapped")
}
}
Thank you

1) Use a UITapGestureRecognizer, create the instance.
2) Attach it to the webview.
3) Implement the action method that handles the gesture.
4) SinceUIWebView already recognises a touch, you need to also take care that your recognizer does the job first. And you decide what to do with the touch.
5) If it suits you, you can disable handling the tap in the built-in recogniser completely.

Related

Gesture recognizer not working if created at class init time

In a collection view, I create a gesture recognizer at class init time. In the viewDidLoad method, I then add the gesture recognizer to the collection view.
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
// some code
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.addGestureRecognizer(longPressGesture)
}
}
With this, the gesture recognizer does not work.
The fix is easy: it suffices to move the line with let longPressGesture to the viewDidLoad method and everything works as expected. However, I find it a bit surprising that the first version would not work.
Can anyone explain why the first version is not working? Is it because, when the gesture recognizer is created, the collection view is not yet ready to have gestures? So, what must a gesture recognizer know about its target in order to be created?
Good question. That's because you are trying to use self when not fully initialized.
Now, how to make that work with the way you wanted? Perhaps declare it lazily, like so:
private lazy var longPressGesture: UILongPressGestureRecognizer! = {
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))
return gesture
}()
Edit: Quoting giorashc's answer from this question:
Due to swift's 2-phase initialization you need to initialize the
parent class before you can use self in the inheriting class.
In your implementation self is yet to be initialized by the parent
class so as you said you should move it to the init method of your
view controller and create the button after calling the parent's
initialization method
2 Phase Initialization SO Question & Answer.

UITapGestureRecognizer not working - Swift 3

I see that there are a ton of these questions, and I think I'm following the accepted Swift 3 methodology, but I'm still getting nothing. I can see that the UITapGestureRecognizer has been attached. Here's my code:
let tileClick = UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:)))
newView.addGestureRecognizer(tileClick)
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
A few things to note:
1) The view that I'm attaching the gesture recognizer to has a two views and a label within it that each cover the entire frame of the view, however, I tried attaching the recognizer to the label, which is the topmost child item and it still doesn't work.
2) Both the function that adds the recognizer and the function that is called on the tap are contained in an NSObject file. I have a variety of interconnected functions that I want to be able to call from multiple view controllers and would prefer to keep this in the separate NSObject file. The process worked when I had everything in a UIViewController file and stopped working when I moved the functions to the NSObject file.
3) I've tried changing GameManagement.initiateTileClick to self.initiateTileClick or just initiateTileClick and none of those worked.
If you are putting your views inside NSObject subclass then these views will lose their behaviors for UIResponder which manages the UI interactions as I am not able to see how you are adding these views to interface.
As you said, it was working inside ViewController because it manages view hierarchy and responder chain.
The solution would be to write extensions to separate code or better abstractions.
extension YourViewController {
newView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:))))
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
}
Giving you an idea how the tap recogniser works.
Firstly add Tap gesture recogniser to your view controller. You have to put the object here as shown in the image.
Then control+drag the tap gesture object to your view and select delegate.
Then control+drag the recogniser to your swift file and action will be like this.
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
}
Now you must have seen when you give some input to a text field, the keyboard appears. But if you press outside the text field, that is anywhere in the view, the keyboard hides. This is because of the tap gesture recogniser.
Consider you have a text field such that if you click in that text field, keyboard is appeared. But when you tap outside the textfield, the keyboard must hide.
Add this delegate
UITextFieldDelegate
Implement this:
#IBOutlet var phoneText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exampleText.delegate = self
}
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
exampleText.endEditing(true)
}
Obviously,this function is instance method.
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
-
UITapGestureRecognizer(target: self, action:#selector(GameManagement.initiateTileClick(_:)))
but thisGameManagement.initiateTileClick(_:) looks like a class is calling a class method!The target should be the caller of method.self can't call GameManagement.initiateTileClick(_:).

UITapGestureRecognizer Only Works on Last Item

I'm creating a resume app (mostly for fun) and can't seem to get the tap gesture recognizer to work for me. What I'd like is to have a single label display the information and have it change depending on which title label they tap below. Here is the code I've written:
import UIKit
class WorkHistoryViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var jobOne: UILabel!
#IBOutlet weak var jobTwo: UILabel!
#IBOutlet weak var jobThree: UILabel!
#IBOutlet weak var jobFour: UILabel!
#IBOutlet weak var jobFive: UILabel!
#IBOutlet weak var workHistoryDescriptionLabel: UILabel!
let tapRec = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapRec.addTarget(self, action: "tappedLabel")
jobOne.addGestureRecognizer(tapRec)
jobTwo.addGestureRecognizer(tapRec)
jobThree.addGestureRecognizer(tapRec)
jobFour.addGestureRecognizer(tapRec)
jobFive.addGestureRecognizer(tapRec)
}
// MARK: Methods
func tappedLabel() {
workHistoryDescriptionLabel.text = "It worked!"
}
}
What's happening is that the last label to have addGestureRecognizer() called in viewDidLoad() is the only one that works. If I comment out the last line then only the label above it works. I also tried to enable user interaction on each of the labels programmatically and on the attributes inspector and neither changed anything.
As per Apple's Event Handling Guidelines
Gesture Recognizers Are Attached to a View
Every gesture recognizer is
associated with one view. By contrast, a view can have multiple
gesture recognizers, because a single view might respond to many
different gestures. For a gesture recognizer to recognize touches that
occur in a particular view, you must attach the gesture recognizer to
that view. When a user touches that view, the gesture recognizer
receives a message that a touch occurred before the view object does.
As a result, the gesture recognizer can respond to touches on behalf
of the view.
So you need to create multiple instances of UITapGestureRecognizer and attach them to each view, even if they perform same actions.
For instance,
let tapRecOne = UITapGestureRecognizer()
tapRecOne.addTarget(self, action: "tappedLabel")
jobOne.addGestureRecognizer(tapRecOne)
let tapRecTwo = UITapGestureRecognizer()
tapRecTwo.addTarget(self, action: "tappedLabel")
jobTwo.addGestureRecognizer(tapRecTwo)
and so on.
You need to create new object of tap gestures to get your work done.You can refer showing images in UIScrollview horizontally on ipad and getting tag of image on tap
Here is Apple's documentation as defined here.
A gesture recognizer operates on touches hit-tested to a specific view and all of that view’s subviews. It thus must be associated with that view. To make that association you must call the UIView method addGestureRecognizer:. A gesture recognizer doesn’t participate in the view’s responder chain.
That means that you need to have separate "UIGestureRecognizer" instances for each view. That should sort out the issue.
Xcode has a small icon in Xcode you see in this screen shot that you can click on when your app is running. As I put in this screenshot its invaluable when trying to understand what is happening with UI issues?

Trouble with tap gestures

I have a tap gesture on a UIImageView within a class that extends UITableViewCell. This code should work, I don't see why it doesn't. The only thing I am iffy on is what the "target" should be - should it be the profileImage, or the overall ViewController that things are in?
#IBOutlet weak var profileImage: UIImageView!
var vc: TweetsViewController? = nil
override func awakeFromNib() {
super.awakeFromNib()
let tapGester = UITapGestureRecognizer(target: vc, action: Selector("handleTapGester:"))
tapGester.delegate = self
profileImage.addGestureRecognizer(tapGester)
}
func handleTapGester(tapGesture: UITapGestureRecognizer) {
print("*******hi*******")
vc?.performSegueWithIdentifier("showProfile", sender: nil)
}
And for the record, as this may seem like a relevant error, I initialize vc when the table cell loads.
The target should be the object that will handle the tap gesture and the handleTapGester function should be inside the object class you specified as the target, not inside the UITableViewCell subclass.
You also need to enable user interaction on the UIImageView by saying:
imageView.userInteractionEnabled = true
Why not just add a tap gesture recogniser to the view, then when called query indexPathForRowAtPoint to find out which cell is being tapped?
If you know the cell you can then determine if the UIImage is being tapped and make your call to performSegueWithIdentifier from there.
If it's tapped on a cell that you're not interested in let it fall through and be handled by the table by calling cancelsTouchesInView on the recogniser.

Remove custom UIView(s) dynamically on user selection/highlighed in ios swift

I am new to swift, please kindly advise the best way to achieve this.
Let say if there are 3 to 4 (custom) UIView(s) added under a parent view
when user select/highlighted a particular one (e.g. the 2nd UIView), and this will get removed and the whole layout will re-render immediately. Any idea?
Connect all views to one IBOutletCollection, add gesture recognizer for tap and in recognizer callback just get the touch point and check if the point is contained in one of the views from the outlet collection.
#IBOutlet var views: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("viewTapped:"))
self.view.addGestureRecognizer(tapGesture)
}
func viewTapped(tapGesture: UITapGestureRecognizer) {
let locationInView = tapGesture.locationInView(view)
for v in views {
if CGRectContainsPoint(v.frame, locationInView) {
v.removeFromSuperview()
}
}
}
Make sure you have your autolayout setup for the state in which each of the view is not there.

Resources