diff --git a/NGVolumeControl/NGVolumeControl.xcodeproj/project.pbxproj b/NGVolumeControl/NGVolumeControl.xcodeproj/project.pbxproj index d6ae50e..581574b 100644 --- a/NGVolumeControl/NGVolumeControl.xcodeproj/project.pbxproj +++ b/NGVolumeControl/NGVolumeControl.xcodeproj/project.pbxproj @@ -10,15 +10,33 @@ 9B0454C91504F33E007F8E7C /* NGGeometryFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B0454C81504F33E007F8E7C /* NGGeometryFunctions.h */; }; 9B0454F815058022007F8E7C /* NGVolumeControl+NGCustomize.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B0454F715058022007F8E7C /* NGVolumeControl+NGCustomize.h */; }; 9B17FBF314FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B17FBF214FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h */; }; + 9B1D3050162C38CA00F2395E /* NGVolumeSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B1D304E162C38CA00F2395E /* NGVolumeSlider.h */; }; + 9B1D3051162C38CA00F2395E /* NGVolumeSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1D304F162C38CA00F2395E /* NGVolumeSlider.m */; }; 9BCC746314FCCE8D004F30FF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BCC746214FCCE8D004F30FF /* Foundation.framework */; }; 9BCC746914FCCE8D004F30FF /* NGVolumeControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BCC746814FCCE8D004F30FF /* NGVolumeControl.m */; }; 9BCC74A214FCD3D9004F30FF /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BCC74A114FCD3D9004F30FF /* MediaPlayer.framework */; }; + A92BA8CD218486AE00BF8486 /* NGVolumeControlKit.h in Headers */ = {isa = PBXBuildFile; fileRef = A92BA8CB218486AE00BF8486 /* NGVolumeControlKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8D1218486C800BF8486 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BCC74A114FCD3D9004F30FF /* MediaPlayer.framework */; }; + A92BA8D2218486CC00BF8486 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BCC746214FCCE8D004F30FF /* Foundation.framework */; }; + A92BA8D3218486D500BF8486 /* NGVolumeControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BCC746814FCCE8D004F30FF /* NGVolumeControl.m */; }; + A92BA8D4218486D800BF8486 /* NGVolumeSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1D304F162C38CA00F2395E /* NGVolumeSlider.m */; }; + A92BA8D5218486E800BF8486 /* NGVolumeControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BCC746714FCCE8D004F30FF /* NGVolumeControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8D6218486E800BF8486 /* NGVolumeControlDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BB6010C162C308F00EFD66B /* NGVolumeControlDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8D7218486E800BF8486 /* NGVolumeSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B1D304E162C38CA00F2395E /* NGVolumeSlider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8D8218486E800BF8486 /* NGVolumeControlExpandDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B17FBF214FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8D9218486E800BF8486 /* NGVolumeControl+NGCustomize.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B0454F715058022007F8E7C /* NGVolumeControl+NGCustomize.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A92BA8DA218486E800BF8486 /* NGGeometryFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B0454C81504F33E007F8E7C /* NGGeometryFunctions.h */; }; + A92BA8DB218486F300BF8486 /* NGVolumeControl.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 9BCC74A914FCDC0F004F30FF /* NGVolumeControl.bundle */; }; + A92BA8DD218487DA00BF8486 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A92BA8DC218487DA00BF8486 /* UIKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 9B0454C81504F33E007F8E7C /* NGGeometryFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NGGeometryFunctions.h; sourceTree = ""; }; 9B0454F715058022007F8E7C /* NGVolumeControl+NGCustomize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NGVolumeControl+NGCustomize.h"; sourceTree = ""; }; 9B17FBF214FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NGVolumeControlExpandDirection.h; sourceTree = ""; }; + 9B1D304E162C38CA00F2395E /* NGVolumeSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NGVolumeSlider.h; sourceTree = ""; }; + 9B1D304F162C38CA00F2395E /* NGVolumeSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NGVolumeSlider.m; sourceTree = ""; }; + 9BB6010C162C308F00EFD66B /* NGVolumeControlDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NGVolumeControlDelegate.h; sourceTree = ""; }; 9BCC745F14FCCE8D004F30FF /* libNGVolumeControl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libNGVolumeControl.a; sourceTree = BUILT_PRODUCTS_DIR; }; 9BCC746214FCCE8D004F30FF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 9BCC746614FCCE8D004F30FF /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; @@ -26,6 +44,11 @@ 9BCC746814FCCE8D004F30FF /* NGVolumeControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NGVolumeControl.m; sourceTree = ""; }; 9BCC74A114FCD3D9004F30FF /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; 9BCC74A914FCDC0F004F30FF /* NGVolumeControl.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = NGVolumeControl.bundle; path = Resources/NGVolumeControl.bundle; sourceTree = ""; }; + A92BA8C9218486AE00BF8486 /* NGVolumeControlKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NGVolumeControlKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A92BA8CB218486AE00BF8486 /* NGVolumeControlKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NGVolumeControlKit.h; sourceTree = ""; }; + A92BA8CC218486AE00BF8486 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A92BA8DC218487DA00BF8486 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + A96C0B8F238DD31400BF7682 /* NGVolumeControl.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; name = NGVolumeControl.podspec; path = ../NGVolumeControl.podspec; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -38,14 +61,26 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A92BA8C6218486AE00BF8486 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A92BA8DD218487DA00BF8486 /* UIKit.framework in Frameworks */, + A92BA8D1218486C800BF8486 /* MediaPlayer.framework in Frameworks */, + A92BA8D2218486CC00BF8486 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 9BCC745414FCCE8C004F30FF = { isa = PBXGroup; children = ( + A96C0B8F238DD31400BF7682 /* NGVolumeControl.podspec */, 9BCC746414FCCE8D004F30FF /* NGVolumeControl */, 9BCC74A814FCDBF2004F30FF /* Resources */, + A92BA8CA218486AE00BF8486 /* NGVolumeControlKit */, 9BCC746114FCCE8D004F30FF /* Frameworks */, 9BCC746014FCCE8D004F30FF /* Products */, ); @@ -55,6 +90,7 @@ isa = PBXGroup; children = ( 9BCC745F14FCCE8D004F30FF /* libNGVolumeControl.a */, + A92BA8C9218486AE00BF8486 /* NGVolumeControlKit.framework */, ); name = Products; sourceTree = ""; @@ -62,6 +98,7 @@ 9BCC746114FCCE8D004F30FF /* Frameworks */ = { isa = PBXGroup; children = ( + A92BA8DC218487DA00BF8486 /* UIKit.framework */, 9BCC74A114FCD3D9004F30FF /* MediaPlayer.framework */, 9BCC746214FCCE8D004F30FF /* Foundation.framework */, ); @@ -73,10 +110,13 @@ children = ( 9BCC746714FCCE8D004F30FF /* NGVolumeControl.h */, 9BCC746814FCCE8D004F30FF /* NGVolumeControl.m */, + 9BB6010C162C308F00EFD66B /* NGVolumeControlDelegate.h */, + 9B1D304E162C38CA00F2395E /* NGVolumeSlider.h */, + 9B1D304F162C38CA00F2395E /* NGVolumeSlider.m */, + 9B17FBF214FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h */, 9B0454F715058022007F8E7C /* NGVolumeControl+NGCustomize.h */, 9B0454C81504F33E007F8E7C /* NGGeometryFunctions.h */, 9BCC746514FCCE8D004F30FF /* Supporting Files */, - 9B17FBF214FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h */, ); path = NGVolumeControl; sourceTree = ""; @@ -97,6 +137,15 @@ name = Resources; sourceTree = ""; }; + A92BA8CA218486AE00BF8486 /* NGVolumeControlKit */ = { + isa = PBXGroup; + children = ( + A92BA8CB218486AE00BF8486 /* NGVolumeControlKit.h */, + A92BA8CC218486AE00BF8486 /* Info.plist */, + ); + path = NGVolumeControlKit; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -107,6 +156,21 @@ 9B17FBF314FCDDDB00BF5F4A /* NGVolumeControlExpandDirection.h in Headers */, 9B0454C91504F33E007F8E7C /* NGGeometryFunctions.h in Headers */, 9B0454F815058022007F8E7C /* NGVolumeControl+NGCustomize.h in Headers */, + 9B1D3050162C38CA00F2395E /* NGVolumeSlider.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A92BA8C4218486AE00BF8486 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A92BA8D9218486E800BF8486 /* NGVolumeControl+NGCustomize.h in Headers */, + A92BA8D8218486E800BF8486 /* NGVolumeControlExpandDirection.h in Headers */, + A92BA8D6218486E800BF8486 /* NGVolumeControlDelegate.h in Headers */, + A92BA8DA218486E800BF8486 /* NGGeometryFunctions.h in Headers */, + A92BA8CD218486AE00BF8486 /* NGVolumeControlKit.h in Headers */, + A92BA8D7218486E800BF8486 /* NGVolumeSlider.h in Headers */, + A92BA8D5218486E800BF8486 /* NGVolumeControl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -130,20 +194,46 @@ productReference = 9BCC745F14FCCE8D004F30FF /* libNGVolumeControl.a */; productType = "com.apple.product-type.library.static"; }; + A92BA8C8218486AE00BF8486 /* NGVolumeControlKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = A92BA8CE218486AE00BF8486 /* Build configuration list for PBXNativeTarget "NGVolumeControlKit" */; + buildPhases = ( + A92BA8C4218486AE00BF8486 /* Headers */, + A92BA8C5218486AE00BF8486 /* Sources */, + A92BA8C6218486AE00BF8486 /* Frameworks */, + A92BA8C7218486AE00BF8486 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NGVolumeControlKit; + productName = NGVolumeControlKit; + productReference = A92BA8C9218486AE00BF8486 /* NGVolumeControlKit.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 9BCC745614FCCE8C004F30FF /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 1120; ORGANIZATIONNAME = "NOUS Wissensmanagement GmbH"; + TargetAttributes = { + A92BA8C8218486AE00BF8486 = { + CreatedOnToolsVersion = 10.0; + DevelopmentTeam = Y6BEBK9HD8; + ProvisioningStyle = Automatic; + }; + }; }; buildConfigurationList = 9BCC745914FCCE8C004F30FF /* Build configuration list for PBXProject "NGVolumeControl" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 9BCC745414FCCE8C004F30FF; @@ -152,16 +242,38 @@ projectRoot = ""; targets = ( 9BCC745E14FCCE8D004F30FF /* NGVolumeControl */, + A92BA8C8218486AE00BF8486 /* NGVolumeControlKit */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + A92BA8C7218486AE00BF8486 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A92BA8DB218486F300BF8486 /* NGVolumeControl.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 9BCC745B14FCCE8D004F30FF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 9BCC746914FCCE8D004F30FF /* NGVolumeControl.m in Sources */, + 9B1D3051162C38CA00F2395E /* NGVolumeSlider.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A92BA8C5218486AE00BF8486 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A92BA8D3218486D500BF8486 /* NGVolumeControl.m in Sources */, + A92BA8D4218486D800BF8486 /* NGVolumeSlider.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -172,11 +284,31 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -184,10 +316,15 @@ ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; @@ -196,15 +333,38 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; @@ -216,7 +376,7 @@ DSTROOT = /tmp/NGVolumeControl.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = NGVolumeControl/Prefix.pch; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -229,13 +389,95 @@ DSTROOT = /tmp/NGVolumeControl.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = NGVolumeControl/Prefix.pch; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; + A92BA8CF218486AE00BF8486 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Y6BEBK9HD8; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NGVolumeControlKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.nousdigital.NGVolumeControlKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A92BA8D0218486AE00BF8486 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Y6BEBK9HD8; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NGVolumeControlKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.nousdigital.NGVolumeControlKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -257,6 +499,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A92BA8CE218486AE00BF8486 /* Build configuration list for PBXNativeTarget "NGVolumeControlKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A92BA8CF218486AE00BF8486 /* Debug */, + A92BA8D0218486AE00BF8486 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 9BCC745614FCCE8C004F30FF /* Project object */; diff --git a/NGVolumeControl/NGVolumeControl.xcodeproj/xcshareddata/xcschemes/NGVolumeControlKit.xcscheme b/NGVolumeControl/NGVolumeControl.xcodeproj/xcshareddata/xcschemes/NGVolumeControlKit.xcscheme new file mode 100644 index 0000000..6c850c6 --- /dev/null +++ b/NGVolumeControl/NGVolumeControl.xcodeproj/xcshareddata/xcschemes/NGVolumeControlKit.xcscheme @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NGVolumeControl/NGVolumeControl/NGGeometryFunctions.h b/NGVolumeControl/NGVolumeControl/NGGeometryFunctions.h index bfaabde..9557f82 100644 --- a/NGVolumeControl/NGVolumeControl/NGGeometryFunctions.h +++ b/NGVolumeControl/NGVolumeControl/NGGeometryFunctions.h @@ -11,4 +11,4 @@ NS_INLINE CGFloat NGDistanceBetweenCGPoints(CGPoint p1, CGPoint p2) { CGFloat dy = p1.y - p2.y; return (CGFloat)sqrt((double)(dx*dx+dy*dy)); -} \ No newline at end of file +} diff --git a/NGVolumeControl/NGVolumeControl/NGVolumeControl.h b/NGVolumeControl/NGVolumeControl/NGVolumeControl.h index c12cfb2..5618218 100644 --- a/NGVolumeControl/NGVolumeControl/NGVolumeControl.h +++ b/NGVolumeControl/NGVolumeControl/NGVolumeControl.h @@ -5,14 +5,19 @@ // Created by Tretter Matthias on 28.02.12. // Copyright (c) 2012 NOUS Wissensmanagement GmbH. All rights reserved. // - +#import #import "NGVolumeControlExpandDirection.h" +#import "NGVolumeControlDelegate.h" + /** A custom volume control that features a quick-select gesture to change the system volume */ @interface NGVolumeControl : UIControl +/* The delegate to be informed about volume control changes */ +@property (nonatomic, unsafe_unretained) id volumeDelegate; + /** The system volume, between 0.0f and 1.0f */ @property (nonatomic, assign) float volume; @@ -22,18 +27,19 @@ @property (nonatomic, assign) BOOL expanded; /** The height of the expanded volume slider */ -@property (nonatomic, assign) CGFloat sliderHeight; +@property (nonatomic, assign) CGFloat sliderHeight UI_APPEARANCE_SELECTOR; /** The color of the slider track below the current value, defaults to white */ -@property (nonatomic, strong) UIColor *minimumTrackColor; +@property (nonatomic, strong) UIColor *minimumTrackColor UI_APPEARANCE_SELECTOR; /** The color of the slider track above the current value, defaults to gray */ -@property (nonatomic, strong) UIColor *maximumTrackColor; +@property (nonatomic, strong) UIColor *maximumTrackColor UI_APPEARANCE_SELECTOR; /** Prevents the system audio change popup from showing up by adding a (hidden) MPVolumeView to the first window. */ -+ (void)preventSystemVolumePopup; ++ (void)beginPreventingSystemVolumePopup; ++ (void)endPreventingSystemVolumePopup; - (void)setExpanded:(BOOL)expanded animated:(BOOL)animated; diff --git a/NGVolumeControl/NGVolumeControl/NGVolumeControl.m b/NGVolumeControl/NGVolumeControl/NGVolumeControl.m index 4121fc9..3093abd 100644 --- a/NGVolumeControl/NGVolumeControl/NGVolumeControl.m +++ b/NGVolumeControl/NGVolumeControl/NGVolumeControl.m @@ -1,6 +1,9 @@ +#import + #import "NGVolumeControl.h" #import "NGGeometryFunctions.h" #import "NGVolumeControl+NGCustomize.h" +#import "NGVolumeSlider.h" #define NGSystemVolumeDidChangeNotification @"AVSystemController_SystemVolumeDidChangeNotification" @@ -12,12 +15,16 @@ #define kNGMinimumTapSize 44.f +static MPVolumeView *ng_volumeView = nil; + + @interface NGVolumeControl () @property (nonatomic, strong) UIImageView *volumeImageView; @property (nonatomic, strong) UIView *sliderView; -@property (nonatomic, strong) UISlider *slider; +@property (nonatomic, strong) NGVolumeSlider *slider; @property (nonatomic, assign) float systemVolume; +@property (nonatomic, assign) float currentSystemVolume; @property (nonatomic, readonly) BOOL sliderVisible; @property (nonatomic, assign) CGPoint touchStartPoint; @@ -25,40 +32,11 @@ @interface NGVolumeControl () @property (nonatomic, strong) UIImageView *sliderBackgroundView; -- (CGAffineTransform)transformForExpandDirection:(NGVolumeControlExpandDirection)expandDirection; -- (CGRect)volumeViewFrameForExpandDirection:(NGVolumeControlExpandDirection)expandDirection; -- (UIImage *)imageForSliderBackgroundForExpandDirection:(NGVolumeControlExpandDirection)expandDirection; - -- (void)showSliderAnimated:(BOOL)animated; -- (void)hideSliderAnimated:(BOOL)animated; -- (void)toggleSliderAnimated:(BOOL)animated; - -- (void)systemVolumeChanged:(NSNotification *)notification; -- (void)handleSliderValueChanged:(id)sender; - -- (void)updateUI; - -// NGVolumeControl+NGSubclass -- (UIImage *)imageForVolume:(float)volume; -- (void)customizeSlider:(UISlider *)slider; - @end @implementation NGVolumeControl -@synthesize expandDirection = _expandDirection; -@synthesize expanded = _expanded; -@synthesize sliderHeight = _sliderHeight; -@synthesize volumeImageView = _volumeImageView; -@synthesize sliderView = _sliderView; -@synthesize slider = _slider; -@synthesize touchStartPoint = _touchStartPoint; -@synthesize touchesMoved = _touchesMoved; -@synthesize minimumTrackColor = _minimumTrackColor; -@synthesize maximumTrackColor = _maximumTrackColor; -@synthesize sliderBackgroundView = _sliderBackgroundView; - //////////////////////////////////////////////////////////////////////// #pragma mark - Lifecycle //////////////////////////////////////////////////////////////////////// @@ -67,71 +45,80 @@ - (id)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { self.opaque = NO; self.backgroundColor = [UIColor clearColor]; - + _sliderHeight = kNGSliderHeight; _expandDirection = NGVolumeControlExpandDirectionUp; _expanded = NO; _minimumTrackColor = [UIColor whiteColor]; _maximumTrackColor = [UIColor colorWithWhite:1.f alpha:.2f]; - + _touchesMoved = NO; _touchStartPoint = CGPointZero; - + _volumeImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.f, 0.f, 21.f, 23.f)]; _volumeImageView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); _volumeImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; _volumeImageView.contentMode = UIViewContentModeCenter; [self addSubview:_volumeImageView]; - + _sliderView = [[UIView alloc] initWithFrame:[self volumeViewFrameForExpandDirection:_expandDirection]]; _sliderView.backgroundColor = [UIColor clearColor]; _sliderView.contentMode = UIViewContentModeTop; _sliderView.clipsToBounds = YES; _sliderView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - - _sliderBackgroundView = [[UIImageView alloc] initWithFrame:_sliderView.bounds]; + + UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSliderViewPan:)]; + panGesture.cancelsTouchesInView = YES; + panGesture.delaysTouchesBegan = YES; + panGesture.delaysTouchesEnded = YES; + [_sliderView addGestureRecognizer:panGesture]; + + _sliderBackgroundView = [[UIImageView alloc] initWithFrame:CGRectInset(_sliderView.bounds, 10.f, 0.f)]; _sliderBackgroundView.image = [self imageForSliderBackgroundForExpandDirection:_expandDirection]; _sliderBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [_sliderView addSubview:_sliderBackgroundView]; [self hideSliderAnimated:NO]; [self addSubview:_sliderView]; - - - _slider = [[UISlider alloc] initWithFrame:(_expandDirection == NGVolumeControlExpandDirectionUp ? CGRectMake(0.f, 5.f, _sliderHeight - 45.f, kNGSliderWidth) : CGRectMake(0.f, 20.f, _sliderHeight - 45.f, kNGSliderWidth))]; + + _slider = [[NGVolumeSlider alloc] initWithFrame:CGRectMake(0.f, 0.f, _sliderHeight - 30.f, kNGSliderWidth)]; _slider.minimumValue = 0.f; _slider.maximumValue = 1.f; _slider.transform = [self transformForExpandDirection:_expandDirection]; - _slider.center = CGPointMake(_sliderView.frame.size.width/2.f, _sliderView.frame.size.height/2.f); + _slider.center = CGPointMake(_sliderView.frame.size.width/2.f, _sliderView.frame.size.height/2.f - 5.f); _slider.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth; [_slider addTarget:self action:@selector(handleSliderValueChanged:) forControlEvents:UIControlEventValueChanged]; [self customizeSlider:_slider]; + [self setSliderHeight:_sliderHeight]; [_sliderView addSubview:_slider]; - + // set properties of glow Layer CALayer *glowLayer = self.layer; if ([glowLayer respondsToSelector:@selector(setShadowPath:)] && [glowLayer respondsToSelector:@selector(shadowPath)]) { CGMutablePathRef path = CGPathCreateMutable(); - + CGPathAddRect(path, NULL, glowLayer.bounds); glowLayer.shadowPath = path; glowLayer.shadowOffset = CGSizeZero; glowLayer.shadowColor = [UIColor whiteColor].CGColor; glowLayer.shadowRadius = kNGShadowRadius; - + CGPathRelease(path); } + self.currentSystemVolume = self.systemVolume; + // observe changes to system volume (volume buttons) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemVolumeChanged:) name:NGSystemVolumeDidChangeNotification object:nil]; } - + return self; } - (void)dealloc { + _volumeDelegate = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:NGSystemVolumeDidChangeNotification object:nil]; } @@ -139,19 +126,27 @@ - (void)dealloc { #pragma mark - Class Methods //////////////////////////////////////////////////////////////////////// -+ (void)preventSystemVolumePopup { - // Prevent Audio-Change Popus - MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-2000., -2000., 0.f, 0.f)]; - NSArray *windows = [UIApplication sharedApplication].windows; - - volumeView.alpha = 0.1f; - volumeView.userInteractionEnabled = NO; - - if (windows.count > 0) { - [[windows objectAtIndex:0] addSubview:volumeView]; ++ (void)beginPreventingSystemVolumePopup { + if (ng_volumeView == nil) { + // Prevent Audio-Change Popus + ng_volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-2000., -2000., 0.f, 0.f)]; + ng_volumeView.alpha = 0.1f; + ng_volumeView.userInteractionEnabled = NO; + } + + if (ng_volumeView.superview == nil) { + NSArray *windows = [UIApplication sharedApplication].windows; + + if (windows.count > 0) { + [[windows objectAtIndex:0] addSubview:ng_volumeView]; + } } } ++ (void)endPreventingSystemVolumePopup { + [ng_volumeView removeFromSuperview]; +} + //////////////////////////////////////////////////////////////////////// #pragma mark - UIView //////////////////////////////////////////////////////////////////////// @@ -172,22 +167,22 @@ - (void)willMoveToWindow:(UIWindow *)newWindow { - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { CGRect rectToTest = self.bounds; - + // if the real bounds are a too small hit-target increase the artificial hit-bounds rect if (self.bounds.size.width < kNGMinimumTapSize && self.bounds.size.height < kNGMinimumTapSize) { rectToTest = CGRectInset(self.bounds, (self.bounds.size.width - kNGMinimumTapSize)/2.f, (self.bounds.size.height - kNGMinimumTapSize)/2.f); } - + // if the slider is expanded we also have to take the sliderView into account BOOL inside = (CGRectContainsPoint(rectToTest, point) || (self.sliderVisible && CGRectContainsPoint(self.sliderView.frame, point))); - + if (!inside) { [self setExpanded:NO animated:YES]; } - + return inside; } @@ -196,8 +191,8 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { //////////////////////////////////////////////////////////////////////// - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { - self.touchStartPoint = [touch locationInView:self]; - + [self handleTouchesBeganAtPoint:[touch locationInView:self]]; + if (!self.expanded) { [self setExpanded:YES animated:YES]; self.touchesMoved = NO; @@ -205,35 +200,17 @@ - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { } else { [self setExpanded:NO animated:YES]; } - + return YES; } - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { if (self.expanded) { CGPoint point = [touch locationInView:self.sliderView]; - CGFloat distance = NGDistanceBetweenCGPoints(point, self.touchStartPoint); - - // check if we moved the touches because we automatically collapse when the touches - // end when the user moved the touches - if (distance > kNGMinimumSlideDistance) { - self.touchesMoved = YES; - } - - if (point.y > self.sliderHeight) { - point.y = self.sliderHeight; - } - - CGFloat percentage = point.y/self.sliderHeight; - - if (self.expandDirection == NGVolumeControlExpandDirectionUp) { - percentage = 1.f - percentage; - } - - self.slider.value = percentage; - self.volume = percentage; + + [self handleTouchesMovedToPoint:point]; } - + return YES; } @@ -243,7 +220,7 @@ - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { [self setExpanded:NO animated:YES]; self.touchesMoved = NO; } - + self.slider.userInteractionEnabled = YES; } @@ -254,10 +231,10 @@ - (void)cancelTrackingWithEvent:(UIEvent *)event { - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; - + // show glow of control like UIButton (showsTouchWhenHighlighted) CALayer *glowLayer = self.layer; - + if ([glowLayer respondsToSelector:@selector(setShadowPath:)] && [glowLayer respondsToSelector:@selector(shadowPath)]) { if (highlighted || self.touchesMoved) { glowLayer.shadowOpacity = 0.9f; @@ -274,10 +251,10 @@ - (void)setHighlighted:(BOOL)highlighted { - (void)setVolume:(float)volume { float maxBound = MIN(volume, 1.f); float boundedVolume = MAX(maxBound, 0.f); - + // update the system volume self.systemVolume = boundedVolume; - + // system volume doesn't work on the simulator, so for testing purposes we // set the slider/image directly instead of using system volume as in updateUI #if TARGET_IPHONE_SIMULATOR @@ -286,7 +263,7 @@ - (void)setVolume:(float)volume { #else [self updateUI]; #endif - + [self sendActionsForControlEvents:UIControlEventValueChanged]; } @@ -301,10 +278,12 @@ - (void)setExpanded:(BOOL)expanded { - (void)setExpanded:(BOOL)expanded animated:(BOOL)animated { if (expanded != _expanded) { _expanded = expanded; - + if (expanded) { + [[self class] beginPreventingSystemVolumePopup]; [self showSliderAnimated:animated]; } else { + [[self class] endPreventingSystemVolumePopup]; [self hideSliderAnimated:animated]; } } @@ -313,10 +292,10 @@ - (void)setExpanded:(BOOL)expanded animated:(BOOL)animated { - (void)setExpandDirection:(NGVolumeControlExpandDirection)expandDirection { if (expandDirection != _expandDirection) { _expandDirection = expandDirection; - + self.slider.transform = [self transformForExpandDirection:expandDirection]; self.sliderView.frame = [self volumeViewFrameForExpandDirection:expandDirection]; - + self.sliderBackgroundView.image = [self imageForSliderBackgroundForExpandDirection:expandDirection]; } } @@ -324,7 +303,7 @@ - (void)setExpandDirection:(NGVolumeControlExpandDirection)expandDirection { - (void)setMinimumTrackColor:(UIColor *)minimumTrackColor { if (minimumTrackColor != _minimumTrackColor) { _minimumTrackColor = minimumTrackColor; - + [self customizeSlider:self.slider]; } } @@ -332,7 +311,7 @@ - (void)setMinimumTrackColor:(UIColor *)minimumTrackColor { - (void)setMaximumTrackColor:(UIColor *)maximumTrackColor { if (maximumTrackColor != _maximumTrackColor) { _maximumTrackColor = maximumTrackColor; - + [self customizeSlider:self.slider]; } } @@ -340,16 +319,19 @@ - (void)setMaximumTrackColor:(UIColor *)maximumTrackColor { - (void)setSliderHeight:(CGFloat)sliderHeight { if (sliderHeight != _sliderHeight) { _sliderHeight = sliderHeight; - - // Update UI to new height - [self hideSliderAnimated:NO]; - self.sliderView.frame = [self volumeViewFrameForExpandDirection:self.expandDirection]; - - self.slider.transform = CGAffineTransformIdentity; - self.slider.frame = (_expandDirection == NGVolumeControlExpandDirectionUp ? CGRectMake(0.f, 5.f, _sliderHeight - 45.f, kNGSliderWidth) : CGRectMake(0.f, 20.f, _sliderHeight - 45.f, kNGSliderWidth)); - self.slider.transform = [self transformForExpandDirection:self.expandDirection]; - self.slider.center = CGPointMake(self.sliderView.frame.size.width/2.f, self.sliderView.frame.size.height/2.f); } + + CGFloat offset = _expandDirection == NGVolumeControlExpandDirectionDown ? -5.f : 5.f; + CGFloat horizontalOffset = _expandDirection == NGVolumeControlExpandDirectionDown ? 0.5f : 0.f; + + // Update UI to new height + [self hideSliderAnimated:NO]; + self.sliderView.frame = [self volumeViewFrameForExpandDirection:self.expandDirection]; + + self.slider.transform = CGAffineTransformIdentity; + self.slider.frame = (CGRect){CGPointZero, (CGSize){_sliderHeight - 30.f, kNGSliderWidth}}; + self.slider.transform = [self transformForExpandDirection:self.expandDirection]; + self.slider.center = CGPointMake(self.sliderView.frame.size.width/2.f + horizontalOffset, self.sliderView.frame.size.height/2.f - offset); } //////////////////////////////////////////////////////////////////////// @@ -376,7 +358,7 @@ - (void)customizeSlider:(UISlider *)slider { } else { //Build a rect of appropriate size at origin 0,0 CGRect fillRect = CGRectMake(0.f,0.f,1.f,10.f); - + //Create a context of the appropriate size UIGraphicsBeginImageContext(CGSizeMake(1.f, 10.f)); CGContextRef currentContext = UIGraphicsGetCurrentContext(); @@ -388,7 +370,7 @@ - (void)customizeSlider:(UISlider *)slider { UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.slider setMinimumTrackImage:image forState:UIControlStateNormal]; - + //Create a context of the appropriate size UIGraphicsBeginImageContext(CGSizeMake(1.f, 10.f)); currentContext = UIGraphicsGetCurrentContext(); @@ -400,8 +382,8 @@ - (void)customizeSlider:(UISlider *)slider { image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.slider setMaximumTrackImage:image forState:UIControlStateNormal]; - } - + } + [self.slider setThumbImage:[UIImage imageNamed:@"NGVolumeControl.bundle/ScrubberKnob"] forState:UIControlStateNormal]; } @@ -410,13 +392,53 @@ - (void)customizeSlider:(UISlider *)slider { //////////////////////////////////////////////////////////////////////// - (void)setSystemVolume:(float)systemVolume { - MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer]; + MPMusicPlayerController *musicPlayer = [MPMusicPlayerController systemMusicPlayer]; + + if ([self.volumeDelegate respondsToSelector:@selector(volumeControl:didChangeOldVolume:toNewVolume:)]) { + [self.volumeDelegate volumeControl:self didChangeOldVolume:musicPlayer.volume toNewVolume:systemVolume]; + } + musicPlayer.volume = systemVolume; + self.currentSystemVolume = systemVolume; + + // TODO: Replace with new version below +// MPVolumeView *volumeView = [[MPVolumeView alloc] init]; +// UISlider *volumeViewSlider = nil; +// +// for (UIView *view in volumeView.subviews) { +// if ([view isKindOfClass:[UISlider class]]) { +// volumeViewSlider = (UISlider *)view; +// break; +// } +// } +// +// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ +// if ([self.volumeDelegate respondsToSelector:@selector(volumeControl:didChangeOldVolume:toNewVolume:)]) { +// [self.volumeDelegate volumeControl:self didChangeOldVolume:volumeViewSlider.value toNewVolume:systemVolume]; +// } +// +// volumeViewSlider.value = systemVolume; +// }); +// +// self.currentSystemVolume = systemVolume; } - (float)systemVolume { - MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer]; + MPMusicPlayerController *musicPlayer = [MPMusicPlayerController systemMusicPlayer]; return musicPlayer.volume; + + // TODO: Replace with new version below +// MPVolumeView *volumeView = [[MPVolumeView alloc] init]; +// UISlider *volumeViewSlider = nil; +// +// for (UIView *view in volumeView.subviews) { +// if ([view isKindOfClass:[UISlider class]]) { +// volumeViewSlider = (UISlider *)view; +// break; +// } +// } +// +// return volumeViewSlider.value; } - (CGAffineTransform)transformForExpandDirection:(NGVolumeControlExpandDirection)expandDirection { @@ -428,25 +450,33 @@ - (CGAffineTransform)transformForExpandDirection:(NGVolumeControlExpandDirection } - (CGRect)volumeViewFrameForExpandDirection:(NGVolumeControlExpandDirection)expandDirection { + CGRect frame = CGRectZero; + if (expandDirection == NGVolumeControlExpandDirectionUp) { - return CGRectMake(0, -self.sliderHeight, self.bounds.size.width, self.sliderHeight); + frame = CGRectMake(0.f, -self.sliderHeight, self.bounds.size.width, self.sliderHeight); } else { - return CGRectMake(0, self.bounds.size.height, self.bounds.size.width, self.sliderHeight); + frame = CGRectMake(0.f, self.bounds.size.height, self.bounds.size.width, self.sliderHeight); } + + return CGRectInset(frame,-10.f,0.f); } - (UIImage *)imageForSliderBackgroundForExpandDirection:(NGVolumeControlExpandDirection)expandDirection { - UIImage *sliderBackgroundImage = [UIImage imageNamed:@"NGVolumeControl.bundle/volume-background.png"]; - + UIImage *sliderBackgroundImage = [UIImage imageNamed:@"NGVolumeControl.bundle/volume-background"]; + + // mirror image if slider expands downwards if (expandDirection == NGVolumeControlExpandDirectionDown) { - sliderBackgroundImage = [[UIImage alloc] initWithCGImage:sliderBackgroundImage.CGImage scale:2.0 orientation:UIImageOrientationDown]; + sliderBackgroundImage = [[UIImage alloc] initWithCGImage:sliderBackgroundImage.CGImage + scale:[UIScreen mainScreen].scale + orientation:UIImageOrientationDown]; } + if ([sliderBackgroundImage respondsToSelector:@selector(resizableImageWithCapInsets:)]) { sliderBackgroundImage = [sliderBackgroundImage resizableImageWithCapInsets:UIEdgeInsetsMake(47.f, 24.f, 47.f, 24.f)]; } else { sliderBackgroundImage = [sliderBackgroundImage stretchableImageWithLeftCapWidth:24 topCapHeight:47]; } - + return sliderBackgroundImage; } @@ -466,27 +496,31 @@ - (void)showSliderAnimated:(BOOL)animated { if (self.sliderVisible) { return; } - + + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlWillExpand:)]) { + [self.volumeDelegate volumeControlWillExpand:self]; + } + if (animated) { CGRect frame = self.sliderView.frame; - + frame.size.height = 0.f; - + if (self.expandDirection == NGVolumeControlExpandDirectionUp) { frame.origin.y = 0.f; } else { frame.origin.y = self.bounds.size.height; } - + self.sliderView.frame = frame; self.sliderView.alpha = 0.f; - + frame.size.height = self.sliderHeight; - + if (self.expandDirection == NGVolumeControlExpandDirectionUp) { frame.origin.y = -self.sliderHeight; - } - + } + [UIView animateWithDuration:kNGSlideDuration delay:0. options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction @@ -494,10 +528,15 @@ - (void)showSliderAnimated:(BOOL)animated { self.sliderView.frame = frame; self.sliderView.alpha = 1.f; } completion:^(BOOL finished) { - + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlDidExpand:)]) { + [self.volumeDelegate volumeControlDidExpand:self]; + } }]; } else { self.sliderView.alpha = 1.f; + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlDidExpand:)]) { + [self.volumeDelegate volumeControlDidExpand:self]; + } } } @@ -505,20 +544,24 @@ - (void)hideSliderAnimated:(BOOL)animated { if (!self.sliderVisible) { return; } - + + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlWillShrink:)]) { + [self.volumeDelegate volumeControlWillShrink:self]; + } + if (animated) { CGRect frame = self.sliderView.frame; - + self.sliderView.alpha = 1.f; - + frame.size.height = 0.f; - + if (self.expandDirection == NGVolumeControlExpandDirectionUp) { frame.origin.y = 0.f; } else { frame.origin.y = self.bounds.size.height; } - + [UIView animateWithDuration:kNGSlideDuration delay:0. options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction @@ -526,10 +569,15 @@ - (void)hideSliderAnimated:(BOOL)animated { self.sliderView.frame = frame; self.sliderView.alpha = 0.f; } completion:^(BOOL finished) { - + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlDidShrink:)]) { + [self.volumeDelegate volumeControlDidShrink:self]; + } }]; } else { self.sliderView.alpha = 0.f; + if ([self.volumeDelegate respondsToSelector:@selector(volumeControlDidShrink:)]) { + [self.volumeDelegate volumeControlDidShrink:self]; + } } } @@ -541,6 +589,11 @@ - (void)updateUI { - (void)systemVolumeChanged:(NSNotification *)notification { // we update the UI when the system volume changed (volume buttons) + if ([self.volumeDelegate respondsToSelector:@selector(volumeControl:didChangeOldVolume:toNewVolume:)]) { + float newVolume = [notification.userInfo[@"AVSystemController_AudioVolumeNotificationParameter"] floatValue]; + [self.volumeDelegate volumeControl:self didChangeOldVolume:self.currentSystemVolume toNewVolume:newVolume]; + self.currentSystemVolume = newVolume; + } [self updateUI]; } @@ -548,4 +601,43 @@ - (void)handleSliderValueChanged:(id)sender { self.volume = self.slider.value; } +- (void)handleSliderViewPan:(UIPanGestureRecognizer *)panGesture { + if (panGesture.state == UIGestureRecognizerStateBegan) { + [self handleTouchesBeganAtPoint:[panGesture locationInView:self.sliderView]]; + } + + else if (panGesture.state == UIGestureRecognizerStateChanged) { + CGPoint point = [panGesture locationInView:self.sliderView]; + + [self handleTouchesMovedToPoint:point]; + } +} + +- (void)handleTouchesBeganAtPoint:(CGPoint)point { + self.touchStartPoint = point; +} + +- (void)handleTouchesMovedToPoint:(CGPoint)point { + CGFloat distance = NGDistanceBetweenCGPoints(point, self.touchStartPoint); + + // check if we moved the touches because we automatically collapse when the touches + // end when the user moved the touches + if (distance > kNGMinimumSlideDistance) { + self.touchesMoved = YES; + } + + if (point.y > self.sliderHeight) { + point.y = self.sliderHeight; + } + + CGFloat percentage = point.y/self.sliderHeight; + + if (self.expandDirection == NGVolumeControlExpandDirectionUp) { + percentage = 1.f - percentage; + } + + self.slider.value = percentage; + self.volume = percentage; +} + @end diff --git a/NGVolumeControl/NGVolumeControl/NGVolumeControlDelegate.h b/NGVolumeControl/NGVolumeControl/NGVolumeControlDelegate.h new file mode 100644 index 0000000..560e598 --- /dev/null +++ b/NGVolumeControl/NGVolumeControl/NGVolumeControlDelegate.h @@ -0,0 +1,25 @@ +// +// NGVolumeControlDelegate.h +// NGVolumeControl +// +// Created by Matthias Tretter on 15.10.12. +// Copyright (c) 2012 NOUS Wissensmanagement GmbH. All rights reserved. +// + +#import + + +@class NGVolumeControl; + + +@protocol NGVolumeControlDelegate + +@optional + +- (void)volumeControlWillExpand:(NGVolumeControl *)volumeControl; +- (void)volumeControlDidExpand:(NGVolumeControl *)volumeControl; +- (void)volumeControlWillShrink:(NGVolumeControl *)volumeControl; +- (void)volumeControlDidShrink:(NGVolumeControl *)volumeControl; +- (void)volumeControl:(NGVolumeControl *)volumeControl didChangeOldVolume:(float)oldVolume toNewVolume:(float)newVolume; + +@end diff --git a/NGVolumeControl/NGVolumeControl/NGVolumeSlider.h b/NGVolumeControl/NGVolumeControl/NGVolumeSlider.h new file mode 100644 index 0000000..8d0203a --- /dev/null +++ b/NGVolumeControl/NGVolumeControl/NGVolumeSlider.h @@ -0,0 +1,15 @@ +// +// NGVolumeSlider.h +// NGVolumeControl +// +// Created by Matthias Tretter on 15.10.12. +// Copyright (c) 2012 NOUS Wissensmanagement GmbH. All rights reserved. +// + +#import + + +/** Custom subclass to increase touch area */ +@interface NGVolumeSlider : UISlider + +@end diff --git a/NGVolumeControl/NGVolumeControl/NGVolumeSlider.m b/NGVolumeControl/NGVolumeControl/NGVolumeSlider.m new file mode 100644 index 0000000..104380a --- /dev/null +++ b/NGVolumeControl/NGVolumeControl/NGVolumeSlider.m @@ -0,0 +1,24 @@ +// +// NGVolumeSlider.m +// NGVolumeControl +// +// Created by Matthias Tretter on 15.10.12. +// Copyright (c) 2012 NOUS Wissensmanagement GmbH. All rights reserved. +// + +#import "NGVolumeSlider.h" + +@implementation NGVolumeSlider + +//////////////////////////////////////////////////////////////////////// +#pragma mark - UIView +//////////////////////////////////////////////////////////////////////// + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + CGRect bounds = CGRectInset(self.bounds, -10.f, -30.f); + //bounds = CGRectApplyAffineTransform(bounds, self.transform); + + return CGRectContainsPoint(bounds, point); +} + +@end diff --git a/NGVolumeControl/NGVolumeControlKit/Info.plist b/NGVolumeControl/NGVolumeControlKit/Info.plist new file mode 100644 index 0000000..e1fe4cf --- /dev/null +++ b/NGVolumeControl/NGVolumeControlKit/Info.plist @@ -0,0 +1,22 @@ + + + + + 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) + + diff --git a/NGVolumeControl/NGVolumeControlKit/NGVolumeControlKit.h b/NGVolumeControl/NGVolumeControlKit/NGVolumeControlKit.h new file mode 100644 index 0000000..e4b69a8 --- /dev/null +++ b/NGVolumeControl/NGVolumeControlKit/NGVolumeControlKit.h @@ -0,0 +1,23 @@ +// +// NGVolumeControlKit.h +// NGVolumeControlKit +// +// Created by Juergen Falb on 27.10.18. +// Copyright © 2018 NOUS Wissensmanagement GmbH. All rights reserved. +// + +#import + +//! Project version number for NGVolumeControlKit. +FOUNDATION_EXPORT double NGVolumeControlKitVersionNumber; + +//! Project version string for NGVolumeControlKit. +FOUNDATION_EXPORT const unsigned char NGVolumeControlKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import diff --git a/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob.png b/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob.png index 02b491c..5e4e1a3 100644 Binary files a/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob.png and b/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob.png differ diff --git a/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob@2x.png b/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob@2x.png index 169c9bc..3e3aa60 100644 Binary files a/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob@2x.png and b/NGVolumeControl/Resources/NGVolumeControl.bundle/ScrubberKnob@2x.png differ diff --git a/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background.png b/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background.png index 83bfbca..2ff7d49 100644 Binary files a/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background.png and b/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background.png differ diff --git a/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background@2x.png b/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background@2x.png index 4e23606..1b19460 100644 Binary files a/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background@2x.png and b/NGVolumeControl/Resources/NGVolumeControl.bundle/volume-background@2x.png differ diff --git a/NGVolumeControlKit.podspec b/NGVolumeControlKit.podspec new file mode 100644 index 0000000..5a6b20c --- /dev/null +++ b/NGVolumeControlKit.podspec @@ -0,0 +1,24 @@ +Pod::Spec.new do |s| + s.platform = :ios, '8.0' + s.name = 'NGVolumeControlKit' + s.version = '1.0.3' + s.license = '' + s.summary = 'A custom (and customizable) volume control for iOS.' + s.description = 'A custom (and customizable) volume control for iOS with a quick-gesture to change volume (touch down, slide finger, touch up).' + s.homepage = "http://pocketscience.com/" + s.source = { :git => 'https://github.com/NOUSguide/NGVolumeControl.git', :tag => '1.0.3' } + s.source_files = 'NGVolumeControl/*/*.{h,m}' + s.public_header_files = 'NGVolumeControl/*/*.h', 'NGVolumeControlKit/NGVolumeControlKit.h' + s.resource = 'NGVolumeControl/Resources/NGVolumeControl.bundle' + s.requires_arc = true + s.frameworks = 'Foundation', 'MediaPlayer' + s.authors = { 'PocketScience GmbH' => 'office@pocketscience.com'} + + s.prefix_header_contents = ' +#ifdef __OBJC__ + #import + #import + #import +#endif + ' +end