-
-
Save cipolleschi/d15767517d60a4b6bb96b85afb0eaf8a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GestureCode.swift | |
// Blending | |
// | |
// Created by Riccardo Cipolleschi on 14/11/2020. | |
// | |
import Foundation | |
import UIKit | |
// MARK: - View Model | |
struct VM { | |
var offset: CGPoint | |
} | |
// MARK: - View | |
class View: UIView { | |
var model: VM? { | |
didSet { | |
self.update(old: oldValue) | |
} | |
} | |
// MARK: Variable Declaration | |
private var ball = UIView() | |
// MARK: Interactions | |
var userDragged: ((CGPoint) -> ())? | |
// MARK: Initializers | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
self.setup() | |
self.style() | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
// MARK: Setup | |
private func setup() { | |
self.addSubview(self.ball) | |
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGestureRecognized)) | |
self.ball.addGestureRecognizer(gestureRecognizer) | |
} | |
@objc private func panGestureRecognized(_ gestureRecognizer: UIPanGestureRecognizer) { | |
switch gestureRecognizer.state { | |
case .began: | |
let currentOffset = model?.offset ?? .zero | |
let translation = gestureRecognizer.translation(in: self) | |
let initialTranslation = currentOffset + translation | |
gestureRecognizer.setTranslation(initialTranslation, in: self) | |
self.userDragged?(initialTranslation) | |
break | |
case .changed: | |
let translation = gestureRecognizer.translation(in: self) | |
self.userDragged?(translation) | |
break | |
case .ended: | |
let translation = gestureRecognizer.translation(in: self) | |
self.userDragged?(translation) | |
break | |
default: | |
break | |
} | |
} | |
// MARK: Style | |
private func style() { | |
self.backgroundColor = .black | |
self.ball.backgroundColor = .red | |
self.ball.layer.cornerRadius = 25 | |
} | |
// MARK: Update | |
private func update(old: VM?) { | |
self.setNeedsLayout() | |
} | |
// MARK: Layout | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
self.ball.center = self.center + model?.offset | |
self.ball.bounds.size = CGSize(width: 50, height: 50) | |
} | |
} | |
// MARK: - VC | |
class VC: UIViewController { | |
// Helper to retrieve a correctly typed view | |
var rootView: View { | |
return self.view as! View | |
} | |
// This override of `loadView` creates the right type of view and sets the initial vm | |
override func loadView() { | |
super.loadView() | |
let view = View() | |
view.model = VM(offset: .zero) | |
self.view = view | |
} | |
// The `viewDidLoad` method is used to connect the interactions | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
self.setupInteraction() | |
} | |
// It connects all the interactions from the View to the VC. | |
// Specifically, it receives the drag interaction in the view and updates the VM accordingly. | |
func setupInteraction() { | |
self.rootView.userDragged = { [unowned self] point in | |
self.rootView.model = VM(offset: point) | |
} | |
} | |
} | |
// MARK: - Helpers | |
extension CGPoint { | |
static func +(lhs: Self, rhs: Self?) -> CGPoint { | |
guard let right = rhs else { | |
return lhs | |
} | |
return .init(x: lhs.x + right.x, y: lhs.y + right.y) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment