I'm looking to hone in on the methodology used to 'select' UIViews by a single tap. In my UI, I have several UIViews that I would like to be 'selectable.' While selected, I will have controls to perform basic adjustments such as alpha (to let the user know which view is selected), and slightly more advanced functionality such as revealing a unique set of menu items. Only 1 view may be selected at a time. No multi-touch here.
The problem is, I've only seen this done with UICollectionViewCells. My interface isn't utilizing a collection view, so that's out of the question.
Thought process going into this is 1 tap gesture to 'select' the uiview, and then 'deselecting' it by tapping outside of the views bounds.
Another thought process is going with UIButtons instead of UIViews to utilize the Selected state.
Which would be the more viable solution here? Is this even possible using just UIViews? Or should I backtrack and go UIButtons instead?
Thanks.
You need to add a gesture recognizer to the desired UIView
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
}
}
Then you need to add the Code to handle whatever happens to your UIView into the gesture recognizer function:
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
}
This will change UIView's alpha property to 0.5 once the UIView gets selected.
Now you could for example put a second UIView with a gesture recognizer and play with the alphas:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var secondView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
let tap2 = UITapGestureRecognizer(target: self, action: #selector(self.tap2(_:)))
tap.delegate = self
secondView.addGestureRecognizer(tap2)
}
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
secondView.alpha = 1
}
func tap2(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 1
secondView.alpha = 0.5
}
}
That way, depending on the UIView you hit, the alpha will change...
Edit:
In this Code the UIViews will be "selected" - the alpha will change and will change back within a set timing of 0.8 seconds.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var secondView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
let tap2 = UITapGestureRecognizer(target: self, action: #selector(self.tap2(_:)))
tap.delegate = self
secondView.addGestureRecognizer(tap2)
}
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
UIView.animate(withDuration: 0.8, animations: {
self.myView.alpha = 1
})
}
func tap2(_ gestureRecognizer: UITapGestureRecognizer) {
secondView.alpha = 0.5
UIView.animate(withDuration: 0.8, animations: {
self.secondView.alpha = 1
})
}
}
Related
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
}
I am trying (and failing) to create just one tapGestureRecognizer to use on several UILabels.
Right now I am creating for every label in viewDidLoad a separate tapGestureRecognizer and adding it to the appropriate label. I ran into this problem because every touch should obviously call a different function.
This is how I create them:
#IBOutlet weak var buttonOne: UILabel!
#IBOutlet weak var buttonTwo: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//tapGestureRecognizer for buttonOne
buttonOne.isUserInteractionEnabled = true
let oneGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MainViewController.buttonOneAction))
buttonOne.addGestureRecognizer(oneGestureRecognizer)
//tapGestureRecognizer for buttonTwo
buttonTwo.isUserInteractionEnabled = true
let twoGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MainViewController.buttonTwoAction))
buttonTwo.addGestureRecognizer(twoGestureRecognizer)
...
They work fine, but how and where could I create just one tapGestureRecognizer and add it in viewDidLoad to each label with a different action?
Just create one per label.
The alternative is to create a single tap gesture recognizer that you attach to the common superview of all target views, and write a BUNCH of code that does hit-testing to figure out if the tap landed on any of your labels, and if so, which one, and dispatches the desired method for that label.
However, that's the whole point of tap gesture recognizers. You'd probably spend several days developing a bunch of code that has no benefits over using multiple tap gesture recognizers.
As already mentioned in comments by #Duncan C you can switch the gesture recognizer view as follow:
class ViewController: UIViewController {
#IBOutlet weak var buttonOne: UILabel!
#IBOutlet weak var buttonTwo: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
[buttonOne, buttonTwo].forEach {
$0?.isUserInteractionEnabled = true
$0?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGesture)))
}
}
#objc func tapGesture(_ gesture: UITapGestureRecognizer) {
guard let label = gesture.view as? UILabel else { return }
switch label {
case buttonOne: print("buttonOne")
case buttonTwo: print("buttonTwo")
default: break
}
}
}
I am using xcode 8.0 & Swift 3.
Here's the overview of my issue:
Scrollview with an imageview named "insidepic" as a subview.
a duplicate of the imageview named "outsidepic" is positioned outside the scrollview.
When "outsidepic" is tapped, touchesBegan is fired.
When "insidepic" is tapped, the tap gesture is fired...but not touchesBegan or touchesEnded are fired.
Here's what I need to solve:
I need for touchesBegan to get fired when the scrollview is tapped. I have added the ".cancelsTouchesInView = false" to the gesture.
Furthermore, the zoom/pan gesturing on the scrollview needs to stay intact. So userinteraction is enabled on both the scrollview & the imageview inside.
This see attached image shows the layout & the viewcontroller swift file.
The yellow area is the scrollview. (with "insidepic" inside)
But, for quick reference, here is my code:
import UIKit
var tap = UITapGestureRecognizer()
class ViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate {
#IBOutlet weak var scroller: UIScrollView!
#IBOutlet weak var insidepic: UIImageView!
#IBOutlet weak var outsidepic: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
scroller.minimumZoomScale = 1
scroller.maximumZoomScale = 6
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap))
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
tap.delegate = self
tap.cancelsTouchesInView = false
scroller.addGestureRecognizer(tap)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches Began")
}
func handleTap(){
print("Tap Gesture Received")
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return insidepic
}
}
MANY THANKS FOR ANY HELP OR INSIGHT YOU CAN OFFER!!
I have a UIScrollView with paging enabled that contains two view controllers: vc0, vc1. The scrollview is contained in a UIView (called "dummyView"), which is contained in its own view controller.
The issue is that vc1 does not respond to touches when the view controller that contains the scrollview is set to the initial view controller. If vc1 is set to the initial view controller however, vc1 responds to touch events just fine
Why does vc1 not respond to touches when it is viewed through the scrollView?
I have included the viewdidLoad() of the view controller that contains the scrollview, where I set up the scrollview and all of its subviews.
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var dummyView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width * 2, self.view.frame.height);
dummyView.frame.size = scrollView.contentSize
createConstraintsForDummyView()
let vc0 = self.storyboard?.instantiateViewControllerWithIdentifier("vc0") as! ViewController0
self.addChildViewController(vc0)
self.dummyView.addSubview(vc0.view)
vc0.didMoveToParentViewController(self)
createConstraintsForVC0(vc0)
let vc1 = self.storyboard?.instantiateViewControllerWithIdentifier("vc1")
var frame1 = vc1!.view.frame
frame1.origin.x = self.view.frame.size.width
vc1!.view.frame = frame1
self.addChildViewController(vc1!)
self.dummyView.addSubview(vc1!.view)
vc1!.didMoveToParentViewController(self)
createConstraintsForVC1(vc1)
}
EDIT: I've included the class for vc1
import UIKit
class ViewController1: UIViewController, UIGestureRecognizerDelegate {
func handleTap(recognizer: UITapGestureRecognizer){
print("Tapped!")
}
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
tap.delegate = self
self.view.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
As discussed here
The dummyView was causing problems.
The solution is to remove the dummyView and use the scrollView instead.
Inside of a VC, I'm trying to figure out how to have a UIButton recognize a horizontal swipe and then tap.
There is an array of text.
var plotList = ["stuff", "to do", "this", "or that"]
default display is plotList[0].
the carousel repeats in an infinite loop doesn't stop at the end of the array.
as user swipes over button only, i don't want the other components of the VC to react to this particular swipe, i want the button to display the plotList string.
when the user taps on the button, i want to shove the result into a switch that will launch the appropriate UIView.
i'd like to avoid doing this UILabel/UIButton combination. See Handling Touch Event in UILabel and hooking it up to an IBAction.
so far i'm here
import UIKit
class carouselVC: UIViewController {
#IBOutlet var carouselView: UIView!
#IBOutlet var labelOutlet: UILabel!
#IBOutlet var buttonOutlet: UIButton!
var plotList = ["stuff", "this", "that"]
let swipeRec = UISwipeGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
var swipeButtonLeft: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "buttonLeft")
swipeButtonLeft.direction = UISwipeGestureRecognizerDirection.Left
self.buttonOutlet.addGestureRecognizer(swipeButtonLeft)
var swipeButtonRight: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "buttonRight")
swipeButtonRight.direction = UISwipeGestureRecognizerDirection.Right
self.buttonOutlet.addGestureRecognizer(swipeButtonRight)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonLeft(){
println("buttonLeft")
}
func buttonRight(){
println("buttonRight")
}
#IBAction func buttonAction(sender: UIButton) {
sender.setTitle(plotList[1], forState: .Normal)
}
}
any help?
Use a UIView with a UILabel, instead of button. Add UIView over UILabel and make it clear color. You may make UIlabel look like a button. Add a swipe gesture/tap gesture on it. And do any operation in the action of the gestures.