Skip to content

Instantly share code, notes, and snippets.

@msepcot
Last active September 6, 2017 06:05
Show Gist options
  • Save msepcot/a770e5b3fb8040bd7655d76d256d0057 to your computer and use it in GitHub Desktop.
Save msepcot/a770e5b3fb8040bd7655d76d256d0057 to your computer and use it in GitHub Desktop.
Implement Mail.app's filter button in code
//
// FadeableButton.swift
// Octomino
//
// Created by Michael Sepcot on 9/5/17.
// Copyright © 2017 Head Down Development. All rights reserved.
//
import UIKit
class FadeableButton: UIControl {
static private let fadeAlpha:CGFloat = 0.2
static private let fadeDuration = 0.3
static private let touchAccessibilityDrift:CGFloat = 70
private var isFaded = false
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
fade(true, animated: false)
return true
}
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let expandedTouchTarget = CGRect(
x: -FadeableButton.touchAccessibilityDrift,
y: -FadeableButton.touchAccessibilityDrift,
width: frame.width + FadeableButton.touchAccessibilityDrift * 2,
height: frame.height + FadeableButton.touchAccessibilityDrift * 2
)
if expandedTouchTarget.contains(touch.location(in: self)) {
fade(true, animated: true)
} else {
fade(false, animated: true)
}
return true
}
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
fade(false, animated: true)
super.endTracking(touch, with: event)
}
override func cancelTracking(with event: UIEvent?) {
fade(false, animated: true)
super.cancelTracking(with: event)
}
private func fade(_ shouldFade: Bool, animated: Bool) {
guard isFaded != shouldFade else { return }
isFaded = shouldFade
UIView.animate(withDuration: animated ? FadeableButton.fadeDuration : 0.0) {
self.alpha = shouldFade ? FadeableButton.fadeAlpha : 1.0
}
}
}
//
// FilterButton.swift
// Octomino
//
// Created by Michael Sepcot on 8/29/17.
// Copyright © 2017 Head Down Development. All rights reserved.
//
import UIKit
@IBDesignable class FilterButton: FadeableButton {
private let buttonSize:CGFloat = 21
private let topLineLength:CGFloat = 14
private let middleLineLength:CGFloat = 10
private let bottomLineLength:CGFloat = 6
@IBInspectable var _isSelected: Bool = false {
willSet {
isSelected = newValue
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
tintColor.setStroke()
tintColor.setFill()
let offsetX = (rect.size.width - buttonSize) / 2
let offsetY = (rect.size.height - buttonSize) / 2
let circle = UIBezierPath(ovalIn: CGRect(x: offsetX, y: offsetY, width: buttonSize, height: buttonSize))
if isSelected {
circle.fill()
}
circle.lineWidth = 1
circle.stroke()
if isSelected {
UIColor.white.setStroke()
}
let topOffsetX = (rect.size.width - topLineLength) / 2
drawLine(from: CGPoint(x: topOffsetX, y: offsetY + 7),
to: CGPoint(x: topOffsetX + topLineLength, y: offsetY + 7))
let middleOffsetX = (rect.size.width - middleLineLength) / 2
drawLine(from: CGPoint(x: middleOffsetX, y: offsetY + 11),
to: CGPoint(x: middleOffsetX + middleLineLength, y: offsetY + 11))
let bottomOffsetX = (rect.size.width - bottomLineLength) / 2
drawLine(from: CGPoint(x: bottomOffsetX, y: offsetY + 15),
to: CGPoint(x: bottomOffsetX + bottomLineLength, y: offsetY + 15))
}
func drawLine(from startPoint: CGPoint, to endPoint: CGPoint) {
let line = UIBezierPath()
line.move(to: startPoint)
line.addLine(to: endPoint)
line.lineWidth = 1
line.stroke()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment