Created
August 31, 2016 20:42
-
-
Save JULI-ya/1a7c293b022207bb427caa3bbb9d3ed8 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
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