Compare commits

..

14 Commits
2.1 ... master

Author SHA1 Message Date
Даша d7c8bb83b0
Merge pull request #2 from TouchInstinct/block_minimum_reached
Added block call when minimum is reached
2019-03-11 16:14:38 +03:00
DashaGitHub 810fcbefea Some refactoring 2019-03-11 16:10:21 +03:00
DashaGitHub f893371f73 Some refactoring 2019-03-11 16:05:15 +03:00
DashaGitHub 9444b32028 Some refactoring 2019-03-11 14:42:45 +03:00
DashaGitHub dd09a1e0d9 Added block call when minimum is reached. Refactoring 2019-03-11 14:37:33 +03:00
DashaGitHub a02560f7e0 Added block call when minimum is reached 2019-03-11 11:57:02 +03:00
Ivan Zinovyev 3c81fa9ade
Merge pull request #1 from iznv/stepper_customization
Stepper customisation
2018-11-01 23:15:18 +06:00
Ivan Zinovyev 719830b81e Update version in podspec 2018-11-01 19:53:14 +03:00
Ivan Zinovyev 82453c0681 Replace buttons text with images 2018-11-01 18:44:46 +03:00
Ivan Zinovyev 7367554d53 Add leftButtonLimitOpacity, buttonsTextInsets, didTouchLabel 2018-10-31 20:19:59 +03:00
Brent Whitman 4bb23e79aa Add swift_version to podspec 2018-09-18 06:38:08 -07:00
Brent Whitman e91c2577e4 change minimum iOS version back to 8.0 2018-09-15 13:44:56 -07:00
Brent Whitman d81e614d1b Update to Swift 4.2 2018-07-13 13:31:32 -07:00
Brent Whitman 338fa1ebad Improve display to avoid rounding imprecision when using non-integer step values. Fix for issue #36. 2018-07-13 13:23:22 -07:00
7 changed files with 118 additions and 87 deletions

View File

@ -1 +1 @@
4.0 4.2

View File

