TGPControls, custom awesome iOS controls

This commit is contained in:
Xavier Schott 2015-01-25 17:54:02 -08:00
commit bb74b3820d
13 changed files with 1646 additions and 0 deletions

126
TGPControls.podspec Normal file
View File

@ -0,0 +1,126 @@
#
# Be sure to run `pod spec lint TGPControls.podspec' to ensure this is a
# valid spec and to remove all comments including this before submitting the spec.
#
# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |s|
# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# These will help people to find your library, and whilst it
# can feel like a chore to fill in it's definitely to your advantage. The
# summary should be tweet-length, and the description more in depth.
#
s.name = "TGPControls"
s.version = "0.0.1"
s.summary = "Custom Awesome iOS Controls."
s.description = <<-DESC
Provide an iOS looking UISlider with discrete, controlable steps
Provide dynamic, animated labels for discrete slider
DESC
s.homepage = "https://github.com/arquebuse/TGPControls"
# s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
# ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Licensing your code is important. See http://choosealicense.com for more info.
# CocoaPods will detect a license file if there is a named LICENSE*
# Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
#
# s.license = "MIT (example)"
s.license = { :type => "MIT", :file => "TGPControls_License.txt" }
# ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the authors of the library, with email addresses. Email addresses
# of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
# accepts just a name if you'd rather not provide an email address.
#
# Specify a social_media_url where others can refer to, for example a twitter
# profile URL.
#
s.author = { "Arquebuse" => "xschott@gmail.com" }
s.social_media_url = "http://twitter.com/xavierschott"
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If this Pod runs only on iOS or OS X, then specify the platform and
# the deployment target. You can optionally include the target after the platform.
#
s.platform = :ios, "7.0"
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the location from where the source should be retrieved.
# Supports git, hg, bzr, svn and HTTP.
#
s.source = { :git => "https://github.com/arquebuse/TGPControls.git", :tag => "0.0.1" }
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# CocoaPods is smart about how it includes source code. For source files
# giving a folder will include any h, m, mm, c & cpp files. For header
# files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
#
s.source_files = "TGPControls", "TGPControls/**/*.{h,m}"
# s.exclude_files = "TGPControls/**/*.{h,m}"
s.public_header_files = "TGPControls/**/*.{h}"
# ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. Anything else will be cleaned.
# You can preserve files from being cleaned, please don't preserve
# non-essential files like tests, examples and documentation.
#
# s.resource = "TGPImages.car"
# s.resources = "TGPImages.xcassets/*.imageset"
# s.preserve_paths = "FilesToSave", "MoreFilesToSave"
# ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Link your library with frameworks, or libraries. Libraries do not include
# the lib prefix of their name.
#
# s.framework = "SomeFramework"
# s.frameworks = "SomeFramework", "AnotherFramework"
# s.library = "iconv"
# s.libraries = "iconv", "xml2"
# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If your library depends on compiler flags you can set them in the xcconfig hash
# where they will only apply to your library. If you depend on other Podspecs
# you can include multiple dependencies to ensure it works.
s.requires_arc = true
# s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
# s.dependency "JSONKit", "~> 1.4"
end

View File

