diff --git a/SnappingSlider.xcodeproj/project.pbxproj b/SnappingSlider.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3678a53 --- /dev/null +++ b/SnappingSlider.xcodeproj/project.pbxproj @@ -0,0 +1,325 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + B05B95581FEC9A1200214B83 /* SnappingSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = B05B95561FEC9A1100214B83 /* SnappingSlider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B05B955F1FEC9A6A00214B83 /* SnappingSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05B955E1FEC9A6A00214B83 /* SnappingSlider.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B05B95531FEC9A1100214B83 /* SnappingSlider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnappingSlider.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B05B95561FEC9A1100214B83 /* SnappingSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SnappingSlider.h; sourceTree = ""; }; + B05B95571FEC9A1200214B83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B05B955E1FEC9A6A00214B83 /* SnappingSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnappingSlider.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B05B954F1FEC9A1100214B83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B05B95491FEC9A1100214B83 = { + isa = PBXGroup; + children = ( + B05B95551FEC9A1100214B83 /* SnappingSlider */, + B05B95541FEC9A1100214B83 /* Products */, + ); + sourceTree = ""; + }; + B05B95541FEC9A1100214B83 /* Products */ = { + isa = PBXGroup; + children = ( + B05B95531FEC9A1100214B83 /* SnappingSlider.framework */, + ); + name = Products; + sourceTree = ""; + }; + B05B95551FEC9A1100214B83 /* SnappingSlider */ = { + isa = PBXGroup; + children = ( + B05B95561FEC9A1100214B83 /* SnappingSlider.h */, + B05B955E1FEC9A6A00214B83 /* SnappingSlider.swift */, + B05B95571FEC9A1200214B83 /* Info.plist */, + ); + path = SnappingSlider; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B05B95501FEC9A1100214B83 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B05B95581FEC9A1200214B83 /* SnappingSlider.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B05B95521FEC9A1100214B83 /* SnappingSlider */ = { + isa = PBXNativeTarget; + buildConfigurationList = B05B955B1FEC9A1200214B83 /* Build configuration list for PBXNativeTarget "SnappingSlider" */; + buildPhases = ( + B05B954E1FEC9A1100214B83 /* Sources */, + B05B954F1FEC9A1100214B83 /* Frameworks */, + B05B95501FEC9A1100214B83 /* Headers */, + B05B95511FEC9A1100214B83 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SnappingSlider; + productName = SnappingSlider; + productReference = B05B95531FEC9A1100214B83 /* SnappingSlider.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B05B954A1FEC9A1100214B83 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Valentin Radu"; + TargetAttributes = { + B05B95521FEC9A1100214B83 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = B05B954D1FEC9A1100214B83 /* Build configuration list for PBXProject "SnappingSlider" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = B05B95491FEC9A1100214B83; + productRefGroup = B05B95541FEC9A1100214B83 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B05B95521FEC9A1100214B83 /* SnappingSlider */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B05B95511FEC9A1100214B83 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B05B954E1FEC9A1100214B83 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B05B955F1FEC9A6A00214B83 /* SnappingSlider.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B05B95591FEC9A1200214B83 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + B05B955A1FEC9A1200214B83 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + B05B955C1FEC9A1200214B83 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SnappingSlider/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.valentinradu.SnappingSlider; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B05B955D1FEC9A1200214B83 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SnappingSlider/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.valentinradu.SnappingSlider; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B05B954D1FEC9A1100214B83 /* Build configuration list for PBXProject "SnappingSlider" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B05B95591FEC9A1200214B83 /* Debug */, + B05B955A1FEC9A1200214B83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B05B955B1FEC9A1200214B83 /* Build configuration list for PBXNativeTarget "SnappingSlider" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B05B955C1FEC9A1200214B83 /* Debug */, + B05B955D1FEC9A1200214B83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B05B954A1FEC9A1100214B83 /* Project object */; +} diff --git a/SnappingSlider.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SnappingSlider.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b8ed4b3 --- /dev/null +++ b/SnappingSlider.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SnappingSlider.xcodeproj/xcshareddata/xcschemes/SnappingSlider.xcscheme b/SnappingSlider.xcodeproj/xcshareddata/xcschemes/SnappingSlider.xcscheme new file mode 100644 index 0000000..7839f18 --- /dev/null +++ b/SnappingSlider.xcodeproj/xcshareddata/xcschemes/SnappingSlider.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SnappingSlider/Info.plist b/SnappingSlider/Info.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/SnappingSlider/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/SnappingSlider/SnappingSlider.h b/SnappingSlider/SnappingSlider.h new file mode 100644 index 0000000..945f4e9 --- /dev/null +++ b/SnappingSlider/SnappingSlider.h @@ -0,0 +1,19 @@ +// +// SnappingSlider.h +// SnappingSlider +// +// Created by Valentin Radu on 22/12/2017. +// Copyright © 2017 Valentin Radu. All rights reserved. +// + +#import + +//! Project version number for SnappingSlider. +FOUNDATION_EXPORT double SnappingSliderVersionNumber; + +//! Project version string for SnappingSlider. +FOUNDATION_EXPORT const unsigned char SnappingSliderVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/SnappingSlider/SnappingSlider.swift b/SnappingSlider/SnappingSlider.swift index d058f6c..1ed2bea 100644 --- a/SnappingSlider/SnappingSlider.swift +++ b/SnappingSlider/SnappingSlider.swift @@ -22,27 +22,35 @@ import UIKit public protocol SnappingSliderDelegate: class { - func snappingSliderDidIncrementValue(_ slider:SnappingSlider) func snappingSliderDidDecrementValue(_ slider:SnappingSlider) } open class SnappingSlider: UIView { - final public weak var delegate:SnappingSliderDelegate? - final public var shouldContinueAlteringValueUntilGestureCancels:Bool = false - final public var incrementAndDecrementLabelFont:UIFont = UIFont(name: "TrebuchetMS-Bold", size: 18.0)! { didSet { setNeedsLayout() } } - final public var incrementAndDecrementLabelTextColor:UIColor = UIColor.white { didSet { setNeedsLayout() } } - final public var incrementAndDecrementBackgroundColor:UIColor = UIColor(red:0.36, green:0.65, blue:0.65, alpha:1) { didSet { setNeedsLayout() } } - final public var sliderColor:UIColor = UIColor(red:0.42, green:0.76, blue:0.74, alpha:1) { didSet { setNeedsLayout() } } - final public var sliderTitleFont:UIFont = UIFont(name: "TrebuchetMS-Bold", size: 15.0)! { didSet { setNeedsLayout() } } - final public var sliderTitleColor:UIColor = UIColor.white { didSet { setNeedsLayout() } } - final public var sliderTitleText:String = "Slide Me" { didSet { setNeedsLayout() } } - final public var sliderCornerRadius:CGFloat = 3.0 { didSet { setNeedsLayout() } } + final public weak var delegate:SnappingSliderDelegate? { + didSet { + plusButton.addTarget(self, action: #selector(inc), for: .touchUpInside) + minusButton.addTarget(self, action: #selector(dec), for: .touchUpInside) + } + } + @objc dynamic public var shouldContinueAlteringValueUntilGestureCancels:Bool = true + @objc dynamic public var incrementAndDecrementLabelFont:UIFont = UIFont(name: "TrebuchetMS-Bold", size: 18.0)! { didSet { setNeedsLayout() } } + @objc dynamic public var incrementAndDecrementLabelTextColor:UIColor = UIColor.white { didSet { setNeedsLayout() } } + @objc dynamic public var incrementAndDecrementBackgroundColor:UIColor = UIColor(red:0.36, green:0.65, blue:0.65, alpha:1) { didSet { setNeedsLayout() } } + @objc dynamic public var sliderColor:UIColor = UIColor(red:0.42, green:0.76, blue:0.74, alpha:1) { didSet { setNeedsLayout() } } + @objc dynamic public var sliderWidthRatio:CGFloat = 0.5 { didSet { setNeedsLayout() } } + @objc dynamic public var sliderTitleFont:UIFont = UIFont(name: "TrebuchetMS-Bold", size: 15.0)! { didSet { setNeedsLayout() } } + @objc dynamic public var sliderTitleColor:UIColor = UIColor.white { didSet { setNeedsLayout() } } + @objc dynamic public var sliderTitleColorAtop:UIColor = UIColor(red:0.36, green:0.65, blue:0.65, alpha:1) + @objc dynamic public var sliderTitleText:String = "Slide Me" { didSet { updateText() } } + @objc dynamic public var sliderTitleAttributedText:NSAttributedString? { didSet { updateText() } } + @objc dynamic public var sliderCornerRadius:CGFloat = 3.0 { didSet { setNeedsLayout() } } + @objc dynamic public var shouldKeepTitleAtop:Bool = true final fileprivate let sliderContainer = UIView(frame: CGRect.zero) - final fileprivate let minusLabel = UILabel(frame: CGRect.zero) - final fileprivate let plusLabel = UILabel(frame: CGRect.zero) + final fileprivate let minusButton = UIButton(frame: CGRect.zero) + final fileprivate let plusButton = UIButton(frame: CGRect.zero) final fileprivate let sliderView = UIView(frame: CGRect.zero) final fileprivate let sliderViewLabel = UILabel(frame: CGRect.zero) @@ -53,7 +61,8 @@ open class SnappingSlider: UIView { final fileprivate let sliderPanGestureRecogniser = UIPanGestureRecognizer() final fileprivate let dynamicButtonAnimator = UIDynamicAnimator() - final fileprivate var snappingBehavior:SliderSnappingBehavior? + final fileprivate var snappingSliderBehavior:SliderSnappingBehavior? + final fileprivate var snappingLabelBehavior:SliderSnappingBehavior? public init(frame:CGRect, title:String) { @@ -75,36 +84,43 @@ open class SnappingSlider: UIView { sliderContainer.backgroundColor = backgroundColor - minusLabel.text = "-" - minusLabel.textAlignment = NSTextAlignment.center - sliderContainer.addSubview(minusLabel) + minusButton.setTitle("-", for: .normal) + minusButton.titleLabel?.textAlignment = NSTextAlignment.center + sliderContainer.addSubview(minusButton) - plusLabel.text = "+" - plusLabel.textAlignment = NSTextAlignment.center - sliderContainer.addSubview(plusLabel) + plusButton.setTitle("+", for: .normal) + plusButton.titleLabel?.textAlignment = NSTextAlignment.center + sliderContainer.addSubview(plusButton) sliderContainer.addSubview(sliderView) sliderViewLabel.isUserInteractionEnabled = false sliderViewLabel.textAlignment = NSTextAlignment.center sliderViewLabel.textColor = sliderTitleColor - sliderView.addSubview(sliderViewLabel) sliderPanGestureRecogniser.addTarget(self, action: #selector(type(of: self).handleGesture(_:))) sliderView.addGestureRecognizer(sliderPanGestureRecogniser) sliderContainer.center = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5) + sliderContainer.clipsToBounds = true addSubview(sliderContainer) - clipsToBounds = true + + if shouldKeepTitleAtop { + addSubview(sliderViewLabel) + } + else { + sliderView.addSubview(sliderViewLabel) + } } override open func layoutSubviews() { super.layoutSubviews() - if snappingBehavior?.snappingPoint.x != center.x { - - snappingBehavior = SliderSnappingBehavior(item: sliderView, snapToPoint: CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5)) + if snappingSliderBehavior?.snappingPoint.x != center.x { + snappingSliderBehavior = SliderSnappingBehavior(item: sliderView, + snapToPoint: CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5), + damping:0.25) lastDelegateFireOffset = sliderView.center.x } @@ -112,36 +128,46 @@ open class SnappingSlider: UIView { sliderContainer.center = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5) sliderContainer.backgroundColor = incrementAndDecrementBackgroundColor - minusLabel.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * 0.25, height: bounds.size.height) - minusLabel.center = CGPoint(x: minusLabel.bounds.size.width * 0.5, y: bounds.size.height * 0.5) - minusLabel.backgroundColor = incrementAndDecrementBackgroundColor - minusLabel.font = incrementAndDecrementLabelFont - minusLabel.textColor = incrementAndDecrementLabelTextColor + minusButton.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * sliderWidthRatio * 0.5, height: bounds.size.height) + minusButton.center = CGPoint(x: minusButton.bounds.size.width * 0.5, y: bounds.size.height * 0.5) + minusButton.backgroundColor = incrementAndDecrementBackgroundColor + minusButton.titleLabel?.font = incrementAndDecrementLabelFont + minusButton.titleLabel?.textColor = incrementAndDecrementLabelTextColor - plusLabel.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * 0.25, height: bounds.size.height) - plusLabel.center = CGPoint(x: bounds.size.width - plusLabel.bounds.size.width * 0.5, y: bounds.size.height * 0.5) - plusLabel.backgroundColor = incrementAndDecrementBackgroundColor - plusLabel.font = incrementAndDecrementLabelFont - plusLabel.textColor = incrementAndDecrementLabelTextColor + plusButton.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * sliderWidthRatio * 0.5, height: bounds.size.height) + plusButton.center = CGPoint(x: bounds.size.width - plusButton.bounds.size.width * 0.5, y: bounds.size.height * 0.5) + plusButton.backgroundColor = incrementAndDecrementBackgroundColor + plusButton.titleLabel?.font = incrementAndDecrementLabelFont + plusButton.titleLabel?.textColor = incrementAndDecrementLabelTextColor - sliderView.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * 0.5, height: bounds.size.height) + sliderView.frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width * sliderWidthRatio, height: bounds.size.height) sliderView.center = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5) sliderView.backgroundColor = sliderColor - sliderViewLabel.frame = CGRect(x: 0.0, y: 0.0, width: sliderView.bounds.size.width, height: sliderView.bounds.size.height) - sliderViewLabel.center = CGPoint(x: sliderViewLabel.bounds.size.width * 0.5, y: sliderViewLabel.bounds.size.height * 0.5) - sliderViewLabel.backgroundColor = sliderColor + sliderViewLabel.frame.size = sliderView.frame.size + sliderViewLabel.center = sliderViewLabel.superview!.convert(sliderView.center, from: sliderView.superview) sliderViewLabel.font = sliderTitleFont - sliderViewLabel.text = sliderTitleText - layer.cornerRadius = sliderCornerRadius + sliderContainer.layer.cornerRadius = sliderCornerRadius + sliderView.layer.cornerRadius = sliderCornerRadius + + if snappingLabelBehavior == nil && shouldKeepTitleAtop { + let point = CGPoint(x: bounds.size.width * 0.5, + y: bounds.size.height * 0.5) + snappingLabelBehavior = SliderSnappingBehavior(item: sliderViewLabel, + snapToPoint: point, + damping:0.8) + dynamicButtonAnimator.addBehavior(snappingLabelBehavior!) + } + + updateText() } // MARK: Gesture & Timer Handling - final func handleGesture(_ sender: UIGestureRecognizer) { + @objc final func handleGesture(_ sender: UIGestureRecognizer) { - guard let snapBehavior = snappingBehavior else { return } + guard let snapSliderBehavior = snappingSliderBehavior else { return } if sender as NSObject == sliderPanGestureRecogniser { @@ -151,9 +177,23 @@ open class SnappingSlider: UIView { isCurrentDraggingSlider = true touchesBeganPoint = sliderPanGestureRecogniser.translation(in: sliderView) - dynamicButtonAnimator.removeBehavior(snapBehavior) + dynamicButtonAnimator.removeBehavior(snapSliderBehavior) lastDelegateFireOffset = (bounds.size.width * 0.5) + ((touchesBeganPoint.x + touchesBeganPoint.x) * 0.40) + if shouldKeepTitleAtop { + UIView.transition(with: self.sliderViewLabel, + duration: 0.3, + options: .transitionCrossDissolve, + animations: { + self.sliderViewLabel.textColor = self.sliderTitleColorAtop + }, completion: nil) + if let s = snappingLabelBehavior { + let point = CGPoint(x: bounds.size.width * 0.5, + y: -bounds.size.height * 0.5) + s.snappingPoint = point + } + } + case .changed: valueChangingTimer?.invalidate() @@ -180,8 +220,21 @@ open class SnappingSlider: UIView { } if shouldContinueAlteringValueUntilGestureCancels { - - valueChangingTimer = Timer.scheduledTimer(timeInterval: 0.7, target: self, selector: NSSelectorFromString("handleTimer:"), userInfo: nil, repeats: true) + let ratio = abs(sliderView.center.x - bounds.width / 2) / ((bounds.width - sliderView.bounds.width) / 2) + let time = 0.05 + 0.5 * (1.0 - ratio) + valueChangingTimer = Timer.scheduledTimer(timeInterval: Double(time), + target: self, + selector: #selector(handleTimer), + userInfo: nil, + repeats: true) + } + + if shouldKeepTitleAtop { + if let s = snappingLabelBehavior { + let point = CGPoint(x: translatedCenterX, + y: -bounds.size.height * 0.5) + s.snappingPoint = point + } } case .ended: @@ -194,11 +247,25 @@ open class SnappingSlider: UIView { case .cancelled: - dynamicButtonAnimator.addBehavior(snapBehavior) + dynamicButtonAnimator.addBehavior(snapSliderBehavior) isCurrentDraggingSlider = false lastDelegateFireOffset = center.x valueChangingTimer?.invalidate() + if shouldKeepTitleAtop { + UIView.transition(with: self.sliderViewLabel, + duration: 0.3, + options: .transitionCrossDissolve, + animations: { + self.sliderViewLabel.textColor = self.sliderTitleColor + }, completion: nil) + if let s = snappingLabelBehavior { + let point = CGPoint(x: bounds.size.width * 0.5, + y: bounds.size.height * 0.5) + s.snappingPoint = point + } + } + case .possible: // Swift requires at least one statement per case @@ -207,31 +274,49 @@ open class SnappingSlider: UIView { } } - final func handleTimer(_ sender: Timer) { - + @objc final func handleTimer(_ sender: Timer) { if sliderView.frame.midX > self.bounds.midX { - delegate?.snappingSliderDidIncrementValue(self) } else { - delegate?.snappingSliderDidDecrementValue(self) } } + //MARK: - Text update + private func updateText() { + if let attributedText = sliderTitleAttributedText { + sliderViewLabel.attributedText = attributedText + } + else { + sliderViewLabel.text = sliderTitleText + } + } + + //MARK: - Inc and dec forwarders + @objc private func inc() { + delegate?.snappingSliderDidIncrementValue(self) + } + @objc private func dec() { + delegate?.snappingSliderDidDecrementValue(self) + } } final class SliderSnappingBehavior: UIDynamicBehavior { - - let snappingPoint:CGPoint - - init(item: UIDynamicItem, snapToPoint point: CGPoint) { + var snappingPoint:CGPoint { + didSet { + snapBehavior.snapPoint = snappingPoint + } + } + let dynamicItemBehavior:UIDynamicItemBehavior + let snapBehavior:UISnapBehavior + init(item: UIDynamicItem, snapToPoint point: CGPoint, damping: CGFloat) { - let dynamicItemBehavior:UIDynamicItemBehavior = UIDynamicItemBehavior(items: [item]) + dynamicItemBehavior = UIDynamicItemBehavior(items: [item]) dynamicItemBehavior.allowsRotation = false - let snapBehavior:UISnapBehavior = UISnapBehavior(item: item, snapTo: point) - snapBehavior.damping = 0.25 + snapBehavior = UISnapBehavior(item: item, snapTo: point) + snapBehavior.damping = damping snappingPoint = point @@ -240,5 +325,4 @@ final class SliderSnappingBehavior: UIDynamicBehavior { addChildBehavior(dynamicItemBehavior) addChildBehavior(snapBehavior) } - } diff --git a/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.pbxproj b/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0436754 --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.pbxproj @@ -0,0 +1,397 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + B03312B61FF3674300C715DB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03312B51FF3674300C715DB /* AppDelegate.swift */; }; + B03312B81FF3674300C715DB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03312B71FF3674300C715DB /* ViewController.swift */; }; + B03312BB1FF3674300C715DB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B03312B91FF3674300C715DB /* Main.storyboard */; }; + B03312BD1FF3674300C715DB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B03312BC1FF3674300C715DB /* Assets.xcassets */; }; + B03312C01FF3674300C715DB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B03312BE1FF3674300C715DB /* LaunchScreen.storyboard */; }; + B03312CF1FF3680200C715DB /* SnappingSlider.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B03312CC1FF3677100C715DB /* SnappingSlider.framework */; }; + B03312D01FF3680200C715DB /* SnappingSlider.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B03312CC1FF3677100C715DB /* SnappingSlider.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B03312CB1FF3677100C715DB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B03312C71FF3677100C715DB /* SnappingSlider.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B05B95531FEC9A1100214B83; + remoteInfo = SnappingSlider; + }; + B03312D11FF3680200C715DB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B03312C71FF3677100C715DB /* SnappingSlider.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B05B95521FEC9A1100214B83; + remoteInfo = SnappingSlider; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + B03312D31FF3680200C715DB /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + B03312D01FF3680200C715DB /* SnappingSlider.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + B03312B21FF3674200C715DB /* SnappingSliderDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SnappingSliderDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B03312B51FF3674300C715DB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + B03312B71FF3674300C715DB /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + B03312BA1FF3674300C715DB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B03312BC1FF3674300C715DB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B03312BF1FF3674300C715DB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + B03312C11FF3674300C715DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B03312C71FF3677100C715DB /* SnappingSlider.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SnappingSlider.xcodeproj; path = ../SnappingSlider.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B03312AF1FF3674200C715DB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B03312CF1FF3680200C715DB /* SnappingSlider.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B03312A91FF3674200C715DB = { + isa = PBXGroup; + children = ( + B03312C71FF3677100C715DB /* SnappingSlider.xcodeproj */, + B03312B41FF3674200C715DB /* SnappingSliderDemo */, + B03312B31FF3674200C715DB /* Products */, + ); + sourceTree = ""; + }; + B03312B31FF3674200C715DB /* Products */ = { + isa = PBXGroup; + children = ( + B03312B21FF3674200C715DB /* SnappingSliderDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + B03312B41FF3674200C715DB /* SnappingSliderDemo */ = { + isa = PBXGroup; + children = ( + B03312B51FF3674300C715DB /* AppDelegate.swift */, + B03312B71FF3674300C715DB /* ViewController.swift */, + B03312B91FF3674300C715DB /* Main.storyboard */, + B03312BC1FF3674300C715DB /* Assets.xcassets */, + B03312BE1FF3674300C715DB /* LaunchScreen.storyboard */, + B03312C11FF3674300C715DB /* Info.plist */, + ); + path = SnappingSliderDemo; + sourceTree = ""; + }; + B03312C81FF3677100C715DB /* Products */ = { + isa = PBXGroup; + children = ( + B03312CC1FF3677100C715DB /* SnappingSlider.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B03312B11FF3674200C715DB /* SnappingSliderDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = B03312C41FF3674300C715DB /* Build configuration list for PBXNativeTarget "SnappingSliderDemo" */; + buildPhases = ( + B03312AE1FF3674200C715DB /* Sources */, + B03312AF1FF3674200C715DB /* Frameworks */, + B03312B01FF3674200C715DB /* Resources */, + B03312D31FF3680200C715DB /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + B03312D21FF3680200C715DB /* PBXTargetDependency */, + ); + name = SnappingSliderDemo; + productName = SnappingSliderDemo; + productReference = B03312B21FF3674200C715DB /* SnappingSliderDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B03312AA1FF3674200C715DB /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Valentin Radu"; + TargetAttributes = { + B03312B11FF3674200C715DB = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = B03312AD1FF3674200C715DB /* Build configuration list for PBXProject "SnappingSliderDemo" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B03312A91FF3674200C715DB; + productRefGroup = B03312B31FF3674200C715DB /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = B03312C81FF3677100C715DB /* Products */; + ProjectRef = B03312C71FF3677100C715DB /* SnappingSlider.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + B03312B11FF3674200C715DB /* SnappingSliderDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + B03312CC1FF3677100C715DB /* SnappingSlider.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = SnappingSlider.framework; + remoteRef = B03312CB1FF3677100C715DB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + B03312B01FF3674200C715DB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B03312C01FF3674300C715DB /* LaunchScreen.storyboard in Resources */, + B03312BD1FF3674300C715DB /* Assets.xcassets in Resources */, + B03312BB1FF3674300C715DB /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B03312AE1FF3674200C715DB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B03312B81FF3674300C715DB /* ViewController.swift in Sources */, + B03312B61FF3674300C715DB /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B03312D21FF3680200C715DB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SnappingSlider; + targetProxy = B03312D11FF3680200C715DB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + B03312B91FF3674300C715DB /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B03312BA1FF3674300C715DB /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + B03312BE1FF3674300C715DB /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B03312BF1FF3674300C715DB /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B03312C21FF3674300C715DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + B03312C31FF3674300C715DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_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_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_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B03312C51FF3674300C715DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SnappingSliderDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.valentinradu.SnappingSliderDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B03312C61FF3674300C715DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SnappingSliderDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.valentinradu.SnappingSliderDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B03312AD1FF3674200C715DB /* Build configuration list for PBXProject "SnappingSliderDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B03312C21FF3674300C715DB /* Debug */, + B03312C31FF3674300C715DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B03312C41FF3674300C715DB /* Build configuration list for PBXNativeTarget "SnappingSliderDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B03312C51FF3674300C715DB /* Debug */, + B03312C61FF3674300C715DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B03312AA1FF3674200C715DB /* Project object */; +} diff --git a/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..38d6073 --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SnappingSliderDemo/SnappingSliderDemo/AppDelegate.swift b/SnappingSliderDemo/SnappingSliderDemo/AppDelegate.swift new file mode 100644 index 0000000..d215a62 --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// SnappingSliderDemo +// +// Created by Valentin Radu on 27/12/2017. +// Copyright © 2017 Valentin Radu. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/SnappingSliderDemo/SnappingSliderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/SnappingSliderDemo/SnappingSliderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..1d060ed --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/LaunchScreen.storyboard b/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f83f6fd --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/Main.storyboard b/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..1babc6f --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/Base.lproj/Main.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SnappingSliderDemo/SnappingSliderDemo/Info.plist b/SnappingSliderDemo/SnappingSliderDemo/Info.plist new file mode 100644 index 0000000..16be3b6 --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/SnappingSliderDemo/SnappingSliderDemo/ViewController.swift b/SnappingSliderDemo/SnappingSliderDemo/ViewController.swift new file mode 100644 index 0000000..5ad98c6 --- /dev/null +++ b/SnappingSliderDemo/SnappingSliderDemo/ViewController.swift @@ -0,0 +1,38 @@ +// +// ViewController.swift +// SnappingSliderDemo +// +// Created by Valentin Radu on 27/12/2017. +// Copyright © 2017 Valentin Radu. All rights reserved. +// + +import UIKit +import SnappingSlider + +class ViewController: UIViewController, SnappingSliderDelegate { + + @IBOutlet var snappingSlider: SnappingSlider! + var value = 0 + + override func viewDidLoad() { + super.viewDidLoad() + snappingSlider.delegate = self + snappingSlider.sliderTitleText = String(value) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func snappingSliderDidIncrementValue(_ slider:SnappingSlider) { + value += 1 + snappingSlider.sliderTitleText = String(value) + } + + func snappingSliderDidDecrementValue(_ slider:SnappingSlider) { + value -= 1 + snappingSlider.sliderTitleText = String(value) + } +} +