MAX Engineering
Published in

MAX Engineering

Lope (Customizable SliderView)

Just like my teacher would always say, don’t reinvent the wheel. These were the words that rang in my head when I was to use a SliderView in the Max Okada Champion app. Like designers do not care they just make very beautiful designs and tell you “hey I am done” I searched the web to get already implemented designs just to follow the approach but found nothing. Time was running out and I had to show that I still got it ‘LOL’ so I thought about the logic for a second the I decided to bring my thought to life

I though to myself should I use the interface builder or not and guess what, I ended up using pure code for Lope. Don’t run just yet, you can also implement using the interface builder. Let us dive into it…. yay

The first step is to setup your components

class Lope: UIView {

lazy var baseView: UIView = {
let baseView = UIView()
baseView.translatesAutoresizingMaskIntoConstraints = false
baseView.backgroundColor = .gray
return baseView
}()
lazy var sliderImage: UIImageView = {
let sliderImage = UIImageView()
sliderImage.translatesAutoresizingMaskIntoConstraints = false
sliderImage.backgroundColor = .blue
sliderImage.layer.cornerRadius = 10
sliderImage.isUserInteractionEnabled = true
sliderImage.clipsToBounds = true
return sliderImage
}()

lazy var title: UILabel = {
let title = UILabel()
title.text = "Slide Me..."
title.translatesAutoresizingMaskIntoConstraints = false
return title
}()

var viewCenter: CGPoint!
var startingFrame: CGRect?
weak var delegate: LopeDelegate?

let screenSize = UIScreen.main.bounds
var slided = false
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(baseView)
baseView.addSubview(sliderImage)
baseView.addSubview(title)
constraints()
swipeFunc()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

We declare a private function and name it swipeFunc()

We also declare a function to hold our constrains constraints()

Now to the constraint logic

func constraints() {

NSLayoutConstraint.activate([
baseView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0),
baseView.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0),
baseView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
baseView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
baseView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
baseView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0),

sliderImage.centerYAnchor.constraint(equalTo: baseView.centerYAnchor, constant: 0),
sliderImage.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 4),
sliderImage.topAnchor.constraint(equalTo: baseView.topAnchor, constant: 8),
sliderImage.bottomAnchor.constraint(equalTo: baseView.bottomAnchor, constant: -8),
sliderImage.aspectRation(1.0/1.0),

title.centerXAnchor.constraint(equalTo: baseView.centerXAnchor, constant: 0),
title.centerYAnchor.constraint(equalTo: baseView.centerYAnchor, constant: 0),
])

}

To the business logic

private func swipeFunc() {// 1 Create a Pan gestureRecognizer        
let swipeGesture = UIPanGestureRecognizer(target: self, action: #selector(acknowledgeSwiped(sender:)))
sliderImage.addGestureRecognizer(swipeGesture)
swipeGesture.delegate = self as? UIGestureRecognizerDelegate
}

@objc func acknowledgeSwiped(sender: UIPanGestureRecognizer) {
if let sliderView = sender.view {
// 2 Set the translation variable to the translation of the baseView let translation = sender.translation(in: self.baseView) // 3 For the cases of the Pan gesture, .begin, .changed, .ended/.default switch sender.state {
case .began:
startingFrame = sliderImage.frame
fallthrough
case .changed:
if let startFrame = startingFrame {

var movex = translation.x

if movex < -startFrame.origin.x {
movex = -startFrame.origin.x
}

let xMax = self.baseView.frame.width - startFrame.origin.x - startFrame.width
if movex > xMax {
movex = xMax
}
// 6 Using CGAffineTransform of CoreGraphics, we set the X value of the translation to movex and y to 0. Why is set to zero in order to make sure the sliderView only moves in the center of the backgroundView sliderView.transform = CGAffineTransform(translationX: movex, y: 0)
}

default:
// 7 after the pan reaches the end of the View, it is then animated back to its initial position and to avoid users dragging the slider to the right, we check to be sure that the sliderImage.frame.origin.x casted from CGFloat to Double is greater than zero.Then we called the endSlide Delegate Method UIView.animate(withDuration: 0.1, animations: {
if (Double(self.sliderImage.frame.origin.x) > 0) {
self.delegate?.endSlide(true)
}
sliderView.transform = CGAffineTransform.identity

})
}
}
}

// Overiding point is important to make the slider slide in the view
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return sliderImage.frame.contains(point)
}
using delegations to perfom request on begin and on finish of slideprotocol LopeDelegate: class {
func startSlide(_ start: Bool)
func endSlide(_ end: Bool)
}

Using this in your project is as easy as

var lope: Lope!

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .white
lope = Lope(frame: CGRect.zero)
lope.delegate = self
lope.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(lope)
setup()

}

func setup() {

NSLayoutConstraint.activate([
// lope.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0),
// lope.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0),

lope.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),

lope.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
lope.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
lope.heightAnchor.constraint(equalToConstant: 64),
])
}


}

extension ViewController: LopeDelegate {
func startSlide(_ start: Bool) {
print("startSlide Lope")
}

func endSlide(_ end: Bool) {
print("endSlide Lope")
}

}

Complete Project is available Via CocoaPods

https://github.com/AdieOlami/Lope

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Adie Olami

Software Developer (iOS). DevOps. BSc Mechanical Engineering. I love traveling. Happiness is Free & it’s a choice, be happy. Yay 🤗