@ -0,0 +1,395 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
DC0468E31A75962E0084DE3E /* TGPCamelLabels7.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0468E01A75962E0084DE3E /* TGPCamelLabels7.m */; };
DC0468E41A75962E0084DE3E /* TGPDiscreteSlider7.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0468E21A75962E0084DE3E /* TGPDiscreteSlider7.m */; };
DC0468E71A7596690084DE3E /* TGPCamelLabels.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0468E61A7596690084DE3E /* TGPCamelLabels.m */; };
DC0468EA1A7596930084DE3E /* TGPDiscreteSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0468E91A7596930084DE3E /* TGPDiscreteSlider.m */; };
DC101D661A75749600ECCF80 /* libTGPControls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC101D5A1A75749600ECCF80 /* libTGPControls.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DC101D671A75749600ECCF80 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DC101D521A75749600ECCF80 /* Project object */;
proxyType = 1;
remoteGlobalIDString = DC101D591A75749600ECCF80;
remoteInfo = TGPControls;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
DC101D581A75749600ECCF80 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
DC0468DF1A75962E0084DE3E /* TGPCamelLabels7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPCamelLabels7.h; sourceTree = "<group>"; };
DC0468E01A75962E0084DE3E /* TGPCamelLabels7.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPCamelLabels7.m; sourceTree = "<group>"; };
DC0468E11A75962E0084DE3E /* TGPDiscreteSlider7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPDiscreteSlider7.h; sourceTree = "<group>"; };
DC0468E21A75962E0084DE3E /* TGPDiscreteSlider7.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPDiscreteSlider7.m; sourceTree = "<group>"; };
DC0468E51A7596690084DE3E /* TGPCamelLabels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPCamelLabels.h; sourceTree = "<group>"; };
DC0468E61A7596690084DE3E /* TGPCamelLabels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPCamelLabels.m; sourceTree = "<group>"; };
DC0468E81A7596930084DE3E /* TGPDiscreteSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPDiscreteSlider.h; sourceTree = "<group>"; };
DC0468E91A7596930084DE3E /* TGPDiscreteSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPDiscreteSlider.m; sourceTree = "<group>"; };
DC101D5A1A75749600ECCF80 /* libTGPControls.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTGPControls.a; sourceTree = BUILT_PRODUCTS_DIR; };
DC101D651A75749600ECCF80 /* TGPControlsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TGPControlsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DC101D6B1A75749600ECCF80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DC101D7A1A75756300ECCF80 /* TGPControls_License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TGPControls_License.txt; sourceTree = "<group>"; };
DC101D7B1A75756300ECCF80 /* TGPControls.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = TGPControls.podspec; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DC101D571A75749600ECCF80 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DC101D621A75749600ECCF80 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DC101D661A75749600ECCF80 /* libTGPControls.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DC0468DE1A75962E0084DE3E /* TGPControls */ = {
isa = PBXGroup;
children = (
DC0468E51A7596690084DE3E /* TGPCamelLabels.h */,
DC0468E61A7596690084DE3E /* TGPCamelLabels.m */,
DC0468E81A7596930084DE3E /* TGPDiscreteSlider.h */,
DC0468E91A7596930084DE3E /* TGPDiscreteSlider.m */,
DC0468DF1A75962E0084DE3E /* TGPCamelLabels7.h */,
DC0468E01A75962E0084DE3E /* TGPCamelLabels7.m */,
DC0468E11A75962E0084DE3E /* TGPDiscreteSlider7.h */,
DC0468E21A75962E0084DE3E /* TGPDiscreteSlider7.m */,
);
path = TGPControls;
sourceTree = "<group>";
};
DC101D511A75749600ECCF80 = {
isa = PBXGroup;
children = (
DC101D7A1A75756300ECCF80 /* TGPControls_License.txt */,
DC101D7B1A75756300ECCF80 /* TGPControls.podspec */,
DC0468DE1A75962E0084DE3E /* TGPControls */,
DC101D691A75749600ECCF80 /* TGPControlsTests */,
DC101D5B1A75749600ECCF80 /* Products */,
);
sourceTree = "<group>";
};
DC101D5B1A75749600ECCF80 /* Products */ = {
isa = PBXGroup;
children = (
DC101D5A1A75749600ECCF80 /* libTGPControls.a */,
DC101D651A75749600ECCF80 /* TGPControlsTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
DC101D691A75749600ECCF80 /* TGPControlsTests */ = {
isa = PBXGroup;
children = (
DC101D6A1A75749600ECCF80 /* Supporting Files */,
);
path = TGPControlsTests;
sourceTree = "<group>";
};
DC101D6A1A75749600ECCF80 /* Supporting Files */ = {
isa = PBXGroup;
children = (
DC101D6B1A75749600ECCF80 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DC101D591A75749600ECCF80 /* TGPControls */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC101D6E1A75749600ECCF80 /* Build configuration list for PBXNativeTarget "TGPControls" */;
buildPhases = (
DC101D561A75749600ECCF80 /* Sources */,
DC101D571A75749600ECCF80 /* Frameworks */,
DC101D581A75749600ECCF80 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = TGPControls;
productName = TGPControls;
productReference = DC101D5A1A75749600ECCF80 /* libTGPControls.a */;
productType = "com.apple.product-type.library.static";
};
DC101D641A75749600ECCF80 /* TGPControlsTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC101D711A75749600ECCF80 /* Build configuration list for PBXNativeTarget "TGPControlsTests" */;
buildPhases = (
DC101D611A75749600ECCF80 /* Sources */,
DC101D621A75749600ECCF80 /* Frameworks */,
DC101D631A75749600ECCF80 /* Resources */,
);
buildRules = (
);
dependencies = (
DC101D681A75749600ECCF80 /* PBXTargetDependency */,
);
name = TGPControlsTests;
productName = TGPControlsTests;
productReference = DC101D651A75749600ECCF80 /* TGPControlsTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DC101D521A75749600ECCF80 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = arquebuse;
TargetAttributes = {
DC101D591A75749600ECCF80 = {
CreatedOnToolsVersion = 6.1.1;
};
DC101D641A75749600ECCF80 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = DC101D551A75749600ECCF80 /* Build configuration list for PBXProject "TGPControls" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = DC101D511A75749600ECCF80;
productRefGroup = DC101D5B1A75749600ECCF80 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DC101D591A75749600ECCF80 /* TGPControls */,
DC101D641A75749600ECCF80 /* TGPControlsTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
DC101D631A75749600ECCF80 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DC101D561A75749600ECCF80 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DC0468E31A75962E0084DE3E /* TGPCamelLabels7.m in Sources */,
DC0468E71A7596690084DE3E /* TGPCamelLabels.m in Sources */,
DC0468E41A75962E0084DE3E /* TGPDiscreteSlider7.m in Sources */,
DC0468EA1A7596930084DE3E /* TGPDiscreteSlider.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DC101D611A75749600ECCF80 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DC101D681A75749600ECCF80 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DC101D591A75749600ECCF80 /* TGPControls */;
targetProxy = DC101D671A75749600ECCF80 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
DC101D6C1A75749600ECCF80 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
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 = 8.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
DC101D6D1A75749600ECCF80 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
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 = 8.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
DC101D6F1A75749600ECCF80 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
DC101D701A75749600ECCF80 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
DC101D721A75749600ECCF80 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = TGPControlsTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
DC101D731A75749600ECCF80 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = TGPControlsTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DC101D551A75749600ECCF80 /* Build configuration list for PBXProject "TGPControls" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DC101D6C1A75749600ECCF80 /* Debug */,
DC101D6D1A75749600ECCF80 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DC101D6E1A75749600ECCF80 /* Build configuration list for PBXNativeTarget "TGPControls" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DC101D6F1A75749600ECCF80 /* Debug */,
DC101D701A75749600ECCF80 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DC101D711A75749600ECCF80 /* Build configuration list for PBXNativeTarget "TGPControlsTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DC101D721A75749600ECCF80 /* Debug */,
DC101D731A75749600ECCF80 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = DC101D521A75749600ECCF80 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:TGPControls.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,48 @@
// @file: TGPCamelLabels.h
// @project: TGPControls
//
// @history: Created November 27, 2014 (Thanksgiving Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPCamelLabels7.h"
IB_DESIGNABLE
@interface TGPCamelLabels : TGPCamelLabels7
@property (nonatomic) IBInspectable NSUInteger tickCount; // Only used if [labels count] < 1
@property (nonatomic) IBInspectable CGFloat ticksDistance;
@property (nonatomic) IBInspectable NSUInteger value;
@property (nonatomic) IBInspectable NSString * upFontName;
@property (nonatomic) IBInspectable CGFloat upFontSize;
@property (nonatomic) IBInspectable UIColor * upFontColor;
@property (nonatomic) IBInspectable NSString * downFontName;
@property (nonatomic) IBInspectable CGFloat downFontSize;
@property (nonatomic) IBInspectable UIColor * downFontColor;
@end

View File

@ -0,0 +1,35 @@
// @file: TGPCamelLabels.m
// @project: TGPControls
//
// @history: Created November 27, 2014 (Thanksgiving Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPCamelLabels.h"
@implementation TGPCamelLabels
@end

View File

@ -0,0 +1,49 @@
// @file: TGPCamelLabels7.h
// @project: TGPControls
//
// @history: Created July 4th, 2014 (Independence Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@interface TGPCamelLabels7 : UIControl
@property (nonatomic) NSUInteger tickCount; // Only used if [labels count] < 1
@property (nonatomic) CGFloat ticksDistance;
@property (nonatomic) NSUInteger value;
@property (nonatomic) NSString * upFontName;
@property (nonatomic) CGFloat upFontSize;
@property (nonatomic) UIColor * upFontColor;
@property (nonatomic) NSString * downFontName;
@property (nonatomic) CGFloat downFontSize;
@property (nonatomic) UIColor * downFontColor;
@property (nonatomic) NSArray * names; // Will dictate the number of ticks
@end

View File

@ -0,0 +1,285 @@
// @file: TGPCamelLabels7.m
// @project: TGPControls
//
// @history: Created July 4th, 2014 (Independence Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPCamelLabels7.h"
@interface TGPCamelLabels7()
@property (nonatomic, assign) NSUInteger lastValue;
@property (nonatomic, retain) NSMutableArray * upLabels;
@property (nonatomic, retain) NSMutableArray * dnLabels;
@end
@implementation TGPCamelLabels7
#pragma mark properties
- (void)setTickCount:(NSUInteger)tickCount {
// calculated property
// Put some order to tickCount: 1 >= count >= 128
const unsigned int count = (unsigned int) MAX(1, MIN(tickCount, 128));
[self debugNames:count];
[self layoutTrack];
}
- (NSUInteger)tickCount {
// Dynamic property
return [_names count];
}
- (void)setTicksDistance:(CGFloat)ticksDistance {
_ticksDistance = MAX(0, ticksDistance);
[self layoutTrack];
}
- (void)setValue:(NSUInteger)value {
_value = value;
[self dockEffect:.15];
}
- (void)setUpFontName:(NSString *)upFontName {
_upFontName = upFontName;
[self layoutTrack];
}
- (void)setUpFontSize:(CGFloat)upFontSize {
_upFontSize = upFontSize;
[self layoutTrack];
}
- (void)setUpFontColor:(UIColor *)upFontColor {
_upFontColor = upFontColor;
[self layoutTrack];
}
- (void)setDownFontName:(NSString *)downFontName {
_downFontName = downFontName;
[self layoutTrack];
}
- (void)setDownFontSize:(CGFloat)downFontSize {
_downFontSize = downFontSize;
[self layoutTrack];
}
- (void)setDownFontColor:(UIColor *)downFontColor {
_downFontColor = downFontColor;
[self layoutTrack];
}
// NSArray<NSString*>
- (void)setNames:(NSArray *)names {
NSAssert(names.count > 0, @"names.count");
_names = names;
[self layoutTrack];
}
#pragma mark UIView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self != nil) {
[self initProperties];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self != nil) {
[self initProperties];
}
return self;
}
// clickthrough
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *view in self.subviews) {
if (!view.hidden && view.alpha > 0 && view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event])
return YES;
}
return NO;
}
#pragma mark TGPCamelSlider
- (void)initProperties {
_ticksDistance = 44.0;
_value = 0;
[self debugNames:10];
_upFontName = nil;
_upFontSize = 12;
_upFontColor = nil;
_downFontName = nil;
_downFontSize = 12;
_downFontColor = nil;
_upLabels = [NSMutableArray array];
_dnLabels = [NSMutableArray array];
_lastValue = NSNotFound; // Never tapped
[self layoutTrack];
}
- (void)debugNames:(unsigned int)count {
// Dynamic property, will create an array with labels, generally for debugging purposes
const NSMutableArray * array = [NSMutableArray array];
for(int iterate = 1; iterate <= count; iterate++) {
[array addObject:[NSString stringWithFormat:@"%d", iterate ]];
}
[self setNames:(NSArray *) array];
}
- (void)layoutTrack {
[self.upLabels enumerateObjectsUsingBlock:^(UIView * view, NSUInteger idx, BOOL *stop) {
[view removeFromSuperview];
}];
[self.upLabels removeAllObjects];
[self.dnLabels enumerateObjectsUsingBlock:^(UIView * view, NSUInteger idx, BOOL *stop) {
[view removeFromSuperview];
}];
[self.dnLabels removeAllObjects];
const NSUInteger count = self.names.count;
if( count > 0) {
CGFloat centerX = (self.bounds.size.width - ((count - 1) * self.ticksDistance))/2.0;
const CGFloat centerY = self.bounds.size.height / 2.0;
for(NSString * name in self.names) {
UILabel * upLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.upLabels addObject:upLabel];
upLabel.text = name;
upLabel.font = ((self.upFontName != nil)
? [UIFont fontWithName:self.upFontName size:self.upFontSize]
: [UIFont boldSystemFontOfSize:self.upFontSize]);
upLabel.textColor = ((self.upFontColor != nil)
? self.upFontColor
: self.tintColor);
[upLabel sizeToFit];
upLabel.center = CGPointMake(centerX, centerY);
upLabel.frame = ({
CGRect frame = upLabel.frame;
// frame.origin.y = 0;
frame.origin.y = self.bounds.size.height - frame.size.height;
frame;
});
upLabel.alpha = 0.0;
[self addSubview:upLabel];
UILabel * dnLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.dnLabels addObject:dnLabel];
dnLabel.text = name;
dnLabel.font = ((self.downFontName != nil)
? [UIFont fontWithName:self.downFontName size:self.downFontSize]
: [UIFont boldSystemFontOfSize:self.downFontSize]);
dnLabel.textColor = ((self.downFontColor != nil)
? self.downFontColor
: [UIColor grayColor]);
[dnLabel sizeToFit];
dnLabel.center = CGPointMake(centerX, centerY);
dnLabel.frame = ({
CGRect frame = dnLabel.frame;
frame.origin.y = self.bounds.size.height - frame.size.height;
frame;
});
[self addSubview:dnLabel];
centerX += self.ticksDistance;
}
[self dockEffect:0.0];
}
}
- (void)dockEffect:(NSTimeInterval)duration
{
const NSUInteger up = self.value;
// Unlike the National Parks from which it is inspired, this Dock Effect
// does not abruptly change from BOLD to plain. Instead, we have 2 sets of
// labels, which are faded back and forth, in unisson.
// - BOLD to plain
// - Black to gray
// - high to low
// Each animation picks up where the previous left off
void (^moveBlock)() = ^void() {
// Bring almost all down
[self.upLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if( up != idx) {
[self moveDown:obj withAlpha:0.f];
}
}];
[self.dnLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if( up != idx) {
[self moveDown:obj withAlpha:1.f];
}
}];
// Bring the selection up
if(up < [self.upLabels count]) {
[self moveUp:[self.upLabels objectAtIndex:up] withAlpha:1.f];
}
if(up < [self.dnLabels count]) {
[self moveUp:[self.dnLabels objectAtIndex:up] withAlpha:0.f];
}
};
if(duration > 0) {
[UIView animateWithDuration:duration
delay:0
options:(UIViewAnimationOptionBeginFromCurrentState +
UIViewAnimationOptionCurveLinear)
animations:moveBlock
completion:nil];
} else {
moveBlock();
}
}
- (void)moveDown:(UIView*)aView withAlpha:(CGFloat) alpha
{
aView.frame = ({
CGRect frame = aView.frame;
frame.origin.y = self.bounds.size.height - frame.size.height;
frame;
});
[aView setAlpha:alpha];
}
- (void)moveUp:(UIView*)aView withAlpha:(CGFloat) alpha
{
aView.frame = ({
CGRect frame = aView.frame;
frame.origin.y = 0;
frame;
});
[aView setAlpha:alpha];
}
@end

