DEV Community

Wesley de Groot
Wesley de Groot

Posted on • Originally published at wesleydegroot.nl on

Swipe actions in Swift

The issue:

Gestures, delegates, it can be a big struggle (especially for beginners).

If you use a lot of gestures then you’ll need to implement it over and over.

One of the problems is, that code what you write will not apply to other views in your application.

If we use a lot of swipe actions, in different places within our application we want to have reusable code.

But then we see ourselfs overloaded with issues, can it be made more easy?

Yes!

How can we make it more easy?

Using a UIViewextension.

Why not a UIViewController?

“Because we also want to support other applications which have a UIView.”

How do we start?

In this post we’ll start with a UIView extension, to make it reusable for other applications (e.g. UIImageView, UIView, ...).

In my case it didn’t work on a UITableViewController.

Psuedo code:

import Foundation
import UIKit
extension UIView {
// psuedo code continues
Enter fullscreen mode Exit fullscreen mode

We also need a variable, but simply var myVariable = ...does not work, since we are working in an extension.

There is a workaround, and it may be more easy than you think.

We’ll use a structand that will solve all our variable problems.

We want to reuse the data, so we make a static var.

Psuedo code:

// .... psuedo code to above blocks<br>struct gestureClosures {
    static var up = ...
    static var down = ...
    static var left = ...
    static var right = ...
}
Enter fullscreen mode Exit fullscreen mode

We also need to create a function, to make it work!

Psuedo code:

func swipeAction(
        swipeDirection: UISwipeGestureRecognizer.Direction,
        completionHandler: @escaping ()->()
        ) {
        // Add a swiper
        let swiper = .... #selector(self.invokeTarget(_:))
            // give the direction as in swipeDirection
            swiper.direction = swipeDirection
        // add to the view        
        self.addGestureRecognizer(swiper)

        // save the completionHandler
        switch swipeDirection {
        case .up:
            gestureClosures.up = completionHandler
        case .down:
            gestureClosures.down = completionHandler
        case .left:
            gestureClosures.left = completionHandler
        case .right:
            gestureClosures.right = completionHandler 
        default:
            print("Nothing")        
        }    
}
Enter fullscreen mode Exit fullscreen mode

But we still need to respond on the swipe actions!

Yup, pseudocode:

@objc func invokeTarget(...) {
    // disamble 
        switch swipeDirection {
        case .up:
            gestureClosures.up()
        case .down:
            gestureClosures.down()
        case .left:
            gestureClosures.left()
        case .right:
            gestureClosures.righ() 
        default:
            print("Nothing")        
        }
}
Enter fullscreen mode Exit fullscreen mode

And if we translate it to functional swift code.

Then the output looks similair to above.

The complete solution:

import Foundation
import UIKit

// Extend UIView
extension UIView {
    /// Setup a struct for saving the gesture handlers
    struct gestureHandler {
        /// Gesture "Up"
        static var up: (()->())? = nil
        /// Gesture "Down"
        static var down: (()->())? = nil
        /// Gesture "Left"
        static var left: (()->())? = nil
        /// Gesture "Right"
        static var right: (()->())? = nil
    }

    /**
     * Add a swipe action to UIView
     *
     * - Parameter swipeDirection: `.up`,`.down`,`.left`,`.right`
     * - Parameter completionHandler: The completionhandler.
     */
    func swipeAction(
        swipeDirection: UISwipeGestureRecognizer.Direction,
        completionHandler: @escaping ()->()
    ) {
        /// Add swipe handler
        let swiper = UISwipeGestureRecognizer(
            // UIView is the target
            target: self,

            // invokeTarget is our responder
            action: #selector(self.invokeTarget(_:))
        )

        // Set the direction of the swipe handler
        swiper.direction = swipeDirection

        // Add the gesture recognizer to the view
        self.addGestureRecognizer(swiper)

        // Switch between directions,
        // Save it to our struct.
        switch swipeDirection {
        case .up:
            // Save the completionHandler to gestureHandler.up
            gestureHandler.up = completionHandler
        case .down:
            // Save the completionHandler to gestureHandler.down
            gestureHandler.down = completionHandler
        case .left:
            // Save the completionHandler to gestureHandler.left
            gestureHandler.left = completionHandler
        case .right:
            // Save the completionHandler to gestureHandler.right
            gestureHandler.right = completionHandler
        default:
            print("Nothing")
        }
    }

    /**
     * Respond to a swipe action from UIView
     *
     * - Parameter gesture: The UIGestureRecognizer
     */
    @objc func invokeTarget(_ gesture: UIGestureRecognizer?) {
        /// Unwrap the swipeGesture
        if let swipeGesture = gesture as? UISwipeGestureRecognizer {
            // Switch between the direction
            switch swipeGesture.direction {
            case .up:
                /// Unwrap gestureHandler.up if possible
                guard let execute = gestureHandler.up else {
                    return
                }

                execute()
                break;
            case .down:
                /// Unwrap gestureHandler.down if possible
                guard let execute = gestureHandler.down else {
                    return
                }

                execute()
                break;
            case .left:
                /// Unwrap gestureHandler.left if possible
                guard let execute = gestureHandler.left else {
                    return
                }

                execute()
                break;
            case .right:
                /// Unwrap gestureHandler.right if possible
                guard let execute = gestureHandler.right else {
                    return
                }

                execute()
                break;
            default:
                print("N/A")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)