diff --git a/TwicketSegmentedControl/Palette.swift b/TwicketSegmentedControl/Palette.swift
index 5f8845e..cf56fee 100644
--- a/TwicketSegmentedControl/Palette.swift
+++ b/TwicketSegmentedControl/Palette.swift
@@ -13,12 +13,12 @@ struct Palette {
static let highlightTextColor = UIColor.white
static let segmentedControlBackgroundColor = Palette.colorFromRGB(237, green: 242, blue: 247, alpha: 0.7)
static let sliderColor = Palette.colorFromRGB(44, green: 131, blue: 255)
-
+
static func colorFromRGB(_ red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat = 1.0) -> UIColor {
func amount(_ amount: CGFloat, with alpha: CGFloat) -> CGFloat {
return (1 - alpha) * 255 + alpha * amount
}
-
+
let red = amount(red, with: alpha)/255
let green = amount(green, with: alpha)/255
let blue = amount(blue, with: alpha)/255
diff --git a/TwicketSegmentedControl/TwicketSegmentedControl.swift b/TwicketSegmentedControl/TwicketSegmentedControl.swift
index 030f3b2..613ae7d 100644
--- a/TwicketSegmentedControl/TwicketSegmentedControl.swift
+++ b/TwicketSegmentedControl/TwicketSegmentedControl.swift
@@ -13,153 +13,153 @@ public protocol TwicketSegmentedControlDelegate: class {
}
open class TwicketSegmentedControl: UIControl {
- open static let height: CGFloat = Constants.height + Constants.topBottomMargin * 2
-
+ public 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()
-
+
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) {
+
+ open var font: UIFont = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium) {
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
}
-
+
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 = CGRect(x: Constants.leadingTrailingMargin,
y: Constants.topBottomMargin,
@@ -169,27 +169,27 @@ open class TwicketSegmentedControl: UIControl {
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
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()
@@ -199,14 +199,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)
@@ -217,33 +217,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:
@@ -254,11 +254,13 @@ open class TwicketSegmentedControl: UIControl {
let location = panGesture.location(in: self)
sliderView.center.x = location.x - correction
case .possible: ()
+ @unknown default:
+ break
}
}
-
+
// MARK: Slider position
-
+
private func moveToNearestPoint(basedOn gesture: UIGestureRecognizer, velocity: CGPoint? = nil) {
var location = gesture.location(in: self)
if let velocity = velocity {
@@ -269,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
diff --git a/TwicketSegmentedControl/UIViewShadowExtension.swift b/TwicketSegmentedControl/UIViewShadowExtension.swift
index caf1e66..a958ea1 100644
--- a/TwicketSegmentedControl/UIViewShadowExtension.swift
+++ b/TwicketSegmentedControl/UIViewShadowExtension.swift
@@ -15,7 +15,7 @@ extension UIView {
layer.shadowOpacity = 0.7
layer.shadowOffset = CGSize(width: 0, height: 5)
}
-
+
func removeShadow() {
layer.shadowOpacity = 0
}
diff --git a/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.pbxproj b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.pbxproj
index 73b188e..b441b89 100644
--- a/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.pbxproj
+++ b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.pbxproj
@@ -181,7 +181,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0800;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 1130;
ORGANIZATIONNAME = "Pol Quintana";
TargetAttributes = {
C18581821D8D7220001A9A00 = {
@@ -199,7 +199,7 @@
};
buildConfigurationList = C185817E1D8D7220001A9A00 /* Build configuration list for PBXProject "TwicketSegmentedControlDemo" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -290,20 +290,30 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -332,6 +342,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -340,20 +351,30 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -374,6 +395,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -389,7 +411,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "Pol-Quintana.TwicketSegmentedControlDemo";
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -403,7 +425,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "Pol-Quintana.TwicketSegmentedControlDemo";
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -426,7 +448,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@@ -450,7 +472,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "Pol-Quintana.TwicketSegmentedControl";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
diff --git a/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/xcshareddata/xcschemes/TwicketSegmentedControl.xcscheme b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/xcshareddata/xcschemes/TwicketSegmentedControl.xcscheme
index db97b44..51c53c1 100644
--- a/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/xcshareddata/xcschemes/TwicketSegmentedControl.xcscheme
+++ b/TwicketSegmentedControlDemo/TwicketSegmentedControlDemo.xcodeproj/xcshareddata/xcschemes/TwicketSegmentedControl.xcscheme
@@ -1,6 +1,6 @@
-
-
-
-
Bool {
+ internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}