View File

@ -0,0 +1,55 @@
// @file: TGPDiscreteSlider.h
// @project: TGPControls
//
// @history: Created November 27, 2014 (Thanksgiving Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPDiscreteSlider7.h"
IB_DESIGNABLE
@interface TGPDiscreteSlider : TGPDiscreteSlider7
@property (nonatomic) IBInspectable int tickStyle;
@property (nonatomic) IBInspectable CGSize tickSize;
@property (nonatomic) IBInspectable int tickCount;
@property (nonatomic) IBInspectable int trackStyle;
@property (nonatomic) IBInspectable CGFloat trackThickness;
@property (nonatomic) IBInspectable int thumbStyle;
@property (nonatomic) IBInspectable CGSize thumbSize;
@property (nonatomic) IBInspectable UIColor * thumbColor;
@property (nonatomic) IBInspectable CGFloat thumbSRadius;
@property (nonatomic) IBInspectable CGSize thumbSOffset;
// AKA: UISlider value (as CGFloat for compatibility with UISlider API, but expected to contain integers)
@property (nonatomic) IBInspectable CGFloat minimumValue;
@property (nonatomic) IBInspectable CGFloat value;
@property (nonatomic) IBInspectable int incrementValue;
@end

View File

@ -0,0 +1,47 @@
// @file: TGPDiscreteSlider.m
// @project: TGPControls
//
// @history: Created November 27, 2014 (Thanksgiving Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPDiscreteSlider.h"
@implementation TGPDiscreteSlider
#pragma mark properties
- (void)setThumbSRadius:(CGFloat)thumbSRadius {
// adapter
self.thumbShadowRadius = thumbSRadius;
}
- (void)setThumbSOffset:(CGSize)thumbSOffset {
// adapter
self.thumbShadowOffset = thumbSOffset;
}
@end

