Skip to content

Instantly share code, notes, and snippets.

@JULI-ya
Created August 31, 2016 20:42
Show Gist options
  • Save JULI-ya/1a7c293b022207bb427caa3bbb9d3ed8 to your computer and use it in GitHub Desktop.
Save JULI-ya/1a7c293b022207bb427caa3bbb9d3ed8 to your computer and use it in GitHub Desktop.
import UIKit
enum SeparatorType {
case Horisontal
case Vertical
}
let kTotalSize: CGFloat = 44; // the total height of the separator (including parts that are not visible
let kVisibleSize: CGFloat = 10; // the height of the visible portion of the separator
let kMargin: CGFloat = (kTotalSize - kVisibleSize) / 2.0; // the height of the non-visible portions of the separator (i.e. above and below the visible portion)
let kMinSize: CGFloat = 10; // the minimum height allowed for views above and below the separator
protocol OnConstraintsUpdateProtocol {
func updateConstraintOnBasisOfTouch(touch: UITouch)
}
class SeparatorView: UIView {
var startConstraint: NSLayoutConstraint? // the constraint that dictates the vertical position of the separator
var primaryView: UIView // the view above the separator
var secondaryView: UIView // the view below the separator
// some properties used for handling the touches
var oldPosition: CGFloat = 0.0 // the position of the separator before the gesture started
var firstTouch: CGPoint? // the position where the drag gesture started
var updateListener: OnConstraintsUpdateProtocol?
internal static func addSeparatorBetweenViews(separatorType: SeparatorType, primaryView: UIView, secondaryView: UIView, parentView: UIView) -> SeparatorView {
var separator: SeparatorView
if (separatorType == .Horisontal) {
separator = HorizontalSeparatorView(primaryView: primaryView, secondaryView: secondaryView)
} else {
separator = VerticalSeparatorView(primaryView: primaryView, secondaryView: secondaryView)
}
separator.setupParentViewConstraints(parentView)
parentView.addSubview(separator)
separator.setupSeparatorConstraints()
return separator
}
init(primaryView: UIView, secondaryView: UIView){
self.primaryView = primaryView
self.secondaryView = secondaryView
super.init(frame: CGRectZero)
self.translatesAutoresizingMaskIntoConstraints = false
self.userInteractionEnabled = true
self.backgroundColor = UIColor.clearColor()
}
func setupParentViewConstraints(parentView: UIView){}
func setupSeparatorConstraints(){}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.firstTouch = touches.first?.locationInView(self.superview)
self.startConstraint!.constant = self.oldPosition;
self.startConstraint!.active = true;
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first, event = event else { return }
// for more responsive UX, use predicted touches, if possible
let predictedTouch = event.predictedTouchesForTouch(touch)?.last
if ((predictedTouch) != nil) {
updateListener?.updateConstraintOnBasisOfTouch(predictedTouch!)
return;
}
// if no predicted touch found, just use the touch provided
updateListener?.updateConstraintOnBasisOfTouch(touch)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
updateListener?.updateConstraintOnBasisOfTouch(touch)
}
override func drawRect(rect: CGRect) {
let path = UIBezierPath(rect: rect)
UIColor.blackColor().set()
path.stroke()
path.fill()
}
}
class HorizontalSeparatorView: SeparatorView, OnConstraintsUpdateProtocol {
override init(primaryView: UIView, secondaryView: UIView) {
super.init(primaryView: primaryView, secondaryView: secondaryView)
updateListener = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupParentViewConstraints(parentView: UIView) {
parentView.leadingAnchor.constraintEqualToAnchor(primaryView.leadingAnchor).active = true
parentView.trailingAnchor.constraintEqualToAnchor(primaryView.trailingAnchor).active = true
parentView.leadingAnchor.constraintEqualToAnchor(secondaryView.leadingAnchor).active = true
parentView.trailingAnchor.constraintEqualToAnchor(secondaryView.trailingAnchor).active = true
parentView.topAnchor.constraintEqualToAnchor(primaryView.topAnchor).active = true
let height = secondaryView.heightAnchor.constraintEqualToAnchor(primaryView.heightAnchor)
height.priority = 250
height.active = true
parentView.bottomAnchor.constraintEqualToAnchor(secondaryView.bottomAnchor).active = true
}
override func setupSeparatorConstraints() {
self.heightAnchor.constraintEqualToConstant(kTotalSize).active = true
self.superview?.leadingAnchor.constraintEqualToAnchor(self.leadingAnchor).active = true
self.superview?.trailingAnchor.constraintEqualToAnchor(self.trailingAnchor).active = true
primaryView.bottomAnchor.constraintEqualToAnchor(self.topAnchor, constant: kMargin).active = true
secondaryView.topAnchor.constraintEqualToAnchor(self.bottomAnchor, constant: -kMargin).active = true
startConstraint = self.topAnchor.constraintEqualToAnchor(self.superview?.topAnchor, constant: 0)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.oldPosition = self.frame.origin.y;
super.touchesBegan(touches, withEvent: event)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesMoved(touches, withEvent: event)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
}
func updateConstraintOnBasisOfTouch(touch: UITouch) {
// calculate where separator should be moved to
var y: CGFloat = self.oldPosition + touch.locationInView(self.superview).y - self.firstTouch!.y
// make sure the views above and below are not too small
y = max(y, self.primaryView.frame.origin.y + kMinSize - kMargin)
y = min(y, self.secondaryView.frame.origin.y + self.secondaryView.frame.size.height - (kMargin + kMinSize))
// set constraint
self.startConstraint!.constant = y
}
override func drawRect(rect: CGRect) {
let separatorRect = CGRectMake(0, kMargin, self.bounds.size.width, kVisibleSize)
super.drawRect(separatorRect)
}
}
class VerticalSeparatorView: SeparatorView, OnConstraintsUpdateProtocol {
override init(primaryView: UIView, secondaryView: UIView) {
super.init(primaryView: primaryView, secondaryView: secondaryView)
updateListener = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setupParentViewConstraints(parentView: UIView) {
parentView.topAnchor.constraintEqualToAnchor(primaryView.topAnchor).active = true
parentView.topAnchor.constraintEqualToAnchor(secondaryView.topAnchor).active = true
parentView.bottomAnchor.constraintEqualToAnchor(primaryView.bottomAnchor).active = true
parentView.leadingAnchor.constraintEqualToAnchor(secondaryView.topAnchor).active = true
parentView.bottomAnchor.constraintEqualToAnchor(secondaryView.bottomAnchor).active = true
parentView.leadingAnchor.constraintEqualToAnchor(primaryView.leadingAnchor).active = true
let width = secondaryView.widthAnchor.constraintEqualToAnchor(primaryView.widthAnchor)
width.priority = 250
width.active = true
parentView.trailingAnchor.constraintEqualToAnchor(secondaryView.trailingAnchor).active = true
}
override func setupSeparatorConstraints() {
self.widthAnchor.constraintEqualToConstant(kTotalSize).active = true
self.superview?.topAnchor.constraintEqualToAnchor(self.topAnchor).active = true
self.superview?.bottomAnchor.constraintEqualToAnchor(self.bottomAnchor).active = true
primaryView.trailingAnchor.constraintEqualToAnchor(self.leadingAnchor, constant: kMargin).active = true
secondaryView.leadingAnchor.constraintEqualToAnchor(self.trailingAnchor, constant: -kMargin).active = true
startConstraint = self.leadingAnchor.constraintEqualToAnchor(self.superview?.leadingAnchor, constant: 0)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.oldPosition = self.frame.origin.x;
super.touchesBegan(touches, withEvent: event)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesMoved(touches, withEvent: event)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
}
func updateConstraintOnBasisOfTouch(touch: UITouch) {
// calculate where separator should be moved to
var x: CGFloat = self.oldPosition + touch.locationInView(self.superview).x - self.firstTouch!.x
// make sure the views above and below are not too small
x = max(x, self.primaryView.frame.origin.x + kMinSize - kMargin)
x = min(x, self.secondaryView.frame.origin.x + self.secondaryView.frame.size.width - (kMargin + kMinSize))
// set constraint
self.startConstraint!.constant = x
}
override func drawRect(rect: CGRect) {
let separatorRect = CGRectMake(kMargin, 0, kVisibleSize, self.bounds.size.height)
super.drawRect(separatorRect)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment