Screenshot |
Video Demo |
|
|
- Helper / Custom Swipe View
@objc public protocol MTSlideToOpenDelegate {
func mtSlideToOpenDelegateDidFinish(_ sender: MTSlideToOpenView)
}
@objcMembers public class MTSlideToOpenView: UIView {
// MARK: All Views
public let textLabel: UILabel = {
let label = UILabel.init()
return label
}()
public let sliderTextLabel: UILabel = {
let label = UILabel()
return label
}()
public let thumnailImageView: UIImageView = {
let view = MTRoundImageView()
view.isUserInteractionEnabled = true
view.contentMode = .center
return view
}()
public let sliderHolderView: UIView = {
let view = UIView()
return view
}()
public let draggedView: UIView = {
let view = UIView()
return view
}()
public let view: UIView = {
let view = UIView()
return view
}()
// MARK: Public properties
public weak var delegate: MTSlideToOpenDelegate?
public var animationVelocity: Double = 0.2
public var sliderViewTopDistance: CGFloat = 8.0 {
didSet {
topSliderConstraint?.constant = sliderViewTopDistance
layoutIfNeeded()
}
}
public var thumbnailViewTopDistance: CGFloat = 0.0 {
didSet {
topThumbnailViewConstraint?.constant = thumbnailViewTopDistance
layoutIfNeeded()
}
}
public var thumbnailViewStartingDistance: CGFloat = 0.0 {
didSet {
leadingThumbnailViewConstraint?.constant = thumbnailViewStartingDistance
trailingDraggedViewConstraint?.constant = thumbnailViewStartingDistance
setNeedsLayout()
}
}
public var textLabelLeadingDistance: CGFloat = 0 {
didSet {
leadingTextLabelConstraint?.constant = textLabelLeadingDistance
setNeedsLayout()
}
}
public var isEnabled:Bool = true {
didSet {
animationChangedEnabledBlock?(isEnabled)
}
}
public var showSliderText:Bool = false {
didSet {
sliderTextLabel.isHidden = !showSliderText
}
}
public var animationChangedEnabledBlock:((Bool) -> Void)?
// MARK: Default styles
public var sliderCornerRadius: CGFloat = 30.0 {
didSet {
sliderHolderView.layer.cornerRadius = sliderCornerRadius
draggedView.layer.cornerRadius = sliderCornerRadius
}
}
public var sliderBackgroundColor: UIColor = UIColor(red:0.1, green:0.61, blue:0.84, alpha:0.1) {
didSet {
sliderHolderView.backgroundColor = sliderBackgroundColor
sliderTextLabel.textColor = sliderBackgroundColor
}
}
public var textColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:0.7) {
didSet {
textLabel.textColor = textColor
}
}
public var slidingColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:0.7) {
didSet {
draggedView.backgroundColor = slidingColor
}
}
public var thumbnailColor:UIColor = UIColor(red:25.0/255, green:155.0/255, blue:215.0/255, alpha:1) {
didSet {
thumnailImageView.backgroundColor = thumbnailColor
}
}
public var labelText: String = "Swipe to open" {
didSet {
textLabel.text = labelText
sliderTextLabel.text = labelText
}
}
public var textFont: UIFont = UIFont.systemFont(ofSize: 15.0) {
didSet {
textLabel.font = textFont
sliderTextLabel.font = textFont
}
}
// MARK: Private Properties
private var leadingThumbnailViewConstraint: NSLayoutConstraint?
private var leadingTextLabelConstraint: NSLayoutConstraint?
private var topSliderConstraint: NSLayoutConstraint?
private var topThumbnailViewConstraint: NSLayoutConstraint?
private var trailingDraggedViewConstraint: NSLayoutConstraint?
private var xPositionInThumbnailView: CGFloat = 0
private var xEndingPoint: CGFloat {
get {
return (self.view.frame.maxX - thumnailImageView.bounds.width - thumbnailViewStartingDistance)
}
}
private var isFinished: Bool = false
override public init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private var panGestureRecognizer: UIPanGestureRecognizer!
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupView()
}
private func setupView() {
self.addSubview(view)
view.addSubview(thumnailImageView)
view.addSubview(sliderHolderView)
view.addSubview(draggedView)
draggedView.addSubview(sliderTextLabel)
sliderHolderView.addSubview(textLabel)
view.bringSubviewToFront(self.thumnailImageView)
setupConstraint()
setStyle()
// Add pan gesture
panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
thumnailImageView.addGestureRecognizer(panGestureRecognizer)
}
private func setupConstraint() {
view.translatesAutoresizingMaskIntoConstraints = false
thumnailImageView.translatesAutoresizingMaskIntoConstraints = false
sliderHolderView.translatesAutoresizingMaskIntoConstraints = false
textLabel.translatesAutoresizingMaskIntoConstraints = false
sliderTextLabel.translatesAutoresizingMaskIntoConstraints = false
draggedView.translatesAutoresizingMaskIntoConstraints = false
// Setup for view
view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
view.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
// Setup for circle View
leadingThumbnailViewConstraint = thumnailImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
leadingThumbnailViewConstraint?.isActive = true
topThumbnailViewConstraint = thumnailImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: thumbnailViewTopDistance)
topThumbnailViewConstraint?.isActive = true
thumnailImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
thumnailImageView.heightAnchor.constraint(equalTo: thumnailImageView.widthAnchor).isActive = true
// Setup for slider holder view
topSliderConstraint = sliderHolderView.topAnchor.constraint(equalTo: view.topAnchor, constant: sliderViewTopDistance)
topSliderConstraint?.isActive = true
sliderHolderView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
sliderHolderView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
sliderHolderView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Setup for textLabel
textLabel.topAnchor.constraint(equalTo: sliderHolderView.topAnchor).isActive = true
textLabel.centerYAnchor.constraint(equalTo: sliderHolderView.centerYAnchor).isActive = true
leadingTextLabelConstraint = textLabel.leadingAnchor.constraint(equalTo: sliderHolderView.leadingAnchor, constant: textLabelLeadingDistance)
leadingTextLabelConstraint?.isActive = true
textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-8)).isActive = true
// Setup for sliderTextLabel
sliderTextLabel.topAnchor.constraint(equalTo: textLabel.topAnchor).isActive = true
sliderTextLabel.centerYAnchor.constraint(equalTo: textLabel.centerYAnchor).isActive = true
sliderTextLabel.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor).isActive = true
sliderTextLabel.trailingAnchor.constraint(equalTo: textLabel.trailingAnchor).isActive = true
// Setup for Dragged View
draggedView.leadingAnchor.constraint(equalTo: sliderHolderView.leadingAnchor).isActive = true
draggedView.topAnchor.constraint(equalTo: sliderHolderView.topAnchor).isActive = true
draggedView.centerYAnchor.constraint(equalTo: sliderHolderView.centerYAnchor).isActive = true
trailingDraggedViewConstraint = draggedView.trailingAnchor.constraint(equalTo: thumnailImageView.trailingAnchor, constant: thumbnailViewStartingDistance)
trailingDraggedViewConstraint?.isActive = true
}
private func setStyle() {
thumnailImageView.backgroundColor = thumbnailColor
textLabel.text = labelText
textLabel.font = textFont
textLabel.textColor = textColor
textLabel.textAlignment = .center
sliderTextLabel.text = labelText
sliderTextLabel.font = textFont
sliderTextLabel.textColor = sliderBackgroundColor
sliderTextLabel.textAlignment = .center
sliderTextLabel.isHidden = !showSliderText
if isOnRightToLeftLanguage() {
textLabel.mt_flipView()
sliderTextLabel.mt_flipView()
}
sliderHolderView.backgroundColor = sliderBackgroundColor
sliderHolderView.layer.cornerRadius = sliderCornerRadius
draggedView.backgroundColor = slidingColor
draggedView.layer.cornerRadius = sliderCornerRadius
draggedView.clipsToBounds = true
draggedView.layer.masksToBounds = true
}
private func isTapOnThumbnailViewWithPoint(_ point: CGPoint) -> Bool{
return self.thumnailImageView.frame.contains(point)
}
private func updateThumbnailXPosition(_ x: CGFloat) {
leadingThumbnailViewConstraint?.constant = x
setNeedsLayout()
}
// MARK: UIPanGestureRecognizer
@objc private func handlePanGesture(_ sender: UIPanGestureRecognizer) {
if isFinished || !isEnabled {
return
}
let translatedPoint = sender.translation(in: view).x * (self.isOnRightToLeftLanguage() ? -1 : 1)
switch sender.state {
case .began:
break
case .changed:
if translatedPoint >= xEndingPoint {
updateThumbnailXPosition(xEndingPoint)
return
}
if translatedPoint <= thumbnailViewStartingDistance {
textLabel.alpha = 1
updateThumbnailXPosition(thumbnailViewStartingDistance)
return
}
updateThumbnailXPosition(translatedPoint)
textLabel.alpha = (xEndingPoint - translatedPoint) / xEndingPoint
break
case .ended:
if translatedPoint >= xEndingPoint {
textLabel.alpha = 0
updateThumbnailXPosition(xEndingPoint)
// Finish action
isFinished = true
delegate?.mtSlideToOpenDelegateDidFinish(self)
return
}
if translatedPoint <= thumbnailViewStartingDistance {
textLabel.alpha = 1
updateThumbnailXPosition(thumbnailViewStartingDistance)
return
}
UIView.animate(withDuration: animationVelocity) {
self.leadingThumbnailViewConstraint?.constant = self.thumbnailViewStartingDistance
self.textLabel.alpha = 1
self.layoutIfNeeded()
}
break
default:
break
}
}
// Others
public func resetStateWithAnimation(_ animated: Bool) {
let action = {
self.leadingThumbnailViewConstraint?.constant = self.thumbnailViewStartingDistance
self.textLabel.alpha = 1
self.layoutIfNeeded()
//
self.isFinished = false
}
if animated {
UIView.animate(withDuration: animationVelocity) {
action()
}
} else {
action()
}
}
// MARK: Helpers
func isOnRightToLeftLanguage() -> Bool {
return UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft
}
}
extension UIView {
func mt_flipView() {
self.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
}
}
class MTRoundImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
let radius: CGFloat = self.bounds.size.width / 2.0
self.layer.cornerRadius = radius
}
}
- Usage in ViewController
import UIKit
class TestCollectionVC: UIViewController, MTSlideToOpenDelegate {
lazy var slideToOpen: MTSlideToOpenView = {
let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 100, width: 317, height: 56))
slide.sliderViewTopDistance = 0
slide.sliderCornerRadius = 28
slide.showSliderText = true
slide.thumbnailColor = UIColor(red:141.0/255, green:19.0/255, blue:65.0/255, alpha:1.0)
slide.slidingColor = UIColor.red
slide.textColor = UIColor.orange
slide.sliderBackgroundColor = UIColor(red:0.88, green:1, blue:0.98, alpha:1.0)
slide.delegate = self
slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection()
return slide
}()
lazy var slideToLock: MTSlideToOpenView = {
let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 200, width: 317, height: 56))
slide.sliderViewTopDistance = 0
slide.sliderCornerRadius = 28
slide.thumnailImageView.backgroundColor = UIColor(red:200.0/255, green:200.0/255, blue:200.0/255, alpha:1.0)
slide.draggedView.backgroundColor = UIColor(red:200.0/255, green:200.0/255, blue:200.0/255, alpha:1.0)
slide.delegate = self
slide.thumbnailViewStartingDistance = 20
slide.labelText = "Slide To Lock"
slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection()
return slide
}()
lazy var customizeSlideToOpen: MTSlideToOpenView = {
let slide = MTSlideToOpenView(frame: CGRect(x: 26, y: 300, width: 317, height: 56))
slide.sliderViewTopDistance = 0
slide.thumbnailViewTopDistance = 4;
slide.thumbnailViewStartingDistance = 4;
slide.sliderCornerRadius = 28
slide.thumnailImageView.backgroundColor = .white
slide.draggedView.backgroundColor = .clear
slide.delegate = self
slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection()
slide.sliderBackgroundColor = .black
return slide
}()
lazy var slideToUnlock: MTSlideToOpenView = {
let frame = CGRect(x: 26, y: 400, width: 317, height: 56)
let slide = MTSlideToOpenView(frame: frame)
slide.sliderViewTopDistance = 6
slide.sliderCornerRadius = 22
slide.delegate = self
slide.labelText = "Slide To Unlock"
slide.thumnailImageView.image = #imageLiteral(resourceName: "ic_arrow").imageFlippedForRightToLeftLayoutDirection()
slide.animationChangedEnabledBlock = { isEnabled in
if isEnabled {
slide.thumnailImageView.backgroundColor = slide.thumbnailColor
slide.draggedView.isHidden = false
slide.thumnailImageView.backgroundColor = slide.sliderBackgroundColor
slide.textLabel.text = slide.labelText
slide.thumnailImageView.layer.shadowOpacity = 0
} else {
slide.thumnailImageView.backgroundColor = UIColor(red:210.0/255, green:219.0/255, blue:228.0/255, alpha:0.8)
slide.thumnailImageView.layer.shadowColor = UIColor(red:0/255, green:0/255, blue:0/255, alpha:0.2).cgColor
slide.thumnailImageView.layer.shadowOffset = CGSize(width: 0, height: 25)
slide.thumnailImageView.layer.shadowRadius = 28
slide.thumnailImageView.layer.shadowOpacity = 1
slide.draggedView.isHidden = true
slide.sliderHolderView.backgroundColor = UIColor(red:245.0/255, green:247.0/255, blue:250.0/255, alpha:1)
slide.textLabel.text = ""
}
}
slide.isEnabled = false
return slide
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(slideToOpen)
self.view.addSubview(slideToUnlock)
self.view.addSubview(customizeSlideToOpen)
self.view.addSubview(slideToLock)
}
// MARK: MTSlideToOpenDelegate
func mtSlideToOpenDelegateDidFinish(_ sender: MTSlideToOpenView) {
let alertController = UIAlertController(title: "", message: "Done!", preferredStyle: .alert)
let doneAction = UIAlertAction(title: "Okay", style: .default) { (action) in
sender.resetStateWithAnimation(false)
}
alertController.addAction(doneAction)
self.present(alertController, animated: true, completion: nil)
}
}