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 }