I'm trying to create a feature similar to tableView swipe to show delete button, the only difference is that I show multiple buttons and have it implemented on a collectionView within a collectionViewCell. I want to be able to slide the inner collectionView to the right and have multiple options buttons snap into view on the left.
Something like this:
I understand that I'll probably need to use UIPanGestureRecognizer, the problem for me is that the collectionView to pan is nested within another collectionView, and I'm not certain as to how to use the UIGestureRecognizer correctly so that the cells slide together and the buttons snap into view.
Any suggestions are very much appreciated.
set delegate in collection view for get action
protocol ColumnBookCellDelegate: class {
func deleteBook(_ book: Book)
}
class ColumnBookCell: AZCollectionViewCell{
weak var deleteDelegate: ColumnBookCellDelegate?
var canBeRemove: Bool = false{
didSet{
if self.canBeRemove{
let swipeL = UISwipeGestureRecognizer(target: self, action: #selector(self.showDelete))
swipeL.numberOfTouchesRequired = 1
swipeL.direction = .left
self.addGestureRecognizer(swipeL)
let swipeR = UISwipeGestureRecognizer(target: self, action: #selector(self.hideDelete))
swipeR.numberOfTouchesRequired = 1
swipeR.direction = .right
self.addGestureRecognizer(swipeR)
}
}
}
// Show Delete Button
func showDelete(){
// unhidden button here
// self.button.isHidden = false
UIView.animate(withDuration: 0.2) {
self.layoutIfNeeded()
}
}
// Hide Delete Button
func hideDelete(){
// hidden button here
// self.button.isHidden = true
// self.deleteButton.aZConstraints.width?.constant = 0
UIView.animate(withDuration: 0.2) {
self.layoutIfNeeded()
}
}
// Delete Action
func deleteAction(){
self.deleteDelegate?.deleteBook(self.book)
self.hideDelete()
}
// blob blob blob
}
Related
I'm using BulletinBoard (BLTNBoard) to create dialogs in my iOS app. There's an option to embed image inside it. I would like to extend it's functionality and allow user to manipulate this image using tap gesture. But eventually when I assign a gesture to it's imageView using addGestureRecognizer nothing happens.
Here's how I initiliaze bulletin and add gesture to the image:
class ViewController: UIViewController {
lazy var bulletinManager: BLTNItemManager = {
let rootItem: BLTNPageItem = BLTNPageItem(title: "")
return BLTNItemManager(rootItem: rootItem)
}()
override func viewDidLoad() {
//etc code
let bulletinManager: BLTNItemManager = {
let item = BLTNPageItem(title: "Welcome")
item.descriptionText = "Pleas welcome to my app"
item.actionButtonTitle = "Go"
item.alternativeButtonTitle = "Try to tap here"
item.requiresCloseButton = false
item.isDismissable = false
item.actionHandler = { item in
self.bulletinManager.dismissBulletin()
}
item.alternativeHandler = { item in
//do nothing by now
}
//
item.image = UIImage(named: "welcome")
//adding gesture to its imageView
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
return BLTNItemManager(rootItem: item)
}()
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
and nothing happens at all (no message printed in console).
However if I assign action inside alternative button it works as expected:
item.alternativeHandler = { item in
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
}
I guess the only thing which can prevent me to assign the tap event to it properly is that imageView becomes available much later than the bulletin is created (for example only when it is shown on the screen).
Could you please help and correct my code. Thanks
upd.
Ok, based on Philipp's answer I have the following solution:
class myPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
let imageView=super.imageView
imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapTap))
imageView?.addGestureRecognizer(tap)
return contentViews
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
When you're working with an open source library, it's easy to check out the source code to find the answer.
As you can see here, image setter doesn't initiate the image view.
Both makeContentViews makeArrangedSubviews (which are responsible for views initializing) doesn't have any finish notification callbacks.
Usually in such cases I had to fork the repo and add functionality by myself - then I'll make a pull request if I think this functionality may be needed by someone else.
But luckily for you the BLTNPageItem is marked open, so you can just subclass it. Override makeContentViews and add your logic there, something like this:
class YourOwnPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
// configure the imageView here
return contentViews
}
}
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.
i have swipe gesture that switch between tab bars when I swipe in the screen right or left its change the tab bar im in it
How would I make it so it looks like it is sliding to the right or left tab bar rather than just instantly changing the tab bar
class SwipeGesture: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let left = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeft))
left.direction = .left
self.view.addGestureRecognizer(left)
let right = UISwipeGestureRecognizer(target: self, action: #selector(swipeRight))
right.direction = .right
self.view.addGestureRecognizer(right)
}
#objc func swipeLeft() {
let total = self.tabBarController!.viewControllers!.count - 1
tabBarController!.selectedIndex = min(total, tabBarController!.selectedIndex + 1)
}
#objc func swipeRight() {
tabBarController!.selectedIndex = max(0, tabBarController!.selectedIndex - 1)
}
}
You can achieve this effect using this pod
If you want to build it from scratch, you need to subclass containerView. Then put the view controllers (childVCs) inside a scrollView (scrolls only in horizontal direction).
I want to remove UIView from screen after user tap something except that view. (to visualize it for you I will upload sketch of my view)
And I want to remove blue UIView after user tap on something except buttons in this view. What should I use?
EDIT:
In blue UIView are two buttons and I want to remove that view when user tap on background image
I did what #yerpy told me to do but it isn't working.
func test(gestureRecognizer: UITapGestureRecognizer) {
print("test")
}
func setUpBackgroundImageView() {
self.view.addSubview(backgroundImageView)
backgroundImageView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
backgroundImageView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
backgroundImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
backgroundImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
let tap = UITapGestureRecognizer(target: self, action: #selector(test(gestureRecognizer:)))
backgroundImageView.addGestureRecognizer(tap)
tap.delegate = self
}
And I also add shouldReceiveTouch function to UIGestureRecognizerDelegate. What am I doing wrong?
Add UIGestureRecognizer to the super view :
As you said you have image view as a background.
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(gestureRecognizer:)))
imageView.addGestureRecognizer(tapRecognizer)
tapRecognizer.delegate = self
Adding target action :
func tapped(gestureRecognizer: UITapGestureRecognizer) {
// Remove the blue view.
}
And then inside UITapGestureRecognizerDelegate :
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.superview!.superclass! .isSubclass(of: UIButton.self) {
return false
}
return true
}
}
Hope it helps !
EDIT
Make sure that user can touch on the view by enabling : self.view.userInteractionEnabled = true
1- Add a view below your view, let call it overlay (gray one)
2- Add your container view with all your buttons inside (green one)
3- Add a tap gesture to the overlay (drag tap to overlay view)
4- Create a #IBAction of the tap to the viewcontroller
5- Write code to hide your green view inside the #IBAction
Image
Image
you can use UITapGestureRecognizer for that view
override func viewDidLoad() {
super.viewDidLoad()
// Add "tap" press gesture recognizer
let tap = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
tap.numberOfTapsRequired = 1
backgroundimage.addGestureRecognizer(tap)
}
// called by gesture recognizer
func tapHandler(gesture: UITapGestureRecognizer) {
// handle touch down and touch up events separately
if gesture.state == .began {
} else if gesture.state == .ended {
}
}
You can
subclass with that background view, and implement an inherited method -beganTouches:(there is another para but I forget what it is)
Add a UITapGestureRecognizer to that view
Add a giant button that covers the entire screen under all the buttons. Anytime the user presses on the giant button underneath the smaller buttons, do what you want it to do.
Not the most elegant but it works :P
I have an array of image views.
var imageViewArray = [UIImageView(image: UIImage())]
I use a for loop to fill this array with images from urls. I want to make it so that when I touch one of these images it becomes hidden or alpha: 0. I tried this:
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(StoryVC.imageTapped))
newImage.userInteractionEnabled = true
newImage.addGestureRecognizer(tapGestureRecognizer)
And I tried adding a tag too but I can't figure out how to get the sender. I need to be able to run the function to hide the image and know which image to hide, that is the part i'm struggling with. Thanks in advance.
You get UITapGestureRecognizer object in your selector's parameter and it has a property view that gives you the view which has been tapped.
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped(_:)))
func imageTapped(_ sender: UITapGestureRecognizer) {
guard let tappedImage = sender.view else { return }
}
The selector should be a function within your class. Here's an example:
// Setting up the tapGestureRecognizers
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(pressed:))
newImage.userInteractionEnabled = true
newImage.addGestureRecognizer(tapGestureRecognizer)
// The function that handles the tap event
func pressed(sender: UIImageView!) {
if sender.alpha == 0{
sender.alpha = 1
}
else{
self.alpha = 0
}
}
Also make sure to double check my syntax, I don't write with Swift often, so it may have some small errors.