mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-21 07:47:32 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
137
mobile/ios/ThirdParty/FilledPageControl.swift
vendored
Normal file
137
mobile/ios/ThirdParty/FilledPageControl.swift
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// FilledPageControl
|
||||
//
|
||||
// Copyright (c) 2016 Kyle Zaragoza <popwarsweet@gmail.com>
|
||||
// MIT License
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable open class FilledPageControl: UIView {
|
||||
|
||||
// MARK: - PageControl
|
||||
|
||||
@IBInspectable open var pageCount: Int = 0 {
|
||||
didSet {
|
||||
updateNumberOfPages(pageCount)
|
||||
}
|
||||
}
|
||||
@IBInspectable open var progress: CGFloat = 0 {
|
||||
didSet {
|
||||
updateActivePageIndicatorMasks(progress)
|
||||
}
|
||||
}
|
||||
open var currentPage: Int {
|
||||
return Int(round(progress))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Appearance
|
||||
|
||||
override open var tintColor: UIColor! {
|
||||
didSet {
|
||||
inactiveLayers.forEach() { $0.backgroundColor = tintColor.cgColor }
|
||||
}
|
||||
}
|
||||
@IBInspectable open var inactiveRingWidth: CGFloat = 1 {
|
||||
didSet {
|
||||
updateActivePageIndicatorMasks(progress)
|
||||
}
|
||||
}
|
||||
@IBInspectable open var indicatorPadding: CGFloat = 10 {
|
||||
didSet {
|
||||
layoutPageIndicators(inactiveLayers)
|
||||
}
|
||||
}
|
||||
@IBInspectable open var indicatorRadius: CGFloat = 5 {
|
||||
didSet {
|
||||
layoutPageIndicators(inactiveLayers)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var indicatorDiameter: CGFloat {
|
||||
return indicatorRadius * 2
|
||||
}
|
||||
fileprivate var inactiveLayers = [CALayer]()
|
||||
|
||||
|
||||
// MARK: - State Update
|
||||
|
||||
fileprivate func updateNumberOfPages(_ count: Int) {
|
||||
// no need to update
|
||||
guard count != inactiveLayers.count else { return }
|
||||
// reset current layout
|
||||
inactiveLayers.forEach() { $0.removeFromSuperlayer() }
|
||||
inactiveLayers = [CALayer]()
|
||||
// add layers for new page count
|
||||
inactiveLayers = stride(from: 0, to:count, by:1).map() { _ in
|
||||
let layer = CALayer()
|
||||
layer.backgroundColor = self.tintColor.cgColor
|
||||
self.layer.addSublayer(layer)
|
||||
return layer
|
||||
}
|
||||
layoutPageIndicators(inactiveLayers)
|
||||
updateActivePageIndicatorMasks(progress)
|
||||
self.invalidateIntrinsicContentSize()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
fileprivate func updateActivePageIndicatorMasks(_ progress: CGFloat) {
|
||||
// ignore if progress is outside of page indicators' bounds
|
||||
guard progress >= 0 && progress <= CGFloat(pageCount - 1) else { return }
|
||||
|
||||
// mask rect w/ default stroke width
|
||||
let insetRect = CGRect(x: 0, y: 0, width: indicatorDiameter, height: indicatorDiameter).insetBy(dx: inactiveRingWidth, dy: inactiveRingWidth)
|
||||
let leftPageFloat = trunc(progress)
|
||||
let leftPageInt = Int(progress)
|
||||
|
||||
// inset right moving page indicator
|
||||
let spaceToMove = insetRect.width / 2
|
||||
let percentPastLeftIndicator = progress - leftPageFloat
|
||||
let additionalSpaceToInsetRight = spaceToMove * percentPastLeftIndicator
|
||||
let closestRightInsetRect = insetRect.insetBy(dx: additionalSpaceToInsetRight, dy: additionalSpaceToInsetRight)
|
||||
|
||||
// inset left moving page indicator
|
||||
let additionalSpaceToInsetLeft = (1 - percentPastLeftIndicator) * spaceToMove
|
||||
let closestLeftInsetRect = insetRect.insetBy(dx: additionalSpaceToInsetLeft, dy: additionalSpaceToInsetLeft)
|
||||
|
||||
// adjust masks
|
||||
for (idx, layer) in inactiveLayers.enumerated() {
|
||||
let maskLayer = CAShapeLayer()
|
||||
maskLayer.fillRule = kCAFillRuleEvenOdd
|
||||
|
||||
let boundsPath = UIBezierPath(rect: layer.bounds)
|
||||
let circlePath: UIBezierPath
|
||||
if leftPageInt == idx {
|
||||
circlePath = UIBezierPath(ovalIn: closestLeftInsetRect)
|
||||
} else if leftPageInt + 1 == idx {
|
||||
circlePath = UIBezierPath(ovalIn: closestRightInsetRect)
|
||||
} else {
|
||||
circlePath = UIBezierPath(ovalIn: insetRect)
|
||||
}
|
||||
boundsPath.append(circlePath)
|
||||
maskLayer.path = boundsPath.cgPath
|
||||
layer.mask = maskLayer
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func layoutPageIndicators(_ layers: [CALayer]) {
|
||||
let layerDiameter = indicatorRadius * 2
|
||||
var layerFrame = CGRect(x: 0, y: 0, width: layerDiameter, height: layerDiameter)
|
||||
layers.forEach() { layer in
|
||||
layer.cornerRadius = self.indicatorRadius
|
||||
layer.frame = layerFrame
|
||||
layerFrame.origin.x += layerDiameter + indicatorPadding
|
||||
}
|
||||
}
|
||||
|
||||
override open var intrinsicContentSize: CGSize {
|
||||
return sizeThatFits(CGSize.zero)
|
||||
}
|
||||
|
||||
override open func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let layerDiameter = indicatorRadius * 2
|
||||
return CGSize(width: CGFloat(inactiveLayers.count) * layerDiameter + CGFloat(inactiveLayers.count - 1) * indicatorPadding,
|
||||
height: layerDiameter)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue