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.
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 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()
}
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'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
})
}
}
I have a viewController with a UISegmentedControl and a UIButton.
Within this viewController, I have two containers, each containing one viewController with a UITextField inside.
I want to save the values in the textField on the click of the button.
Here's the code I have written so far:
View Controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//
//
containerA.showView()
containerB.hideView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(sender: UIButton) {
print(ContainerAViewController.sharedInstance.textFieldA)
}
#IBAction func segmentedControlValueChanged(sender: AnyObject) {
switch(sender.selectedSegmentIndex) {
case 0 : containerA.showView()
containerB.hideView()
case 1 : containerB.showView()
containerA.hideView()
default : containerA.showView()
containerB.hideView()
}
}
#IBOutlet weak var containerA: UIView!
#IBOutlet weak var containerB: UIView!
func hideView(view: UIView) {
view.userInteractionEnabled = false
view.hidden = true
}
func showView(view: UIView) {
view.userInteractionEnabled = true
view.hidden = false
}
}
extension UIView {
func hideView() -> UIView {
self.userInteractionEnabled = false
self.hidden = true
return self
}
func showView() -> UIView {
self.userInteractionEnabled = true
self.hidden = false
return self
}
}
ContainerAViewController:
class ContainerAViewController: UIViewController {
#IBOutlet weak var textFieldA: UITextField!
static let sharedInstance = ContainerAViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
ContainerBViewController:
class ContainerBViewController: UIViewController {
#IBOutlet weak var textFieldB: UITextField!
static let sharedInstance = ContainerBViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
When I tap the button, it gives me the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Can somebody please help?
You should not try to manipulate another view controller's views. That violates the principle of encapsulation, an important principle in object-oriented development.
You should give your child view controllers (ContainerAViewController and ContainerBViewController) string properties, and have the code for those view controllers set that string property when the user enters text into the view controllers' text fields.
Ignoring that design flaw, your code doesn't make sense. You show your CLASS as ContainerAViewController, and yet your buttonTapped method is trying to ask a ContainerAViewController singleton for the value of a text field. That makes no sense.
You want to have properties in your parent view controller that point to your child view controllers.
You should implement a prepareForSegue method in your parent view controller, and in that prepareForSegue method, look for the embed segues that fire when the child view controllers are loaded. When that happens you should set your properties that point to the child view controllers.