View File

@ -0,0 +1,70 @@
// @file: TGPDiscreteSlider7.h
// @project: TGPControls
//
// @history: Created July 4th, 2014 (Independence Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@interface TGPDiscreteSlider7 :
// Interface builder hides the IBInspectable for UIControl
#if TARGET_INTERFACE_BUILDER
UIView
#else // !TARGET_INTERFACE_BUILDER
UIControl
#endif // TARGET_INTERFACE_BUILDER
typedef NS_ENUM(int, ComponentStyle) {
ComponentStyleIOS = 0,
ComponentStyleRectangular,
ComponentStyleRounded,
ComponentStyleInvisible,
ComponentStyleImage
};
@property (nonatomic) ComponentStyle tickStyle;
@property (nonatomic) CGSize tickSize;
@property (nonatomic) int tickCount;
@property (nonatomic, readonly) CGFloat ticksDistance;
@property (nonatomic) ComponentStyle trackStyle;
@property (nonatomic) CGFloat trackThickness;
@property (nonatomic) ComponentStyle thumbStyle;
@property (nonatomic) CGSize thumbSize;
@property (nonatomic) UIColor * thumbColor;
@property (nonatomic) CGFloat thumbShadowRadius;
@property (nonatomic) CGSize thumbShadowOffset;
// AKA: UISlider value (as CGFloat for compatibility with UISlider API, but expected to contain integers)
@property (nonatomic) CGFloat minimumValue;
@property (nonatomic) CGFloat value;
@property (nonatomic) int incrementValue;
@end

