How to detect gesture on a UIView after it becomes transparent? - ios

I have a UIView on top of some other UI components to detect a long-pressed gesture. When a long press begins, I want to tip the user by changing the background color to gray & alpha = 0.1.
After the long press ends, the UIView has to be changed back as completely transparent. I set its alpha to 0, but the problem is ...
no further guesture can be detected.
mainView = UIView()
mainView.frame = ...
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action:Selector("longPressed:"))
mainView.addGestureRecognizer(longPressRecognizer)
func longPressed(sender: UILongPressGestureRecognizer) {
let view = sender.view!
if sender.state == .Began {
view.backgroundColor = UIColor.grayColor()
view.alpha = 0.1
} else if (sender.state == .Ended || sender.state == .Cancelled || sender.state == .Failed) {
view.backgroundColor = UIColor.whiteColor()
view.alpha = 0
}
}
What's the correct way to make this UIView changed back to its original state so that further gestures can be detected as it's initially created?

Setting a UIView's alpha property to 0 will make it stop receiving touches. Instead, set its background to UIColor.clearColor() when you want it invisible.

Related

change the color of uiview when pressed/released

Is there a way to change color of the uiview when pressed and released.
I tried it using touchesBegan and touchesEnded by overriding it but it effected the existing functionality. so without effecting the existing functionality can we achieve it ?
You can use UIGestureRecognizer to detect touch events on your view. Please refer below links for more info.
https://www.raywenderlich.com/433-uigesturerecognizer-tutorial-getting-started
https://developer.apple.com/documentation/uikit/uipangesturerecognizer
Example:
let myView = UIView()
let gesture = UITapGestureRecognizer(target: self, action: #selector(tapEventDetected(gesture:)))
myView.addGestureRecognizer(gesture)
#objc func tapEventDetected(gesture:UITapGestureRecognizer){
if gesture.state == .began{
//Set touch began color
}
if gesture.state = .ended{
//Set touch ended color
}
}
Thanks!

iOS: dragging the copy of a button

