SWIFT Removing Subviews - ios

I'm trying to have the ability to remove the subview the user chooses with three taps.
The problem I'm having is I can only get it to remove in the order the subviews were created. I have played around with 'viewWithTag' but can't figure out how to get it to do what I want.
Can I achieve what I want with the 'tapGesture' removing the subview from the location I tapped?
I'm a noob (as the kids say), so any help is much appreciated!
Thanks!
#IBAction func unwindToParent(segue:UIStoryboardSegue){
var source = segue.sourceViewController as PropViewController
var propImage = UIImage(named: name as String!)
clipView = UIImageView(image: propImage!)
clipView.frame = CGRectMake(0, 0, 200.0, 200.0)
clipView.center = CGPoint (x: view.bounds.size.width/2, y: view.bounds.size.height/2)
clipView.contentMode = UIViewContentMode.ScaleAspectFit
clipView.userInteractionEnabled = true
clipView.multipleTouchEnabled = true
clipView.layer.cornerRadius = 10.0
setTag = tagCounter
tagCounter++
clipView.tag = setTag
addPinchGestureRecognizer(clipView)
addPanGestureRecognizer(clipView)
addRotationGestureRecognizer(clipView)
addTapGestureRecognizer(clipView)
view.addSubview(clipView)
view.bringSubviewToFront(clipView)
let recognizer = UITapGestureRecognizer(target: self, action:Selector("Trash:"))
recognizer.numberOfTapsRequired = 3
recognizer.delegate = self
clipView.addGestureRecognizer(recognizer)
}
func Trash(gesture: UITapGestureRecognizer){
clipView.viewWithTag(setTag)?.removeFromSuperview()
}

The gesture recogniser has a view property - that will be the tapped view.
Never use tags for anything.

Theres probably a half dozen ways I can see this being done.
The 'simplest' is probably getting the location of the tap
tap.locationInSubview
And then checking if any of the views contain that point
uiViewObject.containtsPoint()
If it contains the point remove it
Or you could add gestures to each view and, as suggested by jrturton, uses the taps view source as the view to remove.

Related

How do I make my UIView retractable when I add it as a subview to a scrollView?

I am making a calendar style app only showing individual days, so I have a scroll view right now for each day starting at midnight and ending at midnight, and I want to be able to tap and create "events" on each day.
So far, I have been able to create UIViews every time I tap on the screen, but now I am trying to edit them when I click on them.
Here is some code in my ViewDidLoad for creating an event:
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(gestureRecognizer:)))
scrollView.addGestureRecognizer(tapRecognizer)
tapRecognizer.delegate = self
And here is the code I have in my tapped func:
#objc func tapped(gestureRecognizer: UITapGestureRecognizer) {
let tapLocation = gestureRecognizer.location(in: scrollView)
let eventLength = 100
let eventLocation = CGPoint(x: tapLocation.x, y: tapLocation.y-CGFloat(eventLength/2))
//Creates Rectangle event
let eventRect = CGRect(x: 0, y: Int(eventLocation.y), width: Int(view.frame.size.width), height: (eventLength))
let testEventView = UIView(frame: eventRect)
testEventView.backgroundColor = UIColor(hue: 0.5667, saturation: 0.58, brightness: 0.89, alpha: 1.0)
self.scrollView.addSubview(testEventView)
}
But now, how can I recognize when I tap on each event, and change them depending on which one I tapped on. Like I was thinking about adding a text box on the UIView and editing it when you tap on the text box, but I'm not sure how to make it so the app knows when I tap on each event vs. the whole scrollView.
Sorry for the long and kind of open ended question, but if anyone could point me in the right direction of what to look up, or maybe not using a UIView, or anything that could help me accomplish this event creation would be helpful! Thank you!
First of all, you can add tap gesture to every event view
testEventView.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(eventViewTapped(gestureRecognizer:))))
self.scrollView.addSubview(testEventView)
Then, inside action, you can retrive event view
#objc func eventViewTapped(gestureRecognizer: UITapGestureRecognizer) {
let eventView = gestureRecognizer.view
After you have to determine which event view it is.
There are many ways to do it, for example
Store eventViews as array, then find corresponding index of selected view from this array
Set tag to eventView inside your tapped function, retrive tag inside eventViewTapped
Use some custom ui & data model, that holds all info about event views, just like uitableview does

Make view draggable but not tappable