@ -1,14 +1,16 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "GMStepper" s.name = "GMStepper"
s.version = "2.1" s.version = "2.2.1"
s.summary = "A stepper with a sliding label in the middle." s.summary = "A stepper with a sliding label in the middle."
s.homepage = "https://github.com/gmertk/GMStepper" s.homepage = "https://github.com/gmertk/GMStepper"
s.screenshots = "https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_1.gif" s.screenshots = "https://raw.githubusercontent.com/gmertk/GMStepper/master/Screenshots/screenshot_1.gif"
s.license = 'MIT' s.license = 'MIT'
s.author = { "Gunay Mert Karadogan" => "mertkaradogan@gmail.com" } s.authors = { "Gunay Mert Karadogan" => "mertkaradogan@gmail.com",
"Brent Whitman" => "brent@pathym.com" }
s.source = { :git => "https://github.com/gmertk/GMStepper.git", :tag => s.version.to_s } s.source = { :git => "https://github.com/gmertk/GMStepper.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/gunaymertk' s.social_media_url = 'https://twitter.com/gunaymertk'
s.platform = :ios, '8.0' s.platform = :ios, '8.0'
s.requires_arc = true s.requires_arc = true
s.source_files = 'GMStepper/*.swift' s.source_files = 'GMStepper/*.swift'
s.swift_version = '4.2'
end end

View File

@ -15,26 +15,27 @@ import UIKit
didSet { didSet {
value = min(maximumValue, max(minimumValue, value)) value = min(maximumValue, max(minimumValue, value))
let isInteger = floor(value) == value handleIsLimitReached()
// label.text = formattedValue
// If we have items, we will display them as steps
//
if isInteger && stepValue == 1.0 && items.count > 0 {
label.text = items[Int(value)]
}
else if showIntegerIfDoubleIsInteger && isInteger {
label.text = String(stringInterpolationSegment: Int(value))
} else {
label.text = String(stringInterpolationSegment: value)
}
if oldValue != value { if oldValue != value {
sendActions(for: .valueChanged) sendActions(for: .valueChanged)
} }
} }
} }
private var formattedValue: String? {
let isInteger = Decimal(value).exponent >= 0
// If we have items, we will display them as steps
if isInteger && stepValue == 1.0 && items.count > 0 {
return items[Int(value)]
}
else {
return formatter.string(from: NSNumber(value: value))
}
}
/// Minimum value. Must be less than maximumValue. Defaults to 0. /// Minimum value. Must be less than maximumValue. Defaults to 0.
@objc @IBInspectable public var minimumValue: Double = 0 { @objc @IBInspectable public var minimumValue: Double = 0 {
@ -51,37 +52,53 @@ import UIKit
} }
/// Step/Increment value as in UIStepper. Defaults to 1. /// Step/Increment value as in UIStepper. Defaults to 1.
@objc @IBInspectable public var stepValue: Double = 1 @objc @IBInspectable public var stepValue: Double = 1 {
didSet {
setupNumberFormatter()
}
}
/// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true. /// The same as UIStepper's autorepeat. If true, holding on the buttons or keeping the pan gesture alters the value repeatedly. Defaults to true.
@objc @IBInspectable public var autorepeat: Bool = true @objc @IBInspectable public var autorepeat: Bool = true
/// If the value is integer, it is shown without floating point. /// If the value is integer, it is shown without floating point.
@objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true @objc @IBInspectable public var showIntegerIfDoubleIsInteger: Bool = true {
/// Text on the left button. Be sure that it fits in the button. Defaults to "".
@objc @IBInspectable public var leftButtonText: String = "" {
didSet { didSet {
leftButton.setTitle(leftButtonText, for: .normal) setupNumberFormatter()
} }
} }
/// Text on the right button. Be sure that it fits in the button. Defaults to "+". /// Image on the left button. Defaults to nil.
@objc @IBInspectable public var rightButtonText: String = "+" { @objc @IBInspectable public var leftButtonImage: UIImage? = nil {
didSet { didSet {
rightButton.setTitle(rightButtonText, for: .normal) leftButton.setImage(leftButtonImage, for: .normal)
} }
} }
/// Text color of the buttons. Defaults to white. /// Image on the right button. Defaults to nil.
@objc @IBInspectable public var buttonsTextColor: UIColor = UIColor.white { @objc @IBInspectable public var rightButtonImage: UIImage? = nil {
didSet { didSet {
for button in [leftButton, rightButton] { rightButton.setImage(rightButtonImage, for: .normal)
button.setTitleColor(buttonsTextColor, for: .normal)
}
} }
} }
/// Left button content insets. Defaults to ".zero".
@objc @IBInspectable public var leftButtonContentInsets: UIEdgeInsets = .zero {
didSet {
leftButton.contentEdgeInsets = leftButtonContentInsets
}
}
/// Right button content insets. Defaults to ".zero".
@objc @IBInspectable public var rightButtonContentInsets: UIEdgeInsets = .zero {
didSet {
rightButton.contentEdgeInsets = rightButtonContentInsets
}
}
/// Left button limit opacity. Defaults to "0.4".
@objc @IBInspectable public var leftButtonLimitOpacity: CGFloat = 0.4
/// Background color of the buttons. Defaults to dark blue. /// Background color of the buttons. Defaults to dark blue.
@objc @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) { @objc @IBInspectable public var buttonsBackgroundColor: UIColor = UIColor(red:0.21, green:0.5, blue:0.74, alpha:1) {
didSet { didSet {
@ -92,15 +109,12 @@ import UIKit
} }
} }
/// Font of the buttons. Defaults to AvenirNext-Bold, 20.0 points in size. /// Label tap closure
@objc public var buttonsFont = UIFont(name: "AvenirNext-Bold", size: 20.0)! { @objc public var didTouchLabel: ((Double) -> Void)?
didSet {
for button in [leftButton, rightButton] { /// Block is called when the minimum is exceeded
button.titleLabel?.font = buttonsFont @objc public var minimumExceeded: (() -> Void)?
}
}
}
/// Text color of the middle label. Defaults to white. /// Text color of the middle label. Defaults to white.
@objc @IBInspectable public var labelTextColor: UIColor = UIColor.white { @objc @IBInspectable public var labelTextColor: UIColor = UIColor.white {
didSet { didSet {
@ -164,6 +178,9 @@ import UIKit
/// Color of the flashing animation on the buttons in case the value hit the limit. /// Color of the flashing animation on the buttons in case the value hit the limit.
@objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1) @objc @IBInspectable public var limitHitAnimationColor: UIColor = UIColor(red:0.26, green:0.6, blue:0.87, alpha:1)
/// Formatter for displaying the current value
let formatter = NumberFormatter()
/** /**
Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5. Width of the sliding animation. When buttons clicked, the middle label does a slide animation towards to the clicked button. Defaults to 5.
*/ */
@ -177,10 +194,8 @@ import UIKit
lazy var leftButton: UIButton = { lazy var leftButton: UIButton = {
let button = UIButton() let button = UIButton()
button.setTitle(self.leftButtonText, for: .normal) button.setImage(self.leftButtonImage, for: .normal)
button.setTitleColor(self.buttonsTextColor, for: .normal)
button.backgroundColor = self.buttonsBackgroundColor button.backgroundColor = self.buttonsBackgroundColor
button.titleLabel?.font = self.buttonsFont
button.addTarget(self, action: #selector(GMStepper.leftButtonTouchDown), for: .touchDown) button.addTarget(self, action: #selector(GMStepper.leftButtonTouchDown), for: .touchDown)
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside) button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside) button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
@ -190,10 +205,8 @@ import UIKit
lazy var rightButton: UIButton = { lazy var rightButton: UIButton = {
let button = UIButton() let button = UIButton()
button.setTitle(self.rightButtonText, for: .normal) button.setImage(self.rightButtonImage, for: .normal)
button.setTitleColor(self.buttonsTextColor, for: .normal)
button.backgroundColor = self.buttonsBackgroundColor button.backgroundColor = self.buttonsBackgroundColor
button.titleLabel?.font = self.buttonsFont
button.addTarget(self, action: #selector(GMStepper.rightButtonTouchDown), for: .touchDown) button.addTarget(self, action: #selector(GMStepper.rightButtonTouchDown), for: .touchDown)
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside) button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpInside)
button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside) button.addTarget(self, action: #selector(GMStepper.buttonTouchUp), for: .touchUpOutside)
@ -204,11 +217,7 @@ import UIKit
lazy var label: UILabel = { lazy var label: UILabel = {
let label = UILabel() let label = UILabel()
label.textAlignment = .center label.textAlignment = .center
if self.showIntegerIfDoubleIsInteger && floor(self.value) == self.value { label.text = formattedValue
label.text = String(stringInterpolationSegment: Int(self.value))
} else {
label.text = String(stringInterpolationSegment: self.value)
}
label.textColor = self.labelTextColor label.textColor = self.labelTextColor
label.backgroundColor = self.labelBackgroundColor label.backgroundColor = self.labelBackgroundColor
label.font = self.labelFont label.font = self.labelFont
@ -218,6 +227,8 @@ import UIKit
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GMStepper.handlePan)) let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GMStepper.handlePan))
panRecognizer.maximumNumberOfTouches = 1 panRecognizer.maximumNumberOfTouches = 1
label.addGestureRecognizer(panRecognizer) label.addGestureRecognizer(panRecognizer)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(GMStepper.handleLabelTap))
label.addGestureRecognizer(tapRecognizer)
return label return label
}() }()
@ -247,24 +258,7 @@ import UIKit
@objc public var items : [String] = [] { @objc public var items : [String] = [] {
didSet { didSet {
let isInteger = floor(value) == value label.text = formattedValue
//
// If we have items, we will display them as steps
//
if isInteger && stepValue == 1.0 && items.count > 0 {
var value = Int(self.value)
if value >= items.count {
value = items.count - 1
self.value = Double(value)
}
else {
label.text = items[value]
}
}
} }
} }
@ -301,17 +295,28 @@ import UIKit
setup() setup()
} }
func setup() { fileprivate func setup() {
addSubview(leftButton) addSubview(leftButton)
addSubview(rightButton) addSubview(rightButton)
addSubview(label) addSubview(label)
handleIsLimitReached()
backgroundColor = buttonsBackgroundColor backgroundColor = buttonsBackgroundColor
layer.cornerRadius = cornerRadius layer.cornerRadius = cornerRadius
clipsToBounds = true clipsToBounds = true
labelOriginalCenter = label.center labelOriginalCenter = label.center
NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: NSNotification.Name.UIApplicationWillResignActive, object: nil) setupNumberFormatter()
NotificationCenter.default.addObserver(self, selector: #selector(GMStepper.reset), name: UIApplication.willResignActiveNotification, object: nil)
}
func setupNumberFormatter() {
let decValue = Decimal(stepValue)
let digits = decValue.significantFractionalDecimalDigits
formatter.minimumIntegerDigits = 1
formatter.minimumFractionDigits = showIntegerIfDoubleIsInteger ? 0 : digits
formatter.maximumFractionDigits = digits
} }
public override func layoutSubviews() { public override func layoutSubviews() {
@ -332,8 +337,9 @@ import UIKit
value += stepValue value += stepValue
} else if stepperState == .ShouldDecrease { } else if stepperState == .ShouldDecrease {
value -= stepValue value -= stepValue
} }
} }
deinit { deinit {
resetTimer() resetTimer()
@ -406,6 +412,10 @@ extension GMStepper {
} }
} }
@objc func handleLabelTap() {
didTouchLabel?(value)
}
@objc func reset() { @objc func reset() {
panState = .Stable panState = .Stable
stepperState = .Stable stepperState = .Stable
@ -432,11 +442,11 @@ extension GMStepper {
if value == minimumValue { if value == minimumValue {
animateLimitHitIfNeeded() animateLimitHitIfNeeded()
minimumExceeded?()
} else { } else {
stepperState = .ShouldDecrease stepperState = .ShouldDecrease
animateSlideLeft() animateSlideLeft()
} }
} }
@objc func rightButtonTouchDown(button: UIButton) { @objc func rightButtonTouchDown(button: UIButton) {
@ -516,6 +526,19 @@ extension GMStepper {
timerFireCount = 0 timerFireCount = 0
} }
} }
}
private extension GMStepper {
func handleIsLimitReached() {
let isLimitReached = value == minimumValue
leftButton.alpha = isLimitReached ? leftButtonLimitOpacity : 1
}
}
extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent, 0)
}
} }

