I am trying to use UITapGestureRecognizer to change a UIView color when the view is tapped, then back to the original white color when space outside the UIView is tapped. I can get the UIView to change colors but I cannot get it to change back to white color.
// viewDidLoad
let tapGestureRegonizer = UITapGestureRecognizer(target: self, action:
#selector(mortgagePenaltyVC.viewCellTapped(recognizer:)))
tapGestureRegonizer.numberOfTapsRequired = 1
mortgageLenerViewCell.addGestureRecognizer(tapGestureRegonizer)
#objc func viewCellTapped (recognizer: UITapGestureRecognizer){
print("label Tapped")
mortgageLenerViewCell.backgroundColor = UIColor.lightGray
}
I think you can use touchesBegan method to get the touch outside of view like below
This code works for me...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let location = touch?.location(in: mortgageLenerViewCell) else { return }
if !labelToClick.frame.contains(location) {
print("Tapped outside the view")
mortgageLenerViewCell.backgroundColor = UIColor.white
}else {
print("Tapped inside the view")
mortgageLenerViewCell.backgroundColor = UIColor.lightGray
}
}
here labelToClick is the label on which you want to click & mortgageLenerViewCell is the superview of labelToClick
Feel free to ask for any query...
Thank you.
Related
I`m here because after weeks of trying different solutions and don't come with the right answer and functional in-app I am exhausted.
I need to track the time of finger on-screen and if a finger is on screen longer than 1 sec I need to call function. But also if now user is performing gestures like a pan or pinch function must be not called.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
guard let touchLocation = touch?.location(in: self) else {return }
let tile: Tile?
switch atPoint(touchLocation){
case let targetNode as Tile:
tile = targetNode
case let targetNode as SKLabelNode:
tile = targetNode.parent as? Tile
case let targetNode as SKSpriteNode:
tile = targetNode.parent as? Tile
default:
return
}
guard let tile = tile else {return }
paint(tile: tile)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
}
private func paint(tile: Tile){
let col = tile.column
let row = tile.row
let colorPixel = gridAT(column: col, row: row)
if !tile.isTouchable {
}else {
if !selectedColor.isEmpty {
if tile.mainColor == selectedColor {
tile.background.alpha = 1
tile.background.color = UIColor(hexString:selectedColor)
tile.text.text = ""
tile.backgroundStroke.color = UIColor(hexString:selectedColor)
uniqueColorsCount[selectedColor]?.currentNumber += 1
didPaintCorrect(uniqueColorsCount[selectedColor]?.progress ?? 0)
colorPixel?.currentState = .filledCorrectly
tile.isTouchable = false
}
else if tile.mainColor != selectedColor {
tile.background.color = UIColor(hexString:selectedColor)
tile.background.alpha = 0.5
colorPixel?.currentState = .filledIncorrectly
}
}
}
}
Here is a small example I created for you with my suggestion of using UILongPressGestureRecognizer as it seems easier to manage for your situation than processing touchesBegin and touchesEnded
You can give it the minimum time the user needs to tap so it seems perfect for your requirement.
You can read more about it here
First I just set up a basic UIView inside my UIViewController with this code and add a long tap gesture recognizer to it:
override func viewDidLoad() {
super.viewDidLoad()
// Create a basic UIView
longTapView = UIView(frame: CGRect(x: 15, y: 30, width: 300, height: 300))
longTapView.backgroundColor = .blue
view.addSubview(longTapView)
// Initialize UILongPressGestureRecognizer
let longTapGestureRecognizer = UILongPressGestureRecognizer(target: self,
action: #selector(self.handleLongTap(_:)))
// Configure gesture recognizer to trigger action after 2 seconds
longTapGestureRecognizer.minimumPressDuration = 2
// Add gesture recognizer to the view created above
view.addGestureRecognizer(longTapGestureRecognizer)
}
This gives me something like this:
Next, to get the location of the tap, the main question to ask yourself is - Where did the user tap in relation to what view ?
For example, let's say the user taps here:
Now we can ask what is location of the tap in relation to
The blue UIView - It is approx x = 0, y = 0
The ViewController - It is approx x = 15, y = 30
The UIView - It is approx x = 15, y = 120
So based on your application, you need to decide, in relation to which view do you want the touch.
So here is how you can get the touch based on the view:
#objc
private func handleLongTap(_ sender: UITapGestureRecognizer)
{
let tapLocationInLongTapView = sender.location(in: longTapView)
let tapLocationInViewController = sender.location(in: view)
let tapLocationInWindow = sender.location(in: view.window)
print("Tap point in blue view: \(tapLocationInLongTapView)")
print("Tap point in view controller: \(tapLocationInViewController)")
print("Tap point in window: \(tapLocationInWindow)")
// do your work and function here
}
For same touch as above image, I get the following output printed out:
Tap point in blue view: (6.5, 4.5)
Tap point in view controller: (21.5, 34.5)
Tap point in window: (21.5, 98.5)
I have a view controller where I can add several subviews with a LongPressGesture. For each subview I provide a TapGesture that should open a popover for this view (see picture below).
My problem is, that I can only open the popover for the last subview I added. So why can't I interact with the other subviews anymore?
This is my first App in Swift, so it would be nice if someone could help me.
Some code for you:
This is the LongPressGesture on the root view controller that creates a new subview.
#IBAction func onLongPress(_ gesture : UILongPressGestureRecognizer) {
let position: CGPoint = gesture.location(in: view)
if(gesture.state == .began) {
let subview = MySubview(position: position)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
subview.addGestureRecognizer(tapGesture)
view.addSubview(subview)
}
else if (gesture.state == .ended) {
let subview = self.view.subviews.last
self.openContextMenu(for: subview)
}
}
ViewController with subviews:-
When reacting to an event (long press in your case) only the first responder will handle the event. When adding subviews to a view, all the subviews are added one in front of another. So the last added subview will handle the event - if it is listening for the event. If you need a specific subview to handle the event, you can bring it to front as
parentView.bringSubviewToFront(view: subView)
Alternatively, you can also disable other subviews as
subView.isUserInteractionEnabled = false
Ok, I found a solution for my problem. The good thing is, that it only detects touches in the frames of the subviews.
In my root view controller I added:
var isPanGesture = false
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
isPanGesture = true
if let subview = touches.first!.view as? MySubview {
let position = touches.first!.location(in: self.view)
link.center = position
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let subview = touches.first!.view as? MySubview {
if(!isPanGesture) {
self.openContextMenu(for: subview)
}
isPanGesture = false
}
}
i have three UIImageView on a single Cell when i click on any of the UIImageView on the cell i want to detect which one was clicked on onCellSelection, without placing a UITapGestureRecognizer on each UIImageview
func SocialViewRow(address: SocialMedia)-> ViewRow<SocialMediaViewFile> {
let viewRow = ViewRow<SocialMediaViewFile>() { (row) in
row.tag = UUID.init().uuidString
}
.cellSetup { (cell, row) in
// Construct the view
let bundle = Bundle.main
let nib = UINib(nibName: "SocialMediaView", bundle: bundle)
cell.view = nib.instantiate(withOwner: self, options: nil)[0] as? SocialMediaViewFile
cell.view?.backgroundColor = cell.backgroundColor
cell.height = { 50 }
print("LINK \(address.facebook?[0] ?? "")")
cell.view?.iconOne.tag = 90090
//self.itemDetails.activeURL = address
let openFace = UITapGestureRecognizer(target: self, action: #selector(QuickItemDetailVC.openFace))
let openT = UITapGestureRecognizer(target: self, action: #selector(QuickItemDetailVC.openTwit))
let you = UITapGestureRecognizer(target: self, action: #selector(QuickItemDetailVC.openYouYub))
cell.view?.iconOne.addGestureRecognizer(openFace)
cell.view?.iconTwo.addGestureRecognizer(openT)
cell.view?.iconThree.addGestureRecognizer(you)
cell.frame.insetBy(dx: 5.0, dy: 5.0)
cell.selectionStyle = .none
}.onCellSelection() {cell,row in
//example
//print(iconTwo was clicked)
}
return viewRow
}
Using UITapGestureRecogniser (or UIButton) would be a better approach. These classes intended for tasks like this.
If you still want to use different approach, add method to your cell subclass (replace imageView1, imageView2, imageView3 with your own properties)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let point = touch.location(in: view)
if imageView1.frame.containsPoint(point) {
// code for 1st image view
} else if imageView2.frame.containsPoint(point) {
// code for 2nd image view
} else if imageView3.frame.containsPoint(point) {
// code for 3rd image view
}
}
Docs:
location(ofTouch:in:)
contains(_ point: CGPoint)
Override the touchesbegan function. This method is called every time the user touches the screen. Every time it is called, check to see if the touches began in the same location an image is.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
//check here, include code that compares the locations of the image
}
Location will be a CGPoint. You should be able to get the CGPoints for the bounds of your images and then determine if the touchBegan in those bounds. If you want to include the entire path the user touched, there are ways to do that too but the beginning touch should be sufficient for what you want.
I want to change color of UIView when it's tap and change it back to it's original color after tap event
I already implemented these 2 methods but their behavior is not giving me required results
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
backgroundColor = UIColor.white
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
backgroundColor = UIColor.gray
}
These 2 methods work but after press tap on UIView for 2 seconds then it works. Moreover it doesn't change the color of UIView back to white after pressing it (In short it stays gray until I restart the app)
I am using tap gestures on UIView
Instead of overriding touchesBegan and touchesEnded methods, you could add your own gesture recognizer. Inspired from this answer, you could do something like:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
setupTap()
}
func setupTap() {
let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
touchDown.minimumPressDuration = 0
view.addGestureRecognizer(touchDown)
}
#objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
view.backgroundColor = .white
} else if gesture.state == .ended || gesture.state == .cancelled {
view.backgroundColor = .gray
}
}
}
say, I have a button lying under UITableView, how can I click the button through the UITableViewCell but do not trigger the cell click event:
The reason I put the button behind the tableview is that I want to see and click the button under the cell whose color set to be clear, and when I scroll the table, the button can be covered by cell which is not with clear color
I created a sample project and got it working:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let tap = UITapGestureRecognizer(target: self, action: #selector(TableViewVC.handleTap))
tap.numberOfTapsRequired = 1
self.view.addGestureRecognizer(tap)
}
func handleTap(touch: UITapGestureRecognizer) {
let touchPoint = touch.locationInView(self.view)
let isPointInFrame = CGRectContainsPoint(button.frame, touchPoint)
print(isPointInFrame)
if isPointInFrame == true {
print("button pressed")
}
}
To check of button is really being pressed we need to use long tap gesture:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let tap = UILongPressGestureRecognizer(target: self, action: #selector(TableViewVC.handleTap))
tap.minimumPressDuration = 0.01
self.view.addGestureRecognizer(tap)
}
func handleTap(touch: UILongPressGestureRecognizer) {
let touchPoint = touch.locationInView(self.view)
print(" pressed")
if touch.state == .Began {
let isPointInFrame = CGRectContainsPoint(button.frame, touchPoint)
print(isPointInFrame)
if isPointInFrame == true {
print("button pressed")
button.backgroundColor = UIColor.lightGrayColor()
}
}else if touch.state == .Ended {
button.backgroundColor = UIColor.whiteColor()
}
}
Get the touch point on the main view. Then use following method to check the touch point lies inside the button frame or not.
bool CGRectContainsPoint(CGRect rect, CGPoint point)
You can write your custom view to touch button or special view behind the topview
class MyView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
for subview in self.subviews {
if subview is UIButton {
let subviewPoint = self.convertPoint(point, toView: subview)
if subview.hitTest(subviewPoint, withEvent: event) != nil { // if touch inside button view, return button to handle event
return subview
}
}
}
// if not inside button return nomal action
return super.hitTest(point, withEvent: event)
}
}
Then set your controller view to custom MyView class