I have a label in my view controller that I set like this in viewDidLoad:
var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label = UILabel()
view.add(label)
label.frame = CGRect(x: 0, y, 0, width: 20, height: 20)
label.text = "A"
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(didDrag(_:))
label.addGestureRecognizer(recognizer)
label.isUserInteractionEnabled = true
}
I want the label to be draggable, so I implemented didDrag like this:
#objc func didDrag(_ gesture: UIPanGestureRecognizer) {
let center = label.center
let translation = gesture.translation(in: view)
label.center = CGPoint(x: center.x + translation.x, y: center.y + translation.y)
gesture.setTranslation(.zero, in: view)
}
It works perfectly and I can drag my label around.
However, if the label is over a button in my view and I try to tap the button, the button does not get the touch. I have tried:
label.isUserInteractionEnabled = false // then I can't also drag around
recognizer.cancelsTouchesInView = false // nothing changes
label.isMultipleTouchEnabled = false // nothing changes
Any idea how I can let single or double taps be passed/be ignored by the label's pan recognisers, but still be able to drag it?
I can delete this question if it's a duplicate, but I have not found any other that asks exactly the same.
Edit
Following Pass taps through a UIPanGestureRecognizer, the closest question I've found so far, I also tried:
recognizer.delaysTouchesBegan = true
recognizer.maximumNumberOfTouches = 1
recognizer.cancelsTouchesInView = true
But it, unfortunately, did not work.
Update
The view has all sorts of button and textfields, so comparing with each of them is not really possible, mostly because there are also stack views that contain buttons and sometimes they are there and somethings they are not.
After thinking I came up with the solution that might work. The idea is playing around with touch events. Every view has a set of methods like touchesBegan(), touchesMoved() etc. Now what you can do is when touchesBegan is invoked you can check the location of the touch that is happening and if this location contained in the frame of the button, call the function manually.
Here's the pseudocode so you get the concept. Not tested but from my experience something like that should work
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touches.forEach { touch in
let touchLocation = touch.location(in: myButton) // that may need to be self instead of myButton
if myButton.frame.contains($0.location) {
//invoke button method here
}
}
}

Is there a way for "force" the keyboard to dismiss in iOS Swift programmatically?

I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.

UIView with label + button - Tap gesture not recognized

I added a label and an image to the navigation item title view, like this - https://stackoverflow.com/a/38548905/1373592
And I added these three lines of code, to make the title clickable.
....
let recognizer = UITapGestureRecognizer(target: self, action: #selector(MyViewController.titleTapped(_:)))
navView.isUserInteractionEnabled = true
navView.addGestureRecognizer(recognizer)
And this titleTapped function.
#objc func titleTapped(_ tapGestureRecognizer: UITapGestureRecognizer) {
print("Tapped")
}
What am I doing wrong?
I tried adding gesture recognizer to the label, and to the image (separately). That didn't work either.
Thanks.
Your NavView has no frame, so there is nothing "there" to tap.
Add this line:
// Create a navView to add to the navigation bar
let navView = UIView()
// new line
navView.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
// Create the label
let label = UILabel()
and you should be on your way.
100% sure working in my app and well tested.
var tapGesture = UITapGestureRecognizer()
take your view and set IBOutlet like:
#IBOutlet weak var viewTap: UIView!
Write pretty code on viewDidLoad() like:
tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.myviewTapped(_:)))
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
viewTap.addGestureRecognizer(tapGesture)
viewTap.isUserInteractionEnabled = true
this method is calling when tap gesture recognized
#objc func myviewTapped(_ sender: UITapGestureRecognizer) {
if self.viewTap.backgroundColor == UIColor.yellow {
self.viewTap.backgroundColor = UIColor.green
}else{
self.viewTap.backgroundColor = UIColor.yellow
}
}
Note: In your case you have to sure for view which view infront like UILabel or UIView that is nav and then assign the gesture to it.If your label cover the whole view then let's try to give gesture to label only.

self.view.insertSubview background image array not advancing

I have my background image set by self.view.insertSubview. I'm trying to create a UISwipeGestureRecognizer that advances through the background Image on swipe, while another image array cycles on tap. The tap image works fine but the swipe image only works on the first swipe.
Here's the gesture recognizer:
let gestureRecognizerBackground = UISwipeGestureRecognizer(target: self, action: #selector(changeBackground))
dragon2View.addGestureRecognizer(gestureRecognizerBackground)
and here's the changeBackground func:
func changeBackground() {
let backgroundImageArray = [#imageLiteral(resourceName: "artic.png"),#imageLiteral(resourceName: "beach.png"),#imageLiteral(resourceName: "mountain.jpg"),#imageLiteral(resourceName: "spring.png")]
let randomBackground = Int(arc4random_uniform(UInt32 (backgroundImageArray.count)))
let backgroundImage = UIImageView(frame: UIScreen.main.bounds)
backgroundImage.image = backgroundImageArray[randomBackground]
self.view.insertSubview(backgroundImage, at: 0)
}
Not sure why it's not advancing. Thanks in advance for your comments.
I was trying to set the background image with self.view.insertSubview because I didn't realize I could just have two views and 'send to back'. No problem with the array now.

Resources