View File

@ -168,14 +168,14 @@
TargetAttributes = { TargetAttributes = {
3116C0D71DA4DD360015AC69 = { 3116C0D71DA4DD360015AC69 = {
CreatedOnToolsVersion = 8.0; CreatedOnToolsVersion = 8.0;
LastSwiftMigration = 0900; LastSwiftMigration = 1000;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
TestTargetID = 319C19311B4843EB005EFEE5; TestTargetID = 319C19311B4843EB005EFEE5;
}; };
319C19311B4843EB005EFEE5 = { 319C19311B4843EB005EFEE5 = {
CreatedOnToolsVersion = 6.4; CreatedOnToolsVersion = 6.4;
DevelopmentTeam = 289M6XEDV4; DevelopmentTeam = 289M6XEDV4;
LastSwiftMigration = 0900; LastSwiftMigration = 1000;
}; };
}; };
}; };
@ -282,8 +282,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests; PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
}; };
name = Debug; name = Debug;
@ -300,8 +299,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests; PRODUCT_BUNDLE_IDENTIFIER = com.gmertk.GMStepperExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GMStepperExample.app/GMStepperExample";
}; };
name = Release; name = Release;
@ -411,11 +409,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 289M6XEDV4; DEVELOPMENT_TEAM = 289M6XEDV4;
INFOPLIST_FILE = GMStepperExample/Info.plist; INFOPLIST_FILE = GMStepperExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@ -425,11 +423,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 289M6XEDV4; DEVELOPMENT_TEAM = 289M6XEDV4;
INFOPLIST_FILE = GMStepperExample/Info.plist; INFOPLIST_FILE = GMStepperExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "com.gunaymert.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // Override point for customization after application launch.
return true return true
} }

View File

@ -56,7 +56,7 @@
<color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="value" red="0.90823972230000005" green="0.92638683320000004" blue="0.93171715740000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</userDefinedRuntimeAttribute> </userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="stepValue"> <userDefinedRuntimeAttribute type="number" keyPath="stepValue">
<real key="value" value="0.5"/> <real key="value" value="0.1"/>
</userDefinedRuntimeAttribute> </userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</view> </view>