From 76d68e7dd8249205acdff5b32a743327cc9db423 Mon Sep 17 00:00:00 2001 From: Andrio Frizon Date: Wed, 12 Jul 2017 18:24:20 -0300 Subject: [PATCH 1/2] Update frame size constraints --- .../TwicketSegmentedControl.swift | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/TwicketSegmentedControl/TwicketSegmentedControl.swift b/TwicketSegmentedControl/TwicketSegmentedControl.swift index 030f3b2..31d8d41 100644 --- a/TwicketSegmentedControl/TwicketSegmentedControl.swift +++ b/TwicketSegmentedControl/TwicketSegmentedControl.swift @@ -13,14 +13,6 @@ public protocol TwicketSegmentedControlDelegate: class { } open class TwicketSegmentedControl: UIControl { - open static let height: CGFloat = Constants.height + Constants.topBottomMargin * 2 - - private struct Constants { - static let height: CGFloat = 30 - static let topBottomMargin: CGFloat = 5 - static let leadingTrailingMargin: CGFloat = 10 - } - class SliderView: UIView { // MARK: - Properties fileprivate let sliderMaskView = UIView() @@ -161,10 +153,7 @@ open class TwicketSegmentedControl: UIControl { } private func configureViews() { - containerView.frame = CGRect(x: Constants.leadingTrailingMargin, - y: Constants.topBottomMargin, - width: bounds.width - Constants.leadingTrailingMargin * 2, - height: Constants.height) + containerView.frame = self.bounds let frame = containerView.bounds backgroundView.frame = frame selectedContainerView.frame = frame From dea0b529de2a026e4824231e4aaf3a04e34d9122 Mon Sep 17 00:00:00 2001 From: Andrio Frizon Date: Wed, 12 Jul 2017 18:24:42 -0300 Subject: [PATCH 2/2] Add border to segmented control --- .../TwicketSegmentedControl.swift | 119 ++++++++++-------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/TwicketSegmentedControl/TwicketSegmentedControl.swift b/TwicketSegmentedControl/TwicketSegmentedControl.swift index 31d8d41..116bb26 100644 --- a/TwicketSegmentedControl/TwicketSegmentedControl.swift +++ b/TwicketSegmentedControl/TwicketSegmentedControl.swift @@ -16,169 +16,182 @@ open class TwicketSegmentedControl: UIControl { class SliderView: UIView { // MARK: - Properties fileprivate let sliderMaskView = UIView() - + var cornerRadius: CGFloat! { didSet { layer.cornerRadius = cornerRadius sliderMaskView.layer.cornerRadius = cornerRadius } } - + override var frame: CGRect { didSet { sliderMaskView.frame = frame } } - + override var center: CGPoint { didSet { sliderMaskView.center = center } } - + init() { super.init(frame: .zero) setup() } - + required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } - + private func setup() { layer.masksToBounds = true sliderMaskView.backgroundColor = .black sliderMaskView.addShadow(with: .black) } } - + open weak var delegate: TwicketSegmentedControlDelegate? - + open var defaultTextColor: UIColor = Palette.defaultTextColor { didSet { updateLabelsColor(with: defaultTextColor, selected: false) } } - + open var highlightTextColor: UIColor = Palette.highlightTextColor { didSet { updateLabelsColor(with: highlightTextColor, selected: true) } } - + open var segmentsBackgroundColor: UIColor = Palette.segmentedControlBackgroundColor { didSet { backgroundView.backgroundColor = segmentsBackgroundColor } } - + open var sliderBackgroundColor: UIColor = Palette.sliderColor { didSet { selectedContainerView.backgroundColor = sliderBackgroundColor if !isSliderShadowHidden { selectedContainerView.addShadow(with: sliderBackgroundColor) } } } - + open var font: UIFont = UIFont.systemFont(ofSize: 15, weight: UIFontWeightMedium) { didSet { updateLabelsFont(with: font) } } - + open var isSliderShadowHidden: Bool = false { didSet { updateShadow(with: sliderBackgroundColor, hidden: isSliderShadowHidden) } } - + private(set) open var selectedSegmentIndex: Int = 0 - + private var segments: [String] = [] - + private var numberOfSegments: Int { return segments.count } - + + open var borderColor: UIColor = Palette.sliderColor { + didSet { + backgroundView.layer.borderColor = borderColor.cgColor + } + } + + open var borderWidth: CGFloat = 1.0 { + didSet { + backgroundView.layer.borderWidth = borderWidth + } + } + private var segmentWidth: CGFloat { return self.backgroundView.frame.width / CGFloat(numberOfSegments) } - + private var correction: CGFloat = 0 - + private lazy var containerView: UIView = UIView() private lazy var backgroundView: UIView = UIView() private lazy var selectedContainerView: UIView = UIView() private lazy var sliderView: SliderView = SliderView() - + public override init(frame: CGRect) { super.init(frame: frame) setup() } - + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } - + // MARK: Setup - + private func setup() { addSubview(containerView) containerView.addSubview(backgroundView) containerView.addSubview(selectedContainerView) containerView.addSubview(sliderView) - + selectedContainerView.layer.mask = sliderView.sliderMaskView.layer addTapGesture() addDragGesture() } - + open func setSegmentItems(_ segments: [String]) { guard !segments.isEmpty else { fatalError("Segments array cannot be empty") } - + self.segments = segments configureViews() - + clearLabels() - + for (index, title) in segments.enumerated() { let baseLabel = createLabel(with: title, at: index, selected: false) let selectedLabel = createLabel(with: title, at: index, selected: true) backgroundView.addSubview(baseLabel) selectedContainerView.addSubview(selectedLabel) } - + setupAutoresizingMasks() } - + private func configureViews() { containerView.frame = self.bounds let frame = containerView.bounds backgroundView.frame = frame selectedContainerView.frame = frame sliderView.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: backgroundView.frame.height) - + let cornerRadius = backgroundView.frame.height / 2 [backgroundView, selectedContainerView].forEach { $0.layer.cornerRadius = cornerRadius } sliderView.cornerRadius = cornerRadius - - backgroundColor = .white + backgroundView.backgroundColor = segmentsBackgroundColor + backgroundView.layer.borderColor = borderColor.cgColor + backgroundView.layer.borderWidth = borderWidth selectedContainerView.backgroundColor = sliderBackgroundColor - + if !isSliderShadowHidden { selectedContainerView.addShadow(with: sliderBackgroundColor) } } - + private func setupAutoresizingMasks() { containerView.autoresizingMask = [.flexibleWidth] backgroundView.autoresizingMask = [.flexibleWidth] selectedContainerView.autoresizingMask = [.flexibleWidth] sliderView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleWidth] } - + private func updateShadow(with color: UIColor, hidden: Bool) { if hidden { selectedContainerView.removeShadow() @@ -188,14 +201,14 @@ open class TwicketSegmentedControl: UIControl { sliderView.sliderMaskView.addShadow(with: .black) } } - + // MARK: Labels - + private func clearLabels() { backgroundView.subviews.forEach { $0.removeFromSuperview() } selectedContainerView.subviews.forEach { $0.removeFromSuperview() } } - + private func createLabel(with text: String, at index: Int, selected: Bool) -> UILabel { let rect = CGRect(x: CGFloat(index) * segmentWidth, y: 0, width: segmentWidth, height: backgroundView.frame.height) let label = UILabel(frame: rect) @@ -206,33 +219,33 @@ open class TwicketSegmentedControl: UIControl { label.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleWidth] return label } - + private func updateLabelsColor(with color: UIColor, selected: Bool) { let containerView = selected ? selectedContainerView : backgroundView containerView.subviews.forEach { ($0 as? UILabel)?.textColor = color } } - + private func updateLabelsFont(with font: UIFont) { selectedContainerView.subviews.forEach { ($0 as? UILabel)?.font = font } backgroundView.subviews.forEach { ($0 as? UILabel)?.font = font } } - + // MARK: Tap gestures - + private func addTapGesture() { let tap = UITapGestureRecognizer(target: self, action: #selector(didTap)) addGestureRecognizer(tap) } - + private func addDragGesture() { let drag = UIPanGestureRecognizer(target: self, action: #selector(didPan)) sliderView.addGestureRecognizer(drag) } - + @objc private func didTap(tapGesture: UITapGestureRecognizer) { moveToNearestPoint(basedOn: tapGesture) } - + @objc private func didPan(panGesture: UIPanGestureRecognizer) { switch panGesture.state { case .cancelled, .ended, .failed: @@ -245,9 +258,9 @@ open class TwicketSegmentedControl: UIControl { case .possible: () } } - + // MARK: Slider position - + private func moveToNearestPoint(basedOn gesture: UIGestureRecognizer, velocity: CGPoint? = nil) { var location = gesture.location(in: self) if let velocity = velocity { @@ -258,26 +271,26 @@ open class TwicketSegmentedControl: UIControl { move(to: index) delegate?.didSelect(index) } - + open func move(to index: Int) { let correctOffset = center(at: index) animate(to: correctOffset) - + selectedSegmentIndex = index } - + private func segmentIndex(for point: CGPoint) -> Int { var index = Int(point.x / sliderView.frame.width) if index < 0 { index = 0 } if index > numberOfSegments - 1 { index = numberOfSegments - 1 } return index } - + private func center(at index: Int) -> CGFloat { let xOffset = CGFloat(index) * sliderView.frame.width + sliderView.frame.width / 2 return xOffset } - + private func animate(to position: CGFloat) { UIView.animate(withDuration: 0.2) { self.sliderView.center.x = position