I'm not sure whether this has been asked or not, but I failed to find a solution. I'm implementing panning gesture on a button, but the idea is: the button is fixed to a position, and when the user drags it, a copy of the button is created and moving with the gesture; the original one stays at its initial place (so there'll be 2 buttons in the view). When the panning ends, the new button is used for some processing, and after that it should disappear (the original one stays as it is; so this whole process can repeat). Currently what I have is as below:
private func addPanGesture() {
for btn in self.selectors { //selectors is a list of buttons which needs this gesture
let pan = UIPanGestureRecognizer(target: self, action:#selector(self.panDetected(_:)))
pan.minimumNumberOfTouches = 1
pan.maximumNumberOfTouches = 1
btn.addGesturerecognizer(pan)
}
}
#objc private func panDetected(_ panGesture: UIPanGestureRecognizer) {
var translation = panGesture.translation(in: view)
panGesture.setTranslation(CGPoint(x: 0, y: 0), in: view)
var newButton = UIButton()
if let initButton = panGesture.view as? UIButton {
print ("Button recognized!") // this msg is printed out
newButton.center = CGPoint(x: initButton.center.x + translation.x, y: initButton.center.y + translation.y)
newButton.setImage(UIImage(named: "somename"), for: .normal)
}
if panGesture.state == UIGestureRecognizerState.began {
self.view.addSubview(newButton)
}
if panGesture.state == UIGestureRecognizerState.ended {
//some other processing
}
if panGesture.state == UIGestureRecognizerState.changed {
self.view.addSubview(newButton)
}
// printed-out msgs show began, ended, changed states have all been reached
}
But the new button doesn't show up in my view. May I know how to solve this?
You need to create and add the new button as a subview only on .began and remove it on .ended.
Therefore you need to keep a reference to the new button.
You are setting the new button's center but not it's size. You might set its .frame.
You do not need to set a translation to the pan gesture. When you get var translation = panGesture.translation(in: view) you get everything you need.
I have wrote the below code for only one button, but if you are going to allow simultaneous dragging of buttons, you would need to keep a list of moving buttons instead of var movingButton: UIButton?
private func addPanGesture() {
let pan = UIPanGestureRecognizer(target: self, action:#selector(self.panDetected(_:)))
pan.minimumNumberOfTouches = 1
pan.maximumNumberOfTouches = 1
btn.addGestureRecognizer(pan)
}
#objc private func panDetected(_ panGesture: UIPanGestureRecognizer) {
let translation = panGesture.translation(in: view)
let initButton = panGesture.view as! UIButton
if panGesture.state == UIGestureRecognizerState.began {
// this is just copying initial button
// this might be overkill
// just make sure you set the frame, title and image of the new button correctly
let initButtonData = NSKeyedArchiver.archivedData(withRootObject: initButton)
let newButton = NSKeyedUnarchiver.unarchiveObject(with: initButtonData) as! UIButton
// we store new button's reference since we will just move it while it is added to view
movingButton = newButton
self.view.addSubview(movingButton!)
}
if panGesture.state == UIGestureRecognizerState.ended {
//some other processing
// when we are done just we just remove it from superview
movingButton!.removeFromSuperview()
}
if panGesture.state == UIGestureRecognizerState.changed {
// at any change, all we need to do is update movingButton's frame
var buttonFrame = initButton.frame;
buttonFrame.origin = CGPoint(x: buttonFrame.origin.x + translation.x, y: buttonFrame.origin.y + translation.y)
movingButton!.frame = buttonFrame
}
}
Hard to say without debugging it, but a few things I see:
You create a new button every time through panDetected, and add it to the view each time. You should only create an add the button in the .began state.
You should use init(frame:) to create your button, and initialize it to the size of the image.
It looks like you're attaching the pan gestures to the buttons. Then you get the pan coordinates in the button's coordinate system, which doesn't make sense. You should be converting the pan gesture to the button's superview's coordinate system, and should not be calling setTranslation except when the pan gesture's state is .began.
You should be setting the button's coordinates to the new location of the pan gesture each time you get a 1st.changed` message.

Change UISlider value with swipe gesture swift

I have a UIView with a swipe gesture .
let swipeUpGesture = UISwipeGestureRecognizer(target: self, action: #selector(NextStepCaptureVC.handleSwipeUp(gesture:)))
swipeUpGesture.direction = .up
view.addGestureRecognizer(swipeUpGesture)
func handleSwipeUp(gesture: UISwipeGestureRecognizer) {
print("Swipe Up")
heightSlider.setValue(20, animated: true)
}
When I try to change the value it works but the value jump from 0 to 20. I want the value to change continuously while swiping. How can I do it?
Judging from your code, it looks like you are trying to make 'panning up and down' on the screen translate to the UISlider value changing.
As already mentioned by others, first thing is to change your UISwipeGestureRecognizer to a UIPanGestureRecognizer
let pan = UIPanGestureRecognizer(target: self, action: #selector(pan(gesture:)))
view.addGestureRecognizer(pan)
Then in your pan function, you need to update the slider value based on how much the user has panned.
func pan(gesture: UIPanGestureRecognizer) {
// The amount of movement up/down since last change in slider
let yTranslation = gesture.translation(in: gesture.view).y
// The slide distance needed to equal one value in the slider
let tolerance: CGFloat = 5
if abs(yTranslation) >= tolerance {
let newValue = heightSlider.value + Float(yTranslation / tolerance)
heightSlider.setValue(newValue, animated: true)
// Reset the overall translation within the view
gesture.setTranslation(.zero, in: gesture.view)
}
}
Simply adjust the tolerance variable to make the user swipe more/less in order to adjust the slider value.
You don't want a UISwipeGestureRecognizer, you want a UIPanGestureRecognizer. A swipe is a one-time gesture.
Apple's documentation says "A swipe is a discrete gesture, and thus the associated action message is sent only once per gesture."
You set up a main action from your gesture recognizer to your code (You can use interface builder for that)
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.Began {
} else if recognizer.state == UIGestureRecognizerState.Ended {
} else if recognizer.state == UIGestureRecognizerState.Changed {
}
}
Good luck! =]

Draggable UIButton/Elements in Swift?

this is my first question! I was just wondering, in Swift (specifically Swift 2, although that may go without saying!), how you create a button that the user can drag around. So for example, if it is a UIButton, the user can tap and hold it, and when they move their finger, the UIButton moves with it, and when they release it, it remains in the position that the user left it. Potentially there could be a snapping system but this is unimportant for now.
I've searched StackOverflow and found some quite interesting things, however it's all for Objective-C, and although Swift is pretty similar in some respects, I can't figure out in the slightest as to how to implement this in Swift.
It would be massively appreciated for a project that I am working on!
Thank you very much!
You can implement UIPanGestureRecognizer on your UIButton.
Wherever you create your button (viewDidLoad if using outlets):
let pan = UIPanGestureRecognizer(target: self, action: "panButton:")
button.addGestureRecognizer(pan)
This creates a new pan gesture recognizer and adds it to the button. Now, you'll want to implement the pan's action. First, you need to store the center of the button to be able to reset it when you finish panning. Add this as a view controller property:
var buttonCenter = CGPointZero
Then you implement the pan action. Note that you can use gesture recognizer states to determine when the pan starts and ends:
func panButton(pan: UIPanGestureRecognizer) {
if pan.state == .Began {
buttonCenter = button.center // store old button center
} else if pan.state == .Ended || pan.state == .Failed || pan.state == .Cancelled {
button.center = buttonCenter // restore button center
} else {
let location = pan.locationInView(view) // get pan location
button.center = location // set button to where finger is
}
}
Swift 4 & 5 Version of accepted answer:
var buttonCenter: CGPoint = .zero
viewDidLoad() {
super.viewDidLoad()
let pan = UIPanGestureRecognizer(target: self, action: #selector(YourViewController.panButton(pan:)))
button.addGestureRecognizer(pan)
}
#objc func panButton(pan: UIPanGestureRecognizer) {
if pan.state == .began {
buttonCenter = button.center // store old button center
} else if pan.state == .ended || pan.state == .failed || pan.state == .cancelled {
button.center = buttonCenter // restore button center
} else {
let location = pan.location(in: view) // get pan location
button.center = location // set button to where finger is
}
}
Basically, you want to implement a touch gesture recognizer and set the button's center to the center of your press when you tap/move said button.
Here's how you'll want to do that: https://stackoverflow.com/a/31487087/5700898
Also, really cool idea!

Toggle image when the user touch the image

I'm trying to do two things in Swift:
· When the user touches the image, toggle between the filtered, and original images temporarily.
· When the user lifts their finger, toggle back.
But I don't know what functions or modules to use, any suggestions?
I have got a image View and four buttons 「New Photo」,「Filter」,「Compare」,「Share」.
Try this
override func viewDidLoad(){
super.viewDidLoad()
// add Tap gesture recognizer to ImageView
let imageView = self.your_imageView
let tapGestureRecognizer = UILongPressGestureRecognizer(target:self, action:Selector("toggleImage:"))
imageView.userInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
func toggleImage(sender: UILongPressGestureRecognizer){
if sender.state == .Began{
originalImage()
}else if sender.state == .Ended{
filteredImage()
}
}
It uses UILongPressGestureRecognizer()
I would recommend looking into UILongPressGestureRecognizer.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UILongPressGestureRecognizer_Class/index.html#//apple_ref/occ/instp/UILongPressGestureRecognizer/minimumPressDuration
When the gesture begins, change the UIImage being displayed. You can detect when the gesture ends with UIGestureRecognizerStateEnded. I'm sure you'll figure it out!
I set the minimum press duration to 0.1 from default 0.5 to make it look like a tap button. Code changes to check if the filtered image is not nil and it is not equal to the original image. I set back and forth the image view according the state began and end
override func viewDidLoad() {
super.viewDidLoad()
// adding Tap gesture recognizer to image view
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(imageTapped(_:)))
// Enabling the user interaction for the image view
imageView.userInteractionEnabled = true
imageView.addGestureRecognizer(longPressGestureRecognizer)
// Changing the default minimum press duration from 0.5 to 0.1
longPressGestureRecognizer.minimumPressDuration = 0.1
}
// image tapped function that changes the image view when the user presses on the image view
func imageTapped(longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == .Began {
imageView.image = originalImage
} else if longPressGestureRecognizer.state == .Ended {
if filteredImage != nil && filteredImage != originalImage {
imageView.image = filteredImage
}
}
}

Resources