View File

@ -0,0 +1,486 @@
// @file: TGPDiscreteSlider7.m
// @project: TGPControls
//
// @history: Created July 4th, 2014 (Independence Day)
// @author: Xavier Schott
// mailto://xschott@gmail.com
// http://thegothicparty.com
// tel://+18089383634
//
// @license: http://opensource.org/licenses/MIT
// Copyright (c) 2014, Xavier Schott
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "TGPDiscreteSlider7.h"
static CGFloat iOSThumbShadowRadius = 4.0;
static CGSize iosThumbShadowOffset = (CGSize){0, 3};
@interface TGPDiscreteSlider7 () {
int _intValue;
int _intMinimumValue;
}
@property (nonatomic) NSMutableArray * ticksAbscisses;
@property (nonatomic, assign) CGFloat thumbAbscisse;
@property (nonatomic) CALayer * thumbLayer;
@property (nonatomic) CALayer * colorTrackLayer;
@property (nonatomic) CGRect trackRectangle;
@end
@implementation TGPDiscreteSlider7
#pragma mark properties
- (void)setTickStyle:(ComponentStyle)tickStyle {
_tickStyle = tickStyle;
[self layoutTrack];
}
- (void)setTickSize:(CGSize)tickSize {
_tickSize.width = MAX(0, tickSize.width);
_tickSize.height = MAX(0, tickSize.height);
[self layoutTrack];
}
- (void)setTickCount:(int)tickCount {
_tickCount = MAX(2, tickCount);
[self layoutTrack];
}
- (CGFloat)ticksDistance {
NSAssert1(self.tickCount > 1, @"2 ticks minimum %d", self.tickCount);
const unsigned int segments = MAX(1, self.tickCount - 1);
const CGFloat thumbWidth = [self thumbSizeIncludingShadow].width;
const double trackLength = (double)(self.frame.size.width - thumbWidth);
return (CGFloat) (trackLength / segments);
}
- (void)setTrackStyle:(ComponentStyle)trackStyle {
_trackStyle = trackStyle;
[self layoutTrack];
}
- (void)setTrackThickness:(CGFloat)trackThickness {
_trackThickness = MAX(0, trackThickness);
[self layoutTrack];
}
- (void)setThumbStyle:(ComponentStyle)thumbStyle {
_thumbStyle = thumbStyle;
[self layoutTrack];
}
- (void)setThumbSize:(CGSize)thumbSize {
_thumbSize.width = MAX(1, thumbSize.width);
_thumbSize.height = MAX(1, thumbSize.height);
[self layoutTrack];
}
- (void)setThumbShadowRadius:(CGFloat)thumbShadowRadius {
_thumbShadowRadius = thumbShadowRadius;
[self layoutTrack];
}
- (void)setIncrementValue:(int)incrementValue {
_incrementValue = incrementValue;
if(0 == incrementValue) {
_incrementValue = 1; // nonZeroIncrement
}
[self layoutTrack];
}
// AKA: UISlider value (as CGFloat for compatibility with UISlider API, but expected to contain integers)
- (void)setMinimumValue:(CGFloat)minimumValue {
_intMinimumValue = minimumValue;
[self layoutTrack];
}
- (CGFloat)minimumValue {
return _intMinimumValue; // calculated property, with a float-to-int adapter
}
- (void)setValue:(CGFloat)value {
const unsigned int nonZeroIncrement = ((0 == _incrementValue) ? 1 : _incrementValue);
const int rootValue = ((value - self.minimumValue) / nonZeroIncrement);
_intValue = self.minimumValue + (rootValue * nonZeroIncrement);
[self layoutTrack];
}
- (CGFloat)value {
return _intValue; // calculated property, with a float-to-int adapter
}
#pragma mark UIControl
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self != nil) {
[self initProperties];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self != nil) {
[self initProperties];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self drawTrack];
[self drawThumb];
}
#pragma mark TGPDiscreteSlider7
- (void)initProperties {
_tickStyle = ComponentStyleRectangular;
_tickSize = (CGSize) {1.0, 4.0};
_tickCount = 11;
_trackStyle = ComponentStyleIOS;
_trackThickness = 2.0;
_thumbStyle = ComponentStyleIOS;
_thumbSize = (CGSize) {10.0, 10.0};
_thumbColor = [UIColor lightGrayColor];
_thumbShadowRadius = 0.0;
_thumbShadowOffset = CGSizeZero;
_intMinimumValue = -5;
_incrementValue = 1;
_intValue = 0;
_ticksAbscisses = [NSMutableArray array];
_thumbAbscisse = 0.0;
_trackRectangle = CGRectZero;
// In case we need a colored track, initialize it now
// There may be a more elegant way to do this than with a CALayer,
// but then again CALayer brings free animation and will animate along the thumb
_colorTrackLayer = [CALayer layer];
_colorTrackLayer.backgroundColor = [[UIColor colorWithHue:211.0/360.0 saturation:1 brightness:1 alpha:1] CGColor];
_colorTrackLayer.cornerRadius = 2.0;
[self.layer addSublayer:self.colorTrackLayer];
// The thumb is its own CALayer, which brings in free animation
_thumbLayer = [CALayer layer];
[self.layer addSublayer:self.thumbLayer];
self.multipleTouchEnabled = NO;
[self layoutTrack];
}
- (void)drawTrack {
const CGContextRef ctx = UIGraphicsGetCurrentContext();
// Track
switch(self.trackStyle) {
case ComponentStyleRectangular:
CGContextAddRect(ctx, self.trackRectangle);
break;
case ComponentStyleInvisible:
// Nothing to draw
break;
case ComponentStyleRounded:
case ComponentStyleIOS:
default: {
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:self.trackRectangle
cornerRadius:self.trackRectangle.size.height/2];
CGContextAddPath(ctx, [path CGPath]) ;
break;
}
}
// Ticks
if(ComponentStyleIOS != self.tickStyle) {
NSAssert(nil != self.ticksAbscisses, @"ticksAbscisses");
if(nil != self.ticksAbscisses) {
for(NSValue * originValue in self.ticksAbscisses) {
CGPoint originPoint = [originValue CGPointValue];
CGRect rectangle = CGRectMake(originPoint.x-(self.tickSize.width/2),
originPoint.y-(self.tickSize.height/2),
self.tickSize.width, self.tickSize.height);
switch(self.tickStyle) {
case ComponentStyleRounded: {
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rectangle
cornerRadius:rectangle.size.height/2];
CGContextAddPath(ctx, [path CGPath]) ;
break;
}
case ComponentStyleRectangular:
CGContextAddRect(ctx, rectangle);
break;
case ComponentStyleInvisible:
case ComponentStyleIOS:
default:
// Nothing to draw
break;
}
}
}
} // iOS UISlider does not have ticks
CGContextSetFillColor(ctx, CGColorGetComponents([self.tintColor CGColor]));
CGContextFillPath(ctx);
// For colored track, we overlay a CALayer, which will animate along with the cursor
if(ComponentStyleIOS == self.trackStyle) {
self.colorTrackLayer.frame = ({
CGRect frame = self.trackRectangle;
frame.size.width = self.thumbAbscisse - CGRectGetMinX(self.trackRectangle);
frame;
});
} else {
self.colorTrackLayer.frame = CGRectZero;
}
}
- (void)drawThumb {
const CGSize thumbSizeForStyle = [self thumbSizeIncludingShadow];
const CGFloat thumbWidth = thumbSizeForStyle.width;
const CGFloat thumbHeight = thumbSizeForStyle.height;
const CGRect rectangle = CGRectMake(self.thumbAbscisse - (thumbWidth / 2),
(self.frame.size.height - thumbHeight)/2,
thumbWidth,
thumbHeight);
const CGFloat shadowRadius = ((self.thumbStyle == ComponentStyleIOS)
? iOSThumbShadowRadius
: self.thumbShadowRadius);
const CGSize shadowOffset = ((self.thumbStyle == ComponentStyleIOS)
? iosThumbShadowOffset
: self.thumbShadowOffset);
self.thumbLayer.frame = ((shadowRadius != 0.0) // Ignore offset if there is no shadow
? CGRectInset(rectangle,
shadowRadius + shadowOffset.width,
shadowRadius + shadowOffset.height)
: CGRectInset(rectangle, shadowRadius, shadowRadius));
switch(self.thumbStyle) {
case ComponentStyleRounded: // A rounded thumb is circular
self.thumbLayer.backgroundColor = [self.thumbColor CGColor];
self.thumbLayer.borderColor = [[UIColor clearColor] CGColor];
self.thumbLayer.borderWidth = 0.0;
self.thumbLayer.cornerRadius = self.thumbLayer.frame.size.width/2;
self.thumbLayer.allowsEdgeAntialiasing = YES;
break;
case ComponentStyleRectangular:
self.thumbLayer.backgroundColor = [self.thumbColor CGColor];
self.thumbLayer.borderColor = [[UIColor clearColor] CGColor];
self.thumbLayer.borderWidth = 0.0;
self.thumbLayer.cornerRadius = 0.0;
self.thumbLayer.allowsEdgeAntialiasing = NO;
break;
case ComponentStyleInvisible:
self.thumbLayer.backgroundColor = [[UIColor clearColor] CGColor];
self.thumbLayer.cornerRadius = 0.0;
break;
case ComponentStyleIOS:
default:
self.thumbLayer.backgroundColor = [[UIColor whiteColor] CGColor];
self.thumbLayer.borderColor = [[UIColor colorWithHue:0 saturation: 0 brightness: 0.8 alpha: 1]
CGColor];
self.thumbLayer.borderWidth = 0.5;
self.thumbLayer.cornerRadius = self.thumbLayer.frame.size.width/2;
self.thumbLayer.allowsEdgeAntialiasing = YES;
break;
}
// Shadow
if(shadowRadius != 0.0) {
#if TARGET_INTERFACE_BUILDER
self.thumbLayer.shadowOffset = CGSizeMake(shadowOffset.width, -shadowOffset.height);
#else // !TARGET_INTERFACE_BUILDER
self.thumbLayer.shadowOffset = shadowOffset;
#endif // TARGET_INTERFACE_BUILDER
self.thumbLayer.shadowRadius = shadowRadius;
self.thumbLayer.shadowColor = [[UIColor blackColor] CGColor];
self.thumbLayer.shadowOpacity = 0.15;
} else {
self.thumbLayer.shadowRadius = 0.0;
self.thumbLayer.shadowOffset = CGSizeZero;
self.thumbLayer.shadowColor = [[UIColor clearColor] CGColor];
self.thumbLayer.shadowOpacity = 0.0;
}
}
- (void)layoutTrack {
NSAssert1(self.tickCount > 1, @"2 ticks minimum %d", self.tickCount);
const unsigned int segments = MAX(1, self.tickCount - 1);
const CGFloat thumbWidth = [self thumbSizeIncludingShadow].width;
const double trackLength = (double)(self.frame.size.width - thumbWidth);
// Calculate the track ticks positions
const CGFloat trackHeight = ((ComponentStyleIOS == self.trackStyle)
? 2.0
: self.trackThickness);
self.trackRectangle = CGRectMake(thumbWidth/2, (self.frame.size.height - trackHeight)/2,
self.frame.size.width - thumbWidth, trackHeight);
const CGFloat trackY = self.frame.size.height / 2;
[self.ticksAbscisses removeAllObjects];
for( int iterate = 0; iterate <= segments; iterate++) {
const double ratio = (double)iterate / (double)segments;
const CGFloat originX = (thumbWidth / 2) + (CGFloat)(trackLength * ratio);
[self.ticksAbscisses addObject: [NSValue valueWithCGPoint:CGPointMake(originX, trackY)]];
}
[self layoutThumb];
}
- (void)layoutThumb {
NSAssert1(self.tickCount > 1, @"2 ticks minimum %d", self.tickCount);
const unsigned int segments = MAX(1, self.tickCount - 1);
const CGFloat thumbWidth = [self thumbSizeIncludingShadow].width;
const double trackLength = (double)(self.frame.size.width - thumbWidth);
// Calculate the thumb position
const unsigned int nonZeroIncrement = ((0 == self.incrementValue) ? 1 : self.incrementValue);
double thumbRatio = (double)(self.value - self.minimumValue) / (double)(segments * nonZeroIncrement);
thumbRatio = MAX(0.0, MIN(thumbRatio, 1.0)); // Normalized
self.thumbAbscisse = (thumbWidth / 2) + (CGFloat)(trackLength * thumbRatio);
}
- (CGSize)thumbSizeIncludingShadow {
switch (self.thumbStyle) {
case ComponentStyleInvisible:
return CGSizeZero;
case ComponentStyleRectangular:
case ComponentStyleRounded:
return ((self.thumbShadowRadius != 0.0)
? CGSizeMake(self.thumbSize.width
+ (self.thumbShadowRadius * 2)
+ (self.thumbShadowOffset.width * 2),
self.thumbSize.height
+ (self.thumbShadowRadius * 2)
+ (self.thumbShadowOffset.height * 2))
: self.thumbSize);
case ComponentStyleIOS:
return CGSizeMake(33.0
+ (iOSThumbShadowRadius * 2)
+ (iosThumbShadowOffset.width * 2),
33.0
+ (iOSThumbShadowRadius * 2)
+ (iosThumbShadowOffset.height * 2));
default:
return (CGSize){33.0, 33.0};
}
}
#pragma mark UIControl
- (void)sendActionsForControlEvents {
// Interface builder hides the IBInspectable for UIControl
#if !TARGET_INTERFACE_BUILDER
[self sendActionsForControlEvents:UIControlEventValueChanged];
#endif // !TARGET_INTERFACE_BUILDER
}
#pragma mark Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchDown:touches duration:0.1];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchDown:touches duration:0.0];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchUp:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchUp:touches];
}
- (void)touchDown:(NSSet *)touches duration:(NSTimeInterval)duration {
const UITouch * touch = [touches anyObject];
if(nil != touch) {
const CGPoint location = [touch locationInView:touch.view];
[self moveThumbTo:location.x duration:duration];
}
}
- (void)touchUp:(NSSet *)touches {
const UITouch * touch = [touches anyObject];
if(nil != touch) {
const CGPoint location = [touch locationInView:touch.view];
const unsigned int tick = [self pickTickFromSliderPosition:location.x];
[self moveThumbToTick:tick];
}
}
#pragma mark Notifications
- (void)moveThumbToTick:(unsigned int)tick {
const unsigned int nonZeroIncrement = ((0 == self.incrementValue) ? 1 : self.incrementValue);
_intValue = self.minimumValue + (tick * nonZeroIncrement);
[self sendActionsForControlEvents];
[self layoutThumb];
[self setNeedsDisplay];
}
- (void)moveThumbTo:(CGFloat)abscisse duration:(CFTimeInterval)duration {
const CGFloat leftMost = CGRectGetMinX(self.trackRectangle);
const CGFloat rightMost = CGRectGetMaxX(self.trackRectangle);
self.thumbAbscisse = MAX(leftMost, MIN(abscisse, rightMost));
[CATransaction setAnimationDuration:duration];
const unsigned int tick = [self pickTickFromSliderPosition:self.thumbAbscisse];
const unsigned int nonZeroIncrement = ((0 == self.incrementValue) ? 1 : self.incrementValue);
_intValue = self.minimumValue + (tick * nonZeroIncrement);
[self sendActionsForControlEvents];
[self setNeedsDisplay];
}
- (unsigned int)pickTickFromSliderPosition:(CGFloat)abscisse {
const CGFloat leftMost = CGRectGetMinX(self.trackRectangle);
const CGFloat rightMost = CGRectGetMaxX(self.trackRectangle);
const CGFloat clampedAbscisse = MAX(leftMost, MIN(abscisse, rightMost));
const double ratio = (double)(clampedAbscisse - leftMost) / (double)(rightMost - leftMost);
const unsigned int segments = MAX(1, self.tickCount - 1);
return (unsigned int) round( (double)segments * ratio);
}
#pragma mark - Interface Builder
#if TARGET_INTERFACE_BUILDER
// Interface builder hides the IBInspectable for UIControl
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents {}
#endif // TARGET_INTERFACE_BUILDER
@end

View File

@ -0,0 +1,24 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.thegothicparty.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

19
TGPControls_License.txt Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2014 Xavier Schott
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.