├── .gitignore ├── Example ├── Resources │ ├── dotActive.png │ ├── dotActive@2x.png │ ├── dotActive@3x.png │ ├── dotInactive.png │ ├── dotInactive@2x.png │ ├── dotInactive@3x.png │ ├── image1.jpg │ ├── image2.jpg │ ├── image3.jpg │ ├── image4.jpg │ ├── image5.jpg │ └── image6.jpg ├── TAPageControl.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── TAPageControl │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── TAExampleDotView.h │ ├── TAExampleDotView.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── TAPageControlTests │ ├── Info.plist │ └── TAPageControlTests.m ├── LICENSE ├── README.md ├── TAPageControl.podspec └── TAPageControl ├── TAAbstractDotView.h ├── TAAbstractDotView.m ├── TAAnimatedDotView.h ├── TAAnimatedDotView.m ├── TADotView.h ├── TADotView.m ├── TAPageControl.h └── TAPageControl.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /Example/Resources/dotActive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotActive.png -------------------------------------------------------------------------------- /Example/Resources/dotActive@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotActive@2x.png -------------------------------------------------------------------------------- /Example/Resources/dotActive@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotActive@3x.png -------------------------------------------------------------------------------- /Example/Resources/dotInactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotInactive.png -------------------------------------------------------------------------------- /Example/Resources/dotInactive@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotInactive@2x.png -------------------------------------------------------------------------------- /Example/Resources/dotInactive@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/dotInactive@3x.png -------------------------------------------------------------------------------- /Example/Resources/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image1.jpg -------------------------------------------------------------------------------- /Example/Resources/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image2.jpg -------------------------------------------------------------------------------- /Example/Resources/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image3.jpg -------------------------------------------------------------------------------- /Example/Resources/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image4.jpg -------------------------------------------------------------------------------- /Example/Resources/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image5.jpg -------------------------------------------------------------------------------- /Example/Resources/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TanguyAladenise/TAPageControl/124beae574c10a3d8315e44243a5a9789248af76/Example/Resources/image6.jpg -------------------------------------------------------------------------------- /Example/TAPageControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E51D98C1A72CFEE0035C305 /* TAAbstractDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E51D9851A72CFEE0035C305 /* TAAbstractDotView.m */; }; 11 | 5E51D98D1A72CFEE0035C305 /* TAAnimatedDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E51D9871A72CFEE0035C305 /* TAAnimatedDotView.m */; }; 12 | 5E51D98E1A72CFEE0035C305 /* TADotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E51D9891A72CFEE0035C305 /* TADotView.m */; }; 13 | 5E51D98F1A72CFEE0035C305 /* TAPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E51D98B1A72CFEE0035C305 /* TAPageControl.m */; }; 14 | 5E51D9971A72D00A0035C305 /* dotActive.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9911A72D00A0035C305 /* dotActive.png */; }; 15 | 5E51D9981A72D00A0035C305 /* dotActive@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9921A72D00A0035C305 /* dotActive@2x.png */; }; 16 | 5E51D9991A72D00A0035C305 /* dotActive@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9931A72D00A0035C305 /* dotActive@3x.png */; }; 17 | 5E51D99A1A72D00A0035C305 /* dotInactive.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9941A72D00A0035C305 /* dotInactive.png */; }; 18 | 5E51D99B1A72D00A0035C305 /* dotInactive@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9951A72D00A0035C305 /* dotInactive@2x.png */; }; 19 | 5E51D99C1A72D00A0035C305 /* dotInactive@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E51D9961A72D00A0035C305 /* dotInactive@3x.png */; }; 20 | 5E51D99F1A72E5960035C305 /* TAExampleDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E51D99E1A72E5960035C305 /* TAExampleDotView.m */; }; 21 | 5E619A5A1A7042AF00288DF1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E619A591A7042AF00288DF1 /* main.m */; }; 22 | 5E619A5D1A7042AF00288DF1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E619A5C1A7042AF00288DF1 /* AppDelegate.m */; }; 23 | 5E619A601A7042AF00288DF1 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E619A5F1A7042AF00288DF1 /* ViewController.m */; }; 24 | 5E619A631A7042AF00288DF1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E619A611A7042AF00288DF1 /* Main.storyboard */; }; 25 | 5E619A651A7042AF00288DF1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E619A641A7042AF00288DF1 /* Images.xcassets */; }; 26 | 5E619A681A7042AF00288DF1 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5E619A661A7042AF00288DF1 /* LaunchScreen.xib */; }; 27 | 5E619A741A7042AF00288DF1 /* TAPageControlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E619A731A7042AF00288DF1 /* TAPageControlTests.m */; }; 28 | 5EE840171A734149000CB3CA /* image1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840111A734149000CB3CA /* image1.jpg */; }; 29 | 5EE840181A734149000CB3CA /* image2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840121A734149000CB3CA /* image2.jpg */; }; 30 | 5EE840191A734149000CB3CA /* image3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840131A734149000CB3CA /* image3.jpg */; }; 31 | 5EE8401A1A734149000CB3CA /* image4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840141A734149000CB3CA /* image4.jpg */; }; 32 | 5EE8401B1A734149000CB3CA /* image5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840151A734149000CB3CA /* image5.jpg */; }; 33 | 5EE8401C1A734149000CB3CA /* image6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EE840161A734149000CB3CA /* image6.jpg */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | 5E619A6E1A7042AF00288DF1 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 5E619A4C1A7042AF00288DF1 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = 5E619A531A7042AF00288DF1; 42 | remoteInfo = TAPageControl; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 5E51D9841A72CFEE0035C305 /* TAAbstractDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAAbstractDotView.h; sourceTree = ""; }; 48 | 5E51D9851A72CFEE0035C305 /* TAAbstractDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAAbstractDotView.m; sourceTree = ""; }; 49 | 5E51D9861A72CFEE0035C305 /* TAAnimatedDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAAnimatedDotView.h; sourceTree = ""; }; 50 | 5E51D9871A72CFEE0035C305 /* TAAnimatedDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAAnimatedDotView.m; sourceTree = ""; }; 51 | 5E51D9881A72CFEE0035C305 /* TADotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TADotView.h; sourceTree = ""; }; 52 | 5E51D9891A72CFEE0035C305 /* TADotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TADotView.m; sourceTree = ""; }; 53 | 5E51D98A1A72CFEE0035C305 /* TAPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAPageControl.h; sourceTree = ""; }; 54 | 5E51D98B1A72CFEE0035C305 /* TAPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAPageControl.m; sourceTree = ""; }; 55 | 5E51D9911A72D00A0035C305 /* dotActive.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dotActive.png; sourceTree = ""; }; 56 | 5E51D9921A72D00A0035C305 /* dotActive@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dotActive@2x.png"; sourceTree = ""; }; 57 | 5E51D9931A72D00A0035C305 /* dotActive@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dotActive@3x.png"; sourceTree = ""; }; 58 | 5E51D9941A72D00A0035C305 /* dotInactive.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dotInactive.png; sourceTree = ""; }; 59 | 5E51D9951A72D00A0035C305 /* dotInactive@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dotInactive@2x.png"; sourceTree = ""; }; 60 | 5E51D9961A72D00A0035C305 /* dotInactive@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dotInactive@3x.png"; sourceTree = ""; }; 61 | 5E51D99D1A72E5960035C305 /* TAExampleDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAExampleDotView.h; sourceTree = ""; }; 62 | 5E51D99E1A72E5960035C305 /* TAExampleDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAExampleDotView.m; sourceTree = ""; }; 63 | 5E619A541A7042AF00288DF1 /* TAPageControl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TAPageControl.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 5E619A581A7042AF00288DF1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 5E619A591A7042AF00288DF1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 66 | 5E619A5B1A7042AF00288DF1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 67 | 5E619A5C1A7042AF00288DF1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 68 | 5E619A5E1A7042AF00288DF1 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 69 | 5E619A5F1A7042AF00288DF1 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 70 | 5E619A621A7042AF00288DF1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 71 | 5E619A641A7042AF00288DF1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 72 | 5E619A671A7042AF00288DF1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 73 | 5E619A6D1A7042AF00288DF1 /* TAPageControlTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TAPageControlTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 5E619A721A7042AF00288DF1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 5E619A731A7042AF00288DF1 /* TAPageControlTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TAPageControlTests.m; sourceTree = ""; }; 76 | 5EE840111A734149000CB3CA /* image1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image1.jpg; sourceTree = ""; }; 77 | 5EE840121A734149000CB3CA /* image2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image2.jpg; sourceTree = ""; }; 78 | 5EE840131A734149000CB3CA /* image3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image3.jpg; sourceTree = ""; }; 79 | 5EE840141A734149000CB3CA /* image4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image4.jpg; sourceTree = ""; }; 80 | 5EE840151A734149000CB3CA /* image5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image5.jpg; sourceTree = ""; }; 81 | 5EE840161A734149000CB3CA /* image6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image6.jpg; sourceTree = ""; }; 82 | /* End PBXFileReference section */ 83 | 84 | /* Begin PBXFrameworksBuildPhase section */ 85 | 5E619A511A7042AF00288DF1 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | 5E619A6A1A7042AF00288DF1 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 5E51D9831A72CFEE0035C305 /* TAPageControl */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 5E51D9841A72CFEE0035C305 /* TAAbstractDotView.h */, 106 | 5E51D9851A72CFEE0035C305 /* TAAbstractDotView.m */, 107 | 5E51D9861A72CFEE0035C305 /* TAAnimatedDotView.h */, 108 | 5E51D9871A72CFEE0035C305 /* TAAnimatedDotView.m */, 109 | 5E51D9881A72CFEE0035C305 /* TADotView.h */, 110 | 5E51D9891A72CFEE0035C305 /* TADotView.m */, 111 | 5E51D98A1A72CFEE0035C305 /* TAPageControl.h */, 112 | 5E51D98B1A72CFEE0035C305 /* TAPageControl.m */, 113 | ); 114 | name = TAPageControl; 115 | path = ../../TAPageControl; 116 | sourceTree = ""; 117 | }; 118 | 5E51D9901A72D00A0035C305 /* Resources */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 5EE840111A734149000CB3CA /* image1.jpg */, 122 | 5EE840121A734149000CB3CA /* image2.jpg */, 123 | 5EE840131A734149000CB3CA /* image3.jpg */, 124 | 5EE840141A734149000CB3CA /* image4.jpg */, 125 | 5EE840151A734149000CB3CA /* image5.jpg */, 126 | 5EE840161A734149000CB3CA /* image6.jpg */, 127 | 5E51D9911A72D00A0035C305 /* dotActive.png */, 128 | 5E51D9921A72D00A0035C305 /* dotActive@2x.png */, 129 | 5E51D9931A72D00A0035C305 /* dotActive@3x.png */, 130 | 5E51D9941A72D00A0035C305 /* dotInactive.png */, 131 | 5E51D9951A72D00A0035C305 /* dotInactive@2x.png */, 132 | 5E51D9961A72D00A0035C305 /* dotInactive@3x.png */, 133 | ); 134 | path = Resources; 135 | sourceTree = SOURCE_ROOT; 136 | }; 137 | 5E619A4B1A7042AF00288DF1 = { 138 | isa = PBXGroup; 139 | children = ( 140 | 5E619A561A7042AF00288DF1 /* TAPageControl */, 141 | 5E619A701A7042AF00288DF1 /* TAPageControlTests */, 142 | 5E619A551A7042AF00288DF1 /* Products */, 143 | ); 144 | sourceTree = ""; 145 | }; 146 | 5E619A551A7042AF00288DF1 /* Products */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 5E619A541A7042AF00288DF1 /* TAPageControl.app */, 150 | 5E619A6D1A7042AF00288DF1 /* TAPageControlTests.xctest */, 151 | ); 152 | name = Products; 153 | sourceTree = ""; 154 | }; 155 | 5E619A561A7042AF00288DF1 /* TAPageControl */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 5E51D9901A72D00A0035C305 /* Resources */, 159 | 5E51D9831A72CFEE0035C305 /* TAPageControl */, 160 | 5E619A5B1A7042AF00288DF1 /* AppDelegate.h */, 161 | 5E619A5C1A7042AF00288DF1 /* AppDelegate.m */, 162 | 5E619A5E1A7042AF00288DF1 /* ViewController.h */, 163 | 5E619A5F1A7042AF00288DF1 /* ViewController.m */, 164 | 5E51D99D1A72E5960035C305 /* TAExampleDotView.h */, 165 | 5E51D99E1A72E5960035C305 /* TAExampleDotView.m */, 166 | 5E619A611A7042AF00288DF1 /* Main.storyboard */, 167 | 5E619A641A7042AF00288DF1 /* Images.xcassets */, 168 | 5E619A661A7042AF00288DF1 /* LaunchScreen.xib */, 169 | 5E619A571A7042AF00288DF1 /* Supporting Files */, 170 | ); 171 | path = TAPageControl; 172 | sourceTree = ""; 173 | }; 174 | 5E619A571A7042AF00288DF1 /* Supporting Files */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 5E619A581A7042AF00288DF1 /* Info.plist */, 178 | 5E619A591A7042AF00288DF1 /* main.m */, 179 | ); 180 | name = "Supporting Files"; 181 | sourceTree = ""; 182 | }; 183 | 5E619A701A7042AF00288DF1 /* TAPageControlTests */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 5E619A731A7042AF00288DF1 /* TAPageControlTests.m */, 187 | 5E619A711A7042AF00288DF1 /* Supporting Files */, 188 | ); 189 | path = TAPageControlTests; 190 | sourceTree = ""; 191 | }; 192 | 5E619A711A7042AF00288DF1 /* Supporting Files */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 5E619A721A7042AF00288DF1 /* Info.plist */, 196 | ); 197 | name = "Supporting Files"; 198 | sourceTree = ""; 199 | }; 200 | /* End PBXGroup section */ 201 | 202 | /* Begin PBXNativeTarget section */ 203 | 5E619A531A7042AF00288DF1 /* TAPageControl */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = 5E619A771A7042AF00288DF1 /* Build configuration list for PBXNativeTarget "TAPageControl" */; 206 | buildPhases = ( 207 | 5E619A501A7042AF00288DF1 /* Sources */, 208 | 5E619A511A7042AF00288DF1 /* Frameworks */, 209 | 5E619A521A7042AF00288DF1 /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | ); 215 | name = TAPageControl; 216 | productName = TAPageControl; 217 | productReference = 5E619A541A7042AF00288DF1 /* TAPageControl.app */; 218 | productType = "com.apple.product-type.application"; 219 | }; 220 | 5E619A6C1A7042AF00288DF1 /* TAPageControlTests */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 5E619A7A1A7042AF00288DF1 /* Build configuration list for PBXNativeTarget "TAPageControlTests" */; 223 | buildPhases = ( 224 | 5E619A691A7042AF00288DF1 /* Sources */, 225 | 5E619A6A1A7042AF00288DF1 /* Frameworks */, 226 | 5E619A6B1A7042AF00288DF1 /* Resources */, 227 | ); 228 | buildRules = ( 229 | ); 230 | dependencies = ( 231 | 5E619A6F1A7042AF00288DF1 /* PBXTargetDependency */, 232 | ); 233 | name = TAPageControlTests; 234 | productName = TAPageControlTests; 235 | productReference = 5E619A6D1A7042AF00288DF1 /* TAPageControlTests.xctest */; 236 | productType = "com.apple.product-type.bundle.unit-test"; 237 | }; 238 | /* End PBXNativeTarget section */ 239 | 240 | /* Begin PBXProject section */ 241 | 5E619A4C1A7042AF00288DF1 /* Project object */ = { 242 | isa = PBXProject; 243 | attributes = { 244 | LastUpgradeCheck = 0610; 245 | ORGANIZATIONNAME = "Tanguy Aladenise"; 246 | TargetAttributes = { 247 | 5E619A531A7042AF00288DF1 = { 248 | CreatedOnToolsVersion = 6.1.1; 249 | DevelopmentTeam = SNVBX3TLMS; 250 | }; 251 | 5E619A6C1A7042AF00288DF1 = { 252 | CreatedOnToolsVersion = 6.1.1; 253 | TestTargetID = 5E619A531A7042AF00288DF1; 254 | }; 255 | }; 256 | }; 257 | buildConfigurationList = 5E619A4F1A7042AF00288DF1 /* Build configuration list for PBXProject "TAPageControl" */; 258 | compatibilityVersion = "Xcode 3.2"; 259 | developmentRegion = English; 260 | hasScannedForEncodings = 0; 261 | knownRegions = ( 262 | en, 263 | Base, 264 | ); 265 | mainGroup = 5E619A4B1A7042AF00288DF1; 266 | productRefGroup = 5E619A551A7042AF00288DF1 /* Products */; 267 | projectDirPath = ""; 268 | projectRoot = ""; 269 | targets = ( 270 | 5E619A531A7042AF00288DF1 /* TAPageControl */, 271 | 5E619A6C1A7042AF00288DF1 /* TAPageControlTests */, 272 | ); 273 | }; 274 | /* End PBXProject section */ 275 | 276 | /* Begin PBXResourcesBuildPhase section */ 277 | 5E619A521A7042AF00288DF1 /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 5EE840181A734149000CB3CA /* image2.jpg in Resources */, 282 | 5E619A631A7042AF00288DF1 /* Main.storyboard in Resources */, 283 | 5EE840171A734149000CB3CA /* image1.jpg in Resources */, 284 | 5EE8401B1A734149000CB3CA /* image5.jpg in Resources */, 285 | 5EE840191A734149000CB3CA /* image3.jpg in Resources */, 286 | 5E51D99B1A72D00A0035C305 /* dotInactive@2x.png in Resources */, 287 | 5EE8401C1A734149000CB3CA /* image6.jpg in Resources */, 288 | 5E51D9981A72D00A0035C305 /* dotActive@2x.png in Resources */, 289 | 5E51D99C1A72D00A0035C305 /* dotInactive@3x.png in Resources */, 290 | 5EE8401A1A734149000CB3CA /* image4.jpg in Resources */, 291 | 5E619A681A7042AF00288DF1 /* LaunchScreen.xib in Resources */, 292 | 5E51D99A1A72D00A0035C305 /* dotInactive.png in Resources */, 293 | 5E51D9991A72D00A0035C305 /* dotActive@3x.png in Resources */, 294 | 5E619A651A7042AF00288DF1 /* Images.xcassets in Resources */, 295 | 5E51D9971A72D00A0035C305 /* dotActive.png in Resources */, 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | 5E619A6B1A7042AF00288DF1 /* Resources */ = { 300 | isa = PBXResourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | /* End PBXResourcesBuildPhase section */ 307 | 308 | /* Begin PBXSourcesBuildPhase section */ 309 | 5E619A501A7042AF00288DF1 /* Sources */ = { 310 | isa = PBXSourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | 5E51D99F1A72E5960035C305 /* TAExampleDotView.m in Sources */, 314 | 5E51D98F1A72CFEE0035C305 /* TAPageControl.m in Sources */, 315 | 5E619A601A7042AF00288DF1 /* ViewController.m in Sources */, 316 | 5E51D98E1A72CFEE0035C305 /* TADotView.m in Sources */, 317 | 5E51D98C1A72CFEE0035C305 /* TAAbstractDotView.m in Sources */, 318 | 5E619A5D1A7042AF00288DF1 /* AppDelegate.m in Sources */, 319 | 5E51D98D1A72CFEE0035C305 /* TAAnimatedDotView.m in Sources */, 320 | 5E619A5A1A7042AF00288DF1 /* main.m in Sources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | 5E619A691A7042AF00288DF1 /* Sources */ = { 325 | isa = PBXSourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | 5E619A741A7042AF00288DF1 /* TAPageControlTests.m in Sources */, 329 | ); 330 | runOnlyForDeploymentPostprocessing = 0; 331 | }; 332 | /* End PBXSourcesBuildPhase section */ 333 | 334 | /* Begin PBXTargetDependency section */ 335 | 5E619A6F1A7042AF00288DF1 /* PBXTargetDependency */ = { 336 | isa = PBXTargetDependency; 337 | target = 5E619A531A7042AF00288DF1 /* TAPageControl */; 338 | targetProxy = 5E619A6E1A7042AF00288DF1 /* PBXContainerItemProxy */; 339 | }; 340 | /* End PBXTargetDependency section */ 341 | 342 | /* Begin PBXVariantGroup section */ 343 | 5E619A611A7042AF00288DF1 /* Main.storyboard */ = { 344 | isa = PBXVariantGroup; 345 | children = ( 346 | 5E619A621A7042AF00288DF1 /* Base */, 347 | ); 348 | name = Main.storyboard; 349 | sourceTree = ""; 350 | }; 351 | 5E619A661A7042AF00288DF1 /* LaunchScreen.xib */ = { 352 | isa = PBXVariantGroup; 353 | children = ( 354 | 5E619A671A7042AF00288DF1 /* Base */, 355 | ); 356 | name = LaunchScreen.xib; 357 | sourceTree = ""; 358 | }; 359 | /* End PBXVariantGroup section */ 360 | 361 | /* Begin XCBuildConfiguration section */ 362 | 5E619A751A7042AF00288DF1 /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 367 | CLANG_CXX_LIBRARY = "libc++"; 368 | CLANG_ENABLE_MODULES = YES; 369 | CLANG_ENABLE_OBJC_ARC = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_CONSTANT_CONVERSION = YES; 372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 373 | CLANG_WARN_EMPTY_BODY = YES; 374 | CLANG_WARN_ENUM_CONVERSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 377 | CLANG_WARN_UNREACHABLE_CODE = YES; 378 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 379 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 380 | COPY_PHASE_STRIP = NO; 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu99; 383 | GCC_DYNAMIC_NO_PIC = NO; 384 | GCC_OPTIMIZATION_LEVEL = 0; 385 | GCC_PREPROCESSOR_DEFINITIONS = ( 386 | "DEBUG=1", 387 | "$(inherited)", 388 | ); 389 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 397 | MTL_ENABLE_DEBUG_INFO = YES; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = iphoneos; 400 | }; 401 | name = Debug; 402 | }; 403 | 5E619A761A7042AF00288DF1 /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ALWAYS_SEARCH_USER_PATHS = NO; 407 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 408 | CLANG_CXX_LIBRARY = "libc++"; 409 | CLANG_ENABLE_MODULES = YES; 410 | CLANG_ENABLE_OBJC_ARC = YES; 411 | CLANG_WARN_BOOL_CONVERSION = YES; 412 | CLANG_WARN_CONSTANT_CONVERSION = YES; 413 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INT_CONVERSION = YES; 417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 418 | CLANG_WARN_UNREACHABLE_CODE = YES; 419 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 420 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 421 | COPY_PHASE_STRIP = YES; 422 | ENABLE_NS_ASSERTIONS = NO; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu99; 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 432 | MTL_ENABLE_DEBUG_INFO = NO; 433 | SDKROOT = iphoneos; 434 | VALIDATE_PRODUCT = YES; 435 | }; 436 | name = Release; 437 | }; 438 | 5E619A781A7042AF00288DF1 /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 442 | CODE_SIGN_IDENTITY = "iPhone Developer"; 443 | INFOPLIST_FILE = TAPageControl/Info.plist; 444 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | }; 447 | name = Debug; 448 | }; 449 | 5E619A791A7042AF00288DF1 /* Release */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 453 | CODE_SIGN_IDENTITY = "iPhone Developer"; 454 | INFOPLIST_FILE = TAPageControl/Info.plist; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | }; 458 | name = Release; 459 | }; 460 | 5E619A7B1A7042AF00288DF1 /* Debug */ = { 461 | isa = XCBuildConfiguration; 462 | buildSettings = { 463 | BUNDLE_LOADER = "$(TEST_HOST)"; 464 | FRAMEWORK_SEARCH_PATHS = ( 465 | "$(SDKROOT)/Developer/Library/Frameworks", 466 | "$(inherited)", 467 | ); 468 | GCC_PREPROCESSOR_DEFINITIONS = ( 469 | "DEBUG=1", 470 | "$(inherited)", 471 | ); 472 | INFOPLIST_FILE = TAPageControlTests/Info.plist; 473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TAPageControl.app/TAPageControl"; 476 | }; 477 | name = Debug; 478 | }; 479 | 5E619A7C1A7042AF00288DF1 /* Release */ = { 480 | isa = XCBuildConfiguration; 481 | buildSettings = { 482 | BUNDLE_LOADER = "$(TEST_HOST)"; 483 | FRAMEWORK_SEARCH_PATHS = ( 484 | "$(SDKROOT)/Developer/Library/Frameworks", 485 | "$(inherited)", 486 | ); 487 | INFOPLIST_FILE = TAPageControlTests/Info.plist; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TAPageControl.app/TAPageControl"; 491 | }; 492 | name = Release; 493 | }; 494 | /* End XCBuildConfiguration section */ 495 | 496 | /* Begin XCConfigurationList section */ 497 | 5E619A4F1A7042AF00288DF1 /* Build configuration list for PBXProject "TAPageControl" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 5E619A751A7042AF00288DF1 /* Debug */, 501 | 5E619A761A7042AF00288DF1 /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | defaultConfigurationName = Release; 505 | }; 506 | 5E619A771A7042AF00288DF1 /* Build configuration list for PBXNativeTarget "TAPageControl" */ = { 507 | isa = XCConfigurationList; 508 | buildConfigurations = ( 509 | 5E619A781A7042AF00288DF1 /* Debug */, 510 | 5E619A791A7042AF00288DF1 /* Release */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | 5E619A7A1A7042AF00288DF1 /* Build configuration list for PBXNativeTarget "TAPageControlTests" */ = { 516 | isa = XCConfigurationList; 517 | buildConfigurations = ( 518 | 5E619A7B1A7042AF00288DF1 /* Debug */, 519 | 5E619A7C1A7042AF00288DF1 /* Release */, 520 | ); 521 | defaultConfigurationIsVisible = 0; 522 | defaultConfigurationName = Release; 523 | }; 524 | /* End XCConfigurationList section */ 525 | }; 526 | rootObject = 5E619A4C1A7042AF00288DF1 /* Project object */; 527 | } 528 | -------------------------------------------------------------------------------- /Example/TAPageControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/TAPageControl/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/TAPageControl/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/TAPageControl/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/TAPageControl/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Example/TAPageControl/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/TAPageControl/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.aladenise.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/TAPageControl/TAExampleDotView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TAExampleDotView.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-23. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAAbstractDotView.h" 10 | 11 | @interface TAExampleDotView : TAAbstractDotView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TAPageControl/TAExampleDotView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TAExampleDotView.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-23. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAExampleDotView.h" 10 | 11 | static CGFloat const kAnimateDuration = 1; 12 | 13 | @implementation TAExampleDotView 14 | 15 | - (instancetype)init 16 | { 17 | self = [super init]; 18 | if (self) { 19 | [self initialization]; 20 | } 21 | 22 | return self; 23 | } 24 | 25 | 26 | - (id)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | [self initialization]; 31 | } 32 | return self; 33 | } 34 | 35 | 36 | - (id)initWithCoder:(NSCoder *)aDecoder 37 | { 38 | self = [super initWithCoder:aDecoder]; 39 | if (self) { 40 | [self initialization]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | 47 | - (void)initialization 48 | { 49 | self.backgroundColor = [UIColor purpleColor]; 50 | } 51 | 52 | 53 | - (void)changeActivityState:(BOOL)active 54 | { 55 | if (active) { 56 | [self animateToActiveState]; 57 | } else { 58 | [self animateToDeactiveState]; 59 | } 60 | } 61 | 62 | 63 | - (void)animateToActiveState 64 | { 65 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:-20 options:UIViewAnimationOptionCurveLinear animations:^{ 66 | self.backgroundColor = [UIColor yellowColor]; 67 | self.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(1.4, 1.4), CGAffineTransformMakeRotation(M_PI)) ; 68 | } completion:nil]; 69 | } 70 | 71 | - (void)animateToDeactiveState 72 | { 73 | self.transform = CGAffineTransformIdentity; 74 | 75 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{ 76 | self.backgroundColor = [UIColor purpleColor]; 77 | } completion:nil]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Example/TAPageControl/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/TAPageControl/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | // Controllers 10 | #import "ViewController.h" 11 | // Views 12 | #import "TAExampleDotView.h" 13 | #import "TAPageControl.h" 14 | 15 | 16 | 17 | @interface ViewController () 18 | 19 | @property (weak, nonatomic) IBOutlet TAPageControl *customStoryboardPageControl; 20 | @property (strong, nonatomic) TAPageControl *customPageControl2; 21 | @property (strong, nonatomic) TAPageControl *customPageControl3; 22 | 23 | @property (strong, nonatomic) IBOutletCollection(UIScrollView) NSArray *scrollViews; 24 | 25 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView1; 26 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView2; 27 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView3; 28 | 29 | @property (weak, nonatomic) IBOutlet UIView *wrapper2; 30 | @property (weak, nonatomic) IBOutlet UIView *wrapper3; 31 | 32 | @property (strong, nonatomic) NSArray *imagesData; 33 | 34 | 35 | @end 36 | 37 | 38 | @implementation ViewController 39 | 40 | 41 | #pragma mark - Lifecycle 42 | 43 | 44 | - (void)viewDidLoad 45 | { 46 | [super viewDidLoad]; 47 | 48 | self.imagesData = @[@"image1.jpg", @"image2.jpg", @"image3.jpg", @"image4.jpg", @"image5.jpg", @"image6.jpg"]; 49 | 50 | [self setupScrollViewImages]; 51 | 52 | for (UIScrollView *scrollView in self.scrollViews) { 53 | scrollView.delegate = self; 54 | } 55 | 56 | // TAPageControl from storyboard 57 | self.customStoryboardPageControl.numberOfPages = self.imagesData.count; 58 | 59 | 60 | // Progammatically init a TAPageControl with a custom dot view. 61 | self.customPageControl2 = [[TAPageControl alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.scrollView2.frame) - 40, CGRectGetWidth(self.scrollView2.frame), 40)]; 62 | // Example for touch bullet event 63 | self.customPageControl2.delegate = self; 64 | self.customPageControl2.numberOfPages = self.imagesData.count; 65 | // Custom dot view 66 | self.customPageControl2.dotViewClass = [TAExampleDotView class]; 67 | self.customPageControl2.dotSize = CGSizeMake(12, 12); 68 | 69 | [self.wrapper2 addSubview:self.customPageControl2]; 70 | 71 | 72 | self.customPageControl3 = [[TAPageControl alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.scrollView3.frame) - 40, CGRectGetWidth(self.scrollView3.frame), 40)]; 73 | self.customPageControl3.numberOfPages = self.imagesData.count; 74 | // Custom dot view with image 75 | self.customPageControl3.dotImage = [UIImage imageNamed:@"dotInactive"]; 76 | self.customPageControl3.currentDotImage = [UIImage imageNamed:@"dotActive"]; 77 | 78 | [self.wrapper3 addSubview:self.customPageControl3]; 79 | } 80 | 81 | 82 | - (void)viewDidLayoutSubviews 83 | { 84 | [super viewDidLayoutSubviews]; 85 | 86 | for (UIScrollView *scrollView in self.scrollViews) { 87 | scrollView.contentSize = CGSizeMake(CGRectGetWidth(scrollView.frame) * self.imagesData.count, CGRectGetHeight(scrollView.frame)); 88 | } 89 | } 90 | 91 | 92 | #pragma mark - ScrollView delegate 93 | 94 | 95 | 96 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 97 | { 98 | NSInteger pageIndex = scrollView.contentOffset.x / CGRectGetWidth(scrollView.frame); 99 | 100 | if (scrollView == self.scrollView3) { 101 | self.customPageControl3.currentPage = pageIndex; 102 | } else if (scrollView == self.scrollView2) { 103 | self.customPageControl2.currentPage = pageIndex; 104 | } else { 105 | self.customStoryboardPageControl.currentPage = pageIndex; 106 | } 107 | } 108 | 109 | 110 | // Example of use of delegate for second scroll view to respond to bullet touch event 111 | - (void)TAPageControl:(TAPageControl *)pageControl didSelectPageAtIndex:(NSInteger)index 112 | { 113 | NSLog(@"Bullet index %ld", (long)index); 114 | [self.scrollView2 scrollRectToVisible:CGRectMake(CGRectGetWidth(self.scrollView2.frame) * index, 0, CGRectGetWidth(self.scrollView2.frame), CGRectGetHeight(self.scrollView2.frame)) animated:YES]; 115 | } 116 | 117 | 118 | #pragma mark - Utils 119 | 120 | 121 | - (void)setupScrollViewImages 122 | { 123 | for (UIScrollView *scrollView in self.scrollViews) { 124 | [self.imagesData enumerateObjectsUsingBlock:^(NSString *imageName, NSUInteger idx, BOOL *stop) { 125 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(scrollView.frame) * idx, 0, CGRectGetWidth(scrollView.frame), CGRectGetHeight(scrollView.frame))]; 126 | imageView.contentMode = UIViewContentModeScaleAspectFill; 127 | imageView.image = [UIImage imageNamed:imageName]; 128 | [scrollView addSubview:imageView]; 129 | }]; 130 | } 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Example/TAPageControl/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/TAPageControlTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.aladenise.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/TAPageControlTests/TAPageControlTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TAPageControlTests.m 3 | // TAPageControlTests 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TAPageControlTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation TAPageControlTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tanguy Aladenise 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About TAPageControl 2 | === 3 | TAPageControl basically is a more powerfull UIPageControl. 4 | It offers you greater flexibility and customization. 5 | 6 | ![demo](https://dl.dropboxusercontent.com/u/20482310/TAPageControl.gif) 7 | 8 | 9 | How it works 10 | === 11 | 12 | It works like a basic UIPageControl but with more options and properties so it can fit your needs. 13 | It comes with a default dot view, but you can easily replace it by any UIView object that you want (more on that later) or with any image. 14 | 15 | The only required property to set to get it working, like UIPageControl, is a number of pages : 16 | ```objc 17 | self.pageControl.numberOfPages = 9; 18 | ``` 19 | 20 | Installation 21 | === 22 | 23 | #### CocoaPods 24 | 25 | With CocoaPods by adding in your podfile the following line : 26 | 27 | ```ruby 28 | pod 'TAPageControl' 29 | ``` 30 | 31 | #### Manually 32 | 33 | It's quite easy, just download the archive and add the TAPageControl folder to your own project. 34 | Don't forget to import the header file wherever you need it : 35 | 36 | ``` objective-c 37 | #import "TAPageControl.h" 38 | ``` 39 | 40 | Customization's magic 41 | === 42 | 43 | If you are looking for a page control I guess it's because the default one provided by Apple did not answer your need. Then you are looking for a page control that you can customize at will and you are at the right place! 44 | 45 | TAPageControl uses two kind of source to display "dots" representing pages : 46 |
    47 |
  • UIImage
  • 48 |
  • UIView
  • 49 |
50 | 51 | UIImage is the simplest way to make your page control look the way you want. There are two properties : 52 | ``` objective-c 53 | /** 54 | * UIImage to represent a dot. 55 | */ 56 | @property (nonatomic) UIImage *dotImage; 57 | 58 | /** 59 | * UIImage to represent current page dot. 60 | */ 61 | @property (nonatomic) UIImage *currentDotImage; 62 | ``` 63 | 64 | By setting both properties with your images, the control will take care of displaying the correct version depending on the current page. 65 | 66 | 67 | But what if you need more control? Well, will a UIView be enough for you? 68 | By using : 69 | ``` objective-c 70 | /** 71 | * The Class of your custom UIView, make sure to respect the TAAbstractDotView class. 72 | */ 73 | @property (nonatomic) Class dotViewClass; 74 | ``` 75 | The control will instantiate your UIView class as a dot. 76 | I recommend subclassing your view with TAAbstractDotView because the only important thing to respect is the method to switch your view between active/inactive state. 77 | ``` objective-c 78 | @interface TAAbstractDotView : UIView 79 | 80 | /** 81 | * A method call let view know which state appearance it should take. Active meaning it's current page. Inactive not the current page. 82 | * 83 | * @param active BOOL to tell if view is active or not 84 | */ 85 | - (void)changeActivityState:(BOOL)active; 86 | ``` 87 | There you go, you can do whatever you want with it. Shape, color, animations... everything. 88 | 89 | 90 | Take a look at the sample project to see how great and simple it is by yourself ;) 91 | 92 | 93 | License 94 | === 95 | 96 | TAPageControl is available under the MIT license. See the LICENSE file for more info. 97 | 98 | More 99 | === 100 | 101 | Contribute to this repository as much as you like, and any advice are welcome! =). I hope you will enjoy it. 102 | -------------------------------------------------------------------------------- /TAPageControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "TAPageControl" 3 | s.version = "0.2.0" 4 | s.summary = "A versatile and easily customizable page control for iOS" 5 | s.description = <<-DESC 6 | TAPageControl basically is a more powerfull UIPageControl. It offers you greater flexibility and customization. ;) 7 | DESC 8 | s.homepage = "https://github.com/TanguyAladenise/TAPageControl" 9 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 10 | s.license = 'MIT' 11 | s.author = { "Tanguy Aladenise" => "tanguy.alaedenise@gmail.com" } 12 | s.source = { :git => "https://github.com/TanguyAladenise/TAPageControl.git", :tag => '0.2.0' } 13 | s.social_media_url = 'https://twitter.com/TanguyAladenise' 14 | 15 | s.platform = :ios, '7.0' 16 | s.requires_arc = true 17 | 18 | s.source_files = 'TAPageControl' 19 | end 20 | -------------------------------------------------------------------------------- /TAPageControl/TAAbstractDotView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TAAbstractDotView.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface TAAbstractDotView : UIView 13 | 14 | 15 | /** 16 | * A method call let view know which state appearance it should take. Active meaning it's current page. Inactive not the current page. 17 | * 18 | * @param active BOOL to tell if view is active or not 19 | */ 20 | - (void)changeActivityState:(BOOL)active; 21 | 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /TAPageControl/TAAbstractDotView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TAAbstractDotView.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAAbstractDotView.h" 10 | 11 | 12 | @implementation TAAbstractDotView 13 | 14 | 15 | - (id)init 16 | { 17 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 18 | reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class] 19 | userInfo:nil]; 20 | } 21 | 22 | 23 | - (void)changeActivityState:(BOOL)active 24 | { 25 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 26 | reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class] 27 | userInfo:nil]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /TAPageControl/TAAnimatedDotView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TAAnimatedDotView.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAAbstractDotView.h" 10 | 11 | @interface TAAnimatedDotView : TAAbstractDotView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TAPageControl/TAAnimatedDotView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TAAnimatedDotView.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAAnimatedDotView.h" 10 | 11 | static CGFloat const kAnimateDuration = 1; 12 | 13 | @implementation TAAnimatedDotView 14 | 15 | - (instancetype)init 16 | { 17 | self = [super init]; 18 | if (self) { 19 | [self initialization]; 20 | } 21 | 22 | return self; 23 | } 24 | 25 | 26 | - (id)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | [self initialization]; 31 | } 32 | return self; 33 | } 34 | 35 | 36 | - (id)initWithCoder:(NSCoder *)aDecoder 37 | { 38 | self = [super initWithCoder:aDecoder]; 39 | if (self) { 40 | [self initialization]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | 47 | - (void)initialization 48 | { 49 | self.backgroundColor = [UIColor clearColor]; 50 | self.layer.cornerRadius = CGRectGetWidth(self.frame) / 2; 51 | self.layer.borderColor = [UIColor whiteColor].CGColor; 52 | self.layer.borderWidth = 2; 53 | } 54 | 55 | 56 | - (void)changeActivityState:(BOOL)active 57 | { 58 | if (active) { 59 | [self animateToActiveState]; 60 | } else { 61 | [self animateToDeactiveState]; 62 | } 63 | } 64 | 65 | 66 | - (void)animateToActiveState 67 | { 68 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:-20 options:UIViewAnimationOptionCurveLinear animations:^{ 69 | self.backgroundColor = [UIColor whiteColor]; 70 | self.transform = CGAffineTransformMakeScale(1.4, 1.4); 71 | } completion:nil]; 72 | } 73 | 74 | - (void)animateToDeactiveState 75 | { 76 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{ 77 | self.backgroundColor = [UIColor clearColor]; 78 | self.transform = CGAffineTransformIdentity; 79 | } completion:nil]; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /TAPageControl/TADotView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TADotView.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAAbstractDotView.h" 10 | 11 | @interface TADotView : TAAbstractDotView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TAPageControl/TADotView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TADotView.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-22. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TADotView.h" 10 | 11 | @implementation TADotView 12 | 13 | 14 | - (instancetype)init 15 | { 16 | self = [super init]; 17 | if (self) { 18 | [self initialization]; 19 | } 20 | 21 | return self; 22 | } 23 | 24 | 25 | - (id)initWithFrame:(CGRect)frame 26 | { 27 | self = [super initWithFrame:frame]; 28 | if (self) { 29 | [self initialization]; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | - (id)initWithCoder:(NSCoder *)aDecoder 36 | { 37 | self = [super initWithCoder:aDecoder]; 38 | if (self) { 39 | [self initialization]; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | 46 | - (void)initialization 47 | { 48 | self.backgroundColor = [UIColor clearColor]; 49 | self.layer.cornerRadius = CGRectGetWidth(self.frame) / 2; 50 | self.layer.borderColor = [UIColor whiteColor].CGColor; 51 | self.layer.borderWidth = 2; 52 | } 53 | 54 | 55 | - (void)changeActivityState:(BOOL)active 56 | { 57 | if (active) { 58 | self.backgroundColor = [UIColor whiteColor]; 59 | } else { 60 | self.backgroundColor = [UIColor clearColor]; 61 | } 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /TAPageControl/TAPageControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // TAPageControl.h 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol TAPageControlDelegate; 12 | 13 | 14 | @interface TAPageControl : UIControl 15 | 16 | 17 | /** 18 | * Dot view customization properties 19 | */ 20 | 21 | /** 22 | * The Class of your custom UIView, make sure to respect the TAAbstractDotView class. 23 | */ 24 | @property (nonatomic) Class dotViewClass; 25 | 26 | 27 | /** 28 | * UIImage to represent a dot. 29 | */ 30 | @property (nonatomic) UIImage *dotImage; 31 | 32 | 33 | /** 34 | * UIImage to represent current page dot. 35 | */ 36 | @property (nonatomic) UIImage *currentDotImage; 37 | 38 | 39 | /** 40 | * Dot size for dot views. Default is 8 by 8. 41 | */ 42 | @property (nonatomic) CGSize dotSize; 43 | 44 | 45 | /** 46 | * Spacing between two dot views. Default is 8. 47 | */ 48 | @property (nonatomic) NSInteger spacingBetweenDots; 49 | 50 | 51 | /** 52 | * Page control setup properties 53 | */ 54 | 55 | 56 | /** 57 | * Delegate for TAPageControl 58 | */ 59 | @property(nonatomic,assign) id delegate; 60 | 61 | 62 | /** 63 | * Number of pages for control. Default is 0. 64 | */ 65 | @property (nonatomic) NSInteger numberOfPages; 66 | 67 | 68 | /** 69 | * Current page on which control is active. Default is 0. 70 | */ 71 | @property (nonatomic) NSInteger currentPage; 72 | 73 | 74 | /** 75 | * Hide the control if there is only one page. Default is NO. 76 | */ 77 | @property (nonatomic) BOOL hidesForSinglePage; 78 | 79 | 80 | /** 81 | * Let the control know if should grow bigger by keeping center, or just get longer (right side expanding). By default YES. 82 | */ 83 | @property (nonatomic) BOOL shouldResizeFromCenter; 84 | 85 | 86 | /** 87 | * Return the minimum size required to display control properly for the given page count. 88 | * 89 | * @param pageCount Number of dots that will require display 90 | * 91 | * @return The CGSize being the minimum size required. 92 | */ 93 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; 94 | 95 | 96 | @end 97 | 98 | 99 | @protocol TAPageControlDelegate 100 | 101 | @optional 102 | - (void)TAPageControl:(TAPageControl *)pageControl didSelectPageAtIndex:(NSInteger)index; 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /TAPageControl/TAPageControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // TAPageControl.m 3 | // TAPageControl 4 | // 5 | // Created by Tanguy Aladenise on 2015-01-21. 6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved. 7 | // 8 | 9 | #import "TAPageControl.h" 10 | #import "TAAbstractDotView.h" 11 | #import "TAAnimatedDotView.h" 12 | #import "TADotView.h" 13 | 14 | /** 15 | * Default number of pages for initialization 16 | */ 17 | static NSInteger const kDefaultNumberOfPages = 0; 18 | 19 | /** 20 | * Default current page for initialization 21 | */ 22 | static NSInteger const kDefaultCurrentPage = 0; 23 | 24 | /** 25 | * Default setting for hide for single page feature. For initialization 26 | */ 27 | static BOOL const kDefaultHideForSinglePage = NO; 28 | 29 | /** 30 | * Default setting for shouldResizeFromCenter. For initialiation 31 | */ 32 | static BOOL const kDefaultShouldResizeFromCenter = YES; 33 | 34 | /** 35 | * Default spacing between dots 36 | */ 37 | static NSInteger const kDefaultSpacingBetweenDots = 8; 38 | 39 | /** 40 | * Default dot size 41 | */ 42 | static CGSize const kDefaultDotSize = {8, 8}; 43 | 44 | 45 | @interface TAPageControl() 46 | 47 | 48 | /** 49 | * Array of dot views for reusability and touch events. 50 | */ 51 | @property (strong, nonatomic) NSMutableArray *dots; 52 | 53 | 54 | @end 55 | 56 | @implementation TAPageControl 57 | 58 | 59 | #pragma mark - Lifecycle 60 | 61 | 62 | - (id)init 63 | { 64 | self = [super init]; 65 | if (self) { 66 | [self initialization]; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | 73 | - (id)initWithFrame:(CGRect)frame 74 | { 75 | self = [super initWithFrame:frame]; 76 | if (self) { 77 | [self initialization]; 78 | } 79 | return self; 80 | } 81 | 82 | 83 | - (id)initWithCoder:(NSCoder *)aDecoder 84 | { 85 | self = [super initWithCoder:aDecoder]; 86 | if (self) { 87 | [self initialization]; 88 | } 89 | 90 | return self; 91 | } 92 | 93 | 94 | /** 95 | * Default setup when initiating control 96 | */ 97 | - (void)initialization 98 | { 99 | self.dotViewClass = [TAAnimatedDotView class]; 100 | self.spacingBetweenDots = kDefaultSpacingBetweenDots; 101 | self.numberOfPages = kDefaultNumberOfPages; 102 | self.currentPage = kDefaultCurrentPage; 103 | self.hidesForSinglePage = kDefaultHideForSinglePage; 104 | self.shouldResizeFromCenter = kDefaultShouldResizeFromCenter; 105 | } 106 | 107 | 108 | #pragma mark - Touch event 109 | 110 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 111 | { 112 | UITouch *touch = [touches anyObject]; 113 | if (touch.view != self) { 114 | NSInteger index = [self.dots indexOfObject:touch.view]; 115 | if ([self.delegate respondsToSelector:@selector(TAPageControl:didSelectPageAtIndex:)]) { 116 | [self.delegate TAPageControl:self didSelectPageAtIndex:index]; 117 | } 118 | } 119 | } 120 | 121 | #pragma mark - Layout 122 | 123 | 124 | /** 125 | * Resizes and moves the receiver view so it just encloses its subviews. 126 | */ 127 | - (void)sizeToFit 128 | { 129 | [self updateFrame:YES]; 130 | } 131 | 132 | 133 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount 134 | { 135 | return CGSizeMake((self.dotSize.width + self.spacingBetweenDots) * pageCount - self.spacingBetweenDots , self.dotSize.height); 136 | } 137 | 138 | 139 | /** 140 | * Will update dots display and frame. Reuse existing views or instantiate one if required. Update their position in case frame changed. 141 | */ 142 | - (void)updateDots 143 | { 144 | if (self.numberOfPages == 0) { 145 | return; 146 | } 147 | 148 | for (NSInteger i = 0; i < self.numberOfPages; i++) { 149 | 150 | UIView *dot; 151 | if (i < self.dots.count) { 152 | dot = [self.dots objectAtIndex:i]; 153 | } else { 154 | dot = [self generateDotView]; 155 | } 156 | 157 | [self updateDotFrame:dot atIndex:i]; 158 | } 159 | 160 | [self changeActivity:YES atIndex:self.currentPage]; 161 | 162 | [self hideForSinglePage]; 163 | } 164 | 165 | 166 | /** 167 | * Update frame control to fit current number of pages. It will apply required size if authorize and required. 168 | * 169 | * @param overrideExistingFrame BOOL to allow frame to be overriden. Meaning the required size will be apply no mattter what. 170 | */ 171 | - (void)updateFrame:(BOOL)overrideExistingFrame 172 | { 173 | CGPoint center = self.center; 174 | CGSize requiredSize = [self sizeForNumberOfPages:self.numberOfPages]; 175 | 176 | // We apply requiredSize only if authorize to and necessary 177 | if (overrideExistingFrame || ((CGRectGetWidth(self.frame) < requiredSize.width || CGRectGetHeight(self.frame) < requiredSize.height) && !overrideExistingFrame)) { 178 | self.frame = CGRectMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), requiredSize.width, requiredSize.height); 179 | if (self.shouldResizeFromCenter) { 180 | self.center = center; 181 | } 182 | } 183 | 184 | [self resetDotViews]; 185 | } 186 | 187 | 188 | /** 189 | * Update the frame of a specific dot at a specific index 190 | * 191 | * @param dot Dot view 192 | * @param index Page index of dot 193 | */ 194 | - (void)updateDotFrame:(UIView *)dot atIndex:(NSInteger)index 195 | { 196 | // Dots are always centered within view 197 | CGFloat x = (self.dotSize.width + self.spacingBetweenDots) * index + ( (CGRectGetWidth(self.frame) - [self sizeForNumberOfPages:self.numberOfPages].width) / 2); 198 | CGFloat y = (CGRectGetHeight(self.frame) - self.dotSize.height) / 2; 199 | 200 | dot.frame = CGRectMake(x, y, self.dotSize.width, self.dotSize.height); 201 | } 202 | 203 | 204 | #pragma mark - Utils 205 | 206 | 207 | /** 208 | * Generate a dot view and add it to the collection 209 | * 210 | * @return The UIView object representing a dot 211 | */ 212 | - (UIView *)generateDotView 213 | { 214 | UIView *dotView; 215 | 216 | if (self.dotViewClass) { 217 | dotView = [[self.dotViewClass alloc] initWithFrame:CGRectMake(0, 0, self.dotSize.width, self.dotSize.height)]; 218 | } else { 219 | dotView = [[UIImageView alloc] initWithImage:self.dotImage]; 220 | dotView.frame = CGRectMake(0, 0, self.dotSize.width, self.dotSize.height); 221 | } 222 | 223 | if (dotView) { 224 | [self addSubview:dotView]; 225 | [self.dots addObject:dotView]; 226 | } 227 | 228 | dotView.userInteractionEnabled = YES; 229 | 230 | return dotView; 231 | } 232 | 233 | 234 | /** 235 | * Change activity state of a dot view. Current/not currrent. 236 | * 237 | * @param active Active state to apply 238 | * @param index Index of dot for state update 239 | */ 240 | - (void)changeActivity:(BOOL)active atIndex:(NSInteger)index 241 | { 242 | if (self.dotViewClass) { 243 | TAAbstractDotView *abstractDotView = (TAAbstractDotView *)[self.dots objectAtIndex:index]; 244 | if ([abstractDotView respondsToSelector:@selector(changeActivityState:)]) { 245 | [abstractDotView changeActivityState:active]; 246 | } else { 247 | NSLog(@"Custom view : %@ must implement an 'changeActivityState' method or you can subclass %@ to help you.", self.dotViewClass, [TAAbstractDotView class]); 248 | } 249 | } else if (self.dotImage && self.currentDotImage) { 250 | UIImageView *dotView = (UIImageView *)[self.dots objectAtIndex:index]; 251 | dotView.image = (active) ? self.currentDotImage : self.dotImage; 252 | } 253 | } 254 | 255 | 256 | - (void)resetDotViews 257 | { 258 | for (UIView *dotView in self.dots) { 259 | [dotView removeFromSuperview]; 260 | } 261 | 262 | [self.dots removeAllObjects]; 263 | [self updateDots]; 264 | } 265 | 266 | 267 | - (void)hideForSinglePage 268 | { 269 | if (self.dots.count == 1 && self.hidesForSinglePage) { 270 | self.hidden = YES; 271 | } else { 272 | self.hidden = NO; 273 | } 274 | } 275 | 276 | #pragma mark - Setters 277 | 278 | 279 | - (void)setNumberOfPages:(NSInteger)numberOfPages 280 | { 281 | _numberOfPages = numberOfPages; 282 | 283 | // Update dot position to fit new number of pages 284 | [self resetDotViews]; 285 | } 286 | 287 | 288 | - (void)setSpacingBetweenDots:(NSInteger)spacingBetweenDots 289 | { 290 | _spacingBetweenDots = spacingBetweenDots; 291 | 292 | [self resetDotViews]; 293 | } 294 | 295 | 296 | - (void)setCurrentPage:(NSInteger)currentPage 297 | { 298 | // If no pages, no current page to treat. 299 | if (self.numberOfPages == 0 || currentPage == _currentPage) { 300 | _currentPage = currentPage; 301 | return; 302 | } 303 | 304 | // Pre set 305 | [self changeActivity:NO atIndex:_currentPage]; 306 | _currentPage = currentPage; 307 | // Post set 308 | [self changeActivity:YES atIndex:_currentPage]; 309 | } 310 | 311 | 312 | - (void)setDotImage:(UIImage *)dotImage 313 | { 314 | _dotImage = dotImage; 315 | [self resetDotViews]; 316 | self.dotViewClass = nil; 317 | } 318 | 319 | 320 | - (void)setCurrentDotImage:(UIImage *)currentDotimage 321 | { 322 | _currentDotImage = currentDotimage; 323 | [self resetDotViews]; 324 | self.dotViewClass = nil; 325 | } 326 | 327 | 328 | - (void)setDotViewClass:(Class)dotViewClass 329 | { 330 | _dotViewClass = dotViewClass; 331 | self.dotSize = CGSizeZero; 332 | [self resetDotViews]; 333 | } 334 | 335 | 336 | #pragma mark - Getters 337 | 338 | 339 | - (NSMutableArray *)dots 340 | { 341 | if (!_dots) { 342 | _dots = [[NSMutableArray alloc] init]; 343 | } 344 | 345 | return _dots; 346 | } 347 | 348 | 349 | - (CGSize)dotSize 350 | { 351 | // Dot size logic depending on the source of the dot view 352 | if (self.dotImage && CGSizeEqualToSize(_dotSize, CGSizeZero)) { 353 | _dotSize = self.dotImage.size; 354 | } else if (self.dotViewClass && CGSizeEqualToSize(_dotSize, CGSizeZero)) { 355 | _dotSize = kDefaultDotSize; 356 | return _dotSize; 357 | } 358 | 359 | return _dotSize; 360 | } 361 | 362 | @end 363 | --------------------------------------------------------------------------------