├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── PJ20.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── PJ20.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── Example.xcscheme │ │ └── xcschememanagement.plist └── Example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ └── Main.storyboard │ ├── Example-Info.plist │ ├── Example-Prefix.pch │ ├── GameTimer.h │ ├── GameTimer.m │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── Game-Timer-iOS.podspec ├── GameTimer.h ├── GameTimer.m ├── LICENSE └── README.md /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 53C94225184CAC1700771AB5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C94224184CAC1700771AB5 /* Foundation.framework */; }; 11 | 53C94227184CAC1700771AB5 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C94226184CAC1700771AB5 /* CoreGraphics.framework */; }; 12 | 53C94229184CAC1700771AB5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C94228184CAC1700771AB5 /* UIKit.framework */; }; 13 | 53C9422F184CAC1700771AB5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 53C9422D184CAC1700771AB5 /* InfoPlist.strings */; }; 14 | 53C94231184CAC1700771AB5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C94230184CAC1700771AB5 /* main.m */; }; 15 | 53C94235184CAC1700771AB5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C94234184CAC1700771AB5 /* AppDelegate.m */; }; 16 | 53C94238184CAC1700771AB5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53C94236184CAC1700771AB5 /* Main.storyboard */; }; 17 | 53C9423B184CAC1700771AB5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C9423A184CAC1700771AB5 /* ViewController.m */; }; 18 | 53C94245184CAC1700771AB5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C94224184CAC1700771AB5 /* Foundation.framework */; }; 19 | 53C94246184CAC1700771AB5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C94228184CAC1700771AB5 /* UIKit.framework */; }; 20 | 53C9425E184CB05700771AB5 /* GameTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C9425D184CB05700771AB5 /* GameTimer.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 53C94247184CAC1700771AB5 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 53C94219184CAC1700771AB5 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 53C94220184CAC1700771AB5; 29 | remoteInfo = Example; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 53C94221184CAC1700771AB5 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 53C94224184CAC1700771AB5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | 53C94226184CAC1700771AB5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 37 | 53C94228184CAC1700771AB5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 38 | 53C9422C184CAC1700771AB5 /* Example-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Example-Info.plist"; sourceTree = ""; }; 39 | 53C9422E184CAC1700771AB5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 40 | 53C94230184CAC1700771AB5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | 53C94232184CAC1700771AB5 /* Example-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example-Prefix.pch"; sourceTree = ""; }; 42 | 53C94233184CAC1700771AB5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | 53C94234184CAC1700771AB5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | 53C94237184CAC1700771AB5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 53C94239184CAC1700771AB5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | 53C9423A184CAC1700771AB5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | 53C94242184CAC1700771AB5 /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 53C9425C184CB05700771AB5 /* GameTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameTimer.h; sourceTree = ""; }; 49 | 53C9425D184CB05700771AB5 /* GameTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameTimer.m; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 53C9421E184CAC1700771AB5 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 53C94227184CAC1700771AB5 /* CoreGraphics.framework in Frameworks */, 58 | 53C94229184CAC1700771AB5 /* UIKit.framework in Frameworks */, 59 | 53C94225184CAC1700771AB5 /* Foundation.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 53C9423F184CAC1700771AB5 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 53C94246184CAC1700771AB5 /* UIKit.framework in Frameworks */, 68 | 53C94245184CAC1700771AB5 /* Foundation.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 53C94218184CAC1700771AB5 = { 76 | isa = PBXGroup; 77 | children = ( 78 | 53C9422A184CAC1700771AB5 /* Example */, 79 | 53C94223184CAC1700771AB5 /* Frameworks */, 80 | 53C94222184CAC1700771AB5 /* Products */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 53C94222184CAC1700771AB5 /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 53C94221184CAC1700771AB5 /* Example.app */, 88 | 53C94242184CAC1700771AB5 /* ExampleTests.xctest */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 53C94223184CAC1700771AB5 /* Frameworks */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 53C94224184CAC1700771AB5 /* Foundation.framework */, 97 | 53C94226184CAC1700771AB5 /* CoreGraphics.framework */, 98 | 53C94228184CAC1700771AB5 /* UIKit.framework */, 99 | ); 100 | name = Frameworks; 101 | sourceTree = ""; 102 | }; 103 | 53C9422A184CAC1700771AB5 /* Example */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 53C94233184CAC1700771AB5 /* AppDelegate.h */, 107 | 53C94234184CAC1700771AB5 /* AppDelegate.m */, 108 | 53C94236184CAC1700771AB5 /* Main.storyboard */, 109 | 53C94239184CAC1700771AB5 /* ViewController.h */, 110 | 53C9423A184CAC1700771AB5 /* ViewController.m */, 111 | 53C9425C184CB05700771AB5 /* GameTimer.h */, 112 | 53C9425D184CB05700771AB5 /* GameTimer.m */, 113 | 53C9422B184CAC1700771AB5 /* Supporting Files */, 114 | ); 115 | path = Example; 116 | sourceTree = ""; 117 | }; 118 | 53C9422B184CAC1700771AB5 /* Supporting Files */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 53C9422C184CAC1700771AB5 /* Example-Info.plist */, 122 | 53C9422D184CAC1700771AB5 /* InfoPlist.strings */, 123 | 53C94230184CAC1700771AB5 /* main.m */, 124 | 53C94232184CAC1700771AB5 /* Example-Prefix.pch */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 53C94220184CAC1700771AB5 /* Example */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 53C94253184CAC1700771AB5 /* Build configuration list for PBXNativeTarget "Example" */; 135 | buildPhases = ( 136 | 53C9421D184CAC1700771AB5 /* Sources */, 137 | 53C9421E184CAC1700771AB5 /* Frameworks */, 138 | 53C9421F184CAC1700771AB5 /* Resources */, 139 | ); 140 | buildRules = ( 141 | ); 142 | dependencies = ( 143 | ); 144 | name = Example; 145 | productName = Example; 146 | productReference = 53C94221184CAC1700771AB5 /* Example.app */; 147 | productType = "com.apple.product-type.application"; 148 | }; 149 | 53C94241184CAC1700771AB5 /* ExampleTests */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 53C94256184CAC1700771AB5 /* Build configuration list for PBXNativeTarget "ExampleTests" */; 152 | buildPhases = ( 153 | 53C9423E184CAC1700771AB5 /* Sources */, 154 | 53C9423F184CAC1700771AB5 /* Frameworks */, 155 | 53C94240184CAC1700771AB5 /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | 53C94248184CAC1700771AB5 /* PBXTargetDependency */, 161 | ); 162 | name = ExampleTests; 163 | productName = ExampleTests; 164 | productReference = 53C94242184CAC1700771AB5 /* ExampleTests.xctest */; 165 | productType = "com.apple.product-type.bundle.unit-test"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 53C94219184CAC1700771AB5 /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastUpgradeCheck = 0500; 174 | TargetAttributes = { 175 | 53C94241184CAC1700771AB5 = { 176 | TestTargetID = 53C94220184CAC1700771AB5; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 53C9421C184CAC1700771AB5 /* Build configuration list for PBXProject "Example" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 53C94218184CAC1700771AB5; 189 | productRefGroup = 53C94222184CAC1700771AB5 /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 53C94220184CAC1700771AB5 /* Example */, 194 | 53C94241184CAC1700771AB5 /* ExampleTests */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 53C9421F184CAC1700771AB5 /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 53C9422F184CAC1700771AB5 /* InfoPlist.strings in Resources */, 205 | 53C94238184CAC1700771AB5 /* Main.storyboard in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | 53C94240184CAC1700771AB5 /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXResourcesBuildPhase section */ 217 | 218 | /* Begin PBXSourcesBuildPhase section */ 219 | 53C9421D184CAC1700771AB5 /* Sources */ = { 220 | isa = PBXSourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 53C9423B184CAC1700771AB5 /* ViewController.m in Sources */, 224 | 53C9425E184CB05700771AB5 /* GameTimer.m in Sources */, 225 | 53C94235184CAC1700771AB5 /* AppDelegate.m in Sources */, 226 | 53C94231184CAC1700771AB5 /* main.m in Sources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | 53C9423E184CAC1700771AB5 /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin PBXTargetDependency section */ 240 | 53C94248184CAC1700771AB5 /* PBXTargetDependency */ = { 241 | isa = PBXTargetDependency; 242 | target = 53C94220184CAC1700771AB5 /* Example */; 243 | targetProxy = 53C94247184CAC1700771AB5 /* PBXContainerItemProxy */; 244 | }; 245 | /* End PBXTargetDependency section */ 246 | 247 | /* Begin PBXVariantGroup section */ 248 | 53C9422D184CAC1700771AB5 /* InfoPlist.strings */ = { 249 | isa = PBXVariantGroup; 250 | children = ( 251 | 53C9422E184CAC1700771AB5 /* en */, 252 | ); 253 | name = InfoPlist.strings; 254 | sourceTree = ""; 255 | }; 256 | 53C94236184CAC1700771AB5 /* Main.storyboard */ = { 257 | isa = PBXVariantGroup; 258 | children = ( 259 | 53C94237184CAC1700771AB5 /* Base */, 260 | ); 261 | name = Main.storyboard; 262 | sourceTree = ""; 263 | }; 264 | /* End PBXVariantGroup section */ 265 | 266 | /* Begin XCBuildConfiguration section */ 267 | 53C94251184CAC1700771AB5 /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 273 | CLANG_CXX_LIBRARY = "libc++"; 274 | CLANG_ENABLE_MODULES = YES; 275 | CLANG_ENABLE_OBJC_ARC = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INT_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 284 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 285 | COPY_PHASE_STRIP = NO; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_OPTIMIZATION_LEVEL = 0; 289 | GCC_PREPROCESSOR_DEFINITIONS = ( 290 | "DEBUG=1", 291 | "$(inherited)", 292 | ); 293 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 301 | ONLY_ACTIVE_ARCH = YES; 302 | SDKROOT = iphoneos; 303 | }; 304 | name = Debug; 305 | }; 306 | 53C94252184CAC1700771AB5 /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_MODULES = YES; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_CONSTANT_CONVERSION = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = YES; 325 | ENABLE_NS_ASSERTIONS = NO; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 329 | GCC_WARN_UNDECLARED_SELECTOR = YES; 330 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 331 | GCC_WARN_UNUSED_FUNCTION = YES; 332 | GCC_WARN_UNUSED_VARIABLE = YES; 333 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 334 | SDKROOT = iphoneos; 335 | VALIDATE_PRODUCT = YES; 336 | }; 337 | name = Release; 338 | }; 339 | 53C94254184CAC1700771AB5 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 344 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 345 | GCC_PREFIX_HEADER = "Example/Example-Prefix.pch"; 346 | INFOPLIST_FILE = "Example/Example-Info.plist"; 347 | PRODUCT_NAME = "$(TARGET_NAME)"; 348 | WRAPPER_EXTENSION = app; 349 | }; 350 | name = Debug; 351 | }; 352 | 53C94255184CAC1700771AB5 /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 357 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 358 | GCC_PREFIX_HEADER = "Example/Example-Prefix.pch"; 359 | INFOPLIST_FILE = "Example/Example-Info.plist"; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | WRAPPER_EXTENSION = app; 362 | }; 363 | name = Release; 364 | }; 365 | 53C94257184CAC1700771AB5 /* Debug */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 369 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 370 | FRAMEWORK_SEARCH_PATHS = ( 371 | "$(SDKROOT)/Developer/Library/Frameworks", 372 | "$(inherited)", 373 | "$(DEVELOPER_FRAMEWORKS_DIR)", 374 | ); 375 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 376 | GCC_PREFIX_HEADER = "Example/Example-Prefix.pch"; 377 | GCC_PREPROCESSOR_DEFINITIONS = ( 378 | "DEBUG=1", 379 | "$(inherited)", 380 | ); 381 | INFOPLIST_FILE = "ExampleTests/ExampleTests-Info.plist"; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | TEST_HOST = "$(BUNDLE_LOADER)"; 384 | WRAPPER_EXTENSION = xctest; 385 | }; 386 | name = Debug; 387 | }; 388 | 53C94258184CAC1700771AB5 /* Release */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 392 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 393 | FRAMEWORK_SEARCH_PATHS = ( 394 | "$(SDKROOT)/Developer/Library/Frameworks", 395 | "$(inherited)", 396 | "$(DEVELOPER_FRAMEWORKS_DIR)", 397 | ); 398 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 399 | GCC_PREFIX_HEADER = "Example/Example-Prefix.pch"; 400 | INFOPLIST_FILE = "ExampleTests/ExampleTests-Info.plist"; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | TEST_HOST = "$(BUNDLE_LOADER)"; 403 | WRAPPER_EXTENSION = xctest; 404 | }; 405 | name = Release; 406 | }; 407 | /* End XCBuildConfiguration section */ 408 | 409 | /* Begin XCConfigurationList section */ 410 | 53C9421C184CAC1700771AB5 /* Build configuration list for PBXProject "Example" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | 53C94251184CAC1700771AB5 /* Debug */, 414 | 53C94252184CAC1700771AB5 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | 53C94253184CAC1700771AB5 /* Build configuration list for PBXNativeTarget "Example" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | 53C94254184CAC1700771AB5 /* Debug */, 423 | 53C94255184CAC1700771AB5 /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Release; 427 | }; 428 | 53C94256184CAC1700771AB5 /* Build configuration list for PBXNativeTarget "ExampleTests" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | 53C94257184CAC1700771AB5 /* Debug */, 432 | 53C94258184CAC1700771AB5 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | /* End XCConfigurationList section */ 438 | }; 439 | rootObject = 53C94219184CAC1700771AB5 /* Project object */; 440 | } 441 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/xcuserdata/PJ20.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjebs/Game-Timer-iOS/d74323e74eb5c1afffdac27c59ff0e84d5d79c9f/Example/Example.xcodeproj/project.xcworkspace/xcuserdata/PJ20.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/PJ20.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/PJ20.xcuserdatad/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/PJ20.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 53C94220184CAC1700771AB5 16 | 17 | primary 18 | 19 | 20 | 53C94241184CAC1700771AB5 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Example 4 | // 5 | // Created by PJ on 2/12/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Example 4 | // 5 | // Created by PJ on 2/12/13. 6 | // 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // 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. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // 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. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 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 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Example/Example/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 | 30 | 40 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Example/Example/Example-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | PJ-Engineering-and-Business-Solutions-Pty.-Ltd..${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/Example/Example-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/Example/GameTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameTimer.h 3 | // 4 | // Version 1.0 5 | // 6 | // Copyright (c) 2013 PJ Engineering and Business Solutions Pty. Ltd. 7 | // All rights reserved. 8 | // enquiries@pjebs.com.au 9 | // 10 | 11 | //GameTimer incorporates 2 timers - referred to as longTimer and shortTimer. 12 | //The longTimer MUST have a longer timer duration than the shortTimer. 13 | //If that is not the case, then the behaviour of GameTimer is indeterminate and unpredictable. 14 | //The shortTimer acts as a 'finer' resolution timer that can be used to update a progressbar or 15 | //continually poll a network connection (for example). It's interval is usually set to a fraction 16 | //of the longTimer. 17 | // 18 | //Example: The longTimer can be set to 2 minutes (i.e. the time to boil an egg). The shortTimer can be 19 | //set to 1 second so the progressbar updates every 1 second. This represents a fraction of 1/120. 20 | // 21 | //GameTimer automatically pauses when the app enters the BACKGROUND and 'unpauses' when the app is 22 | //ACTIVE again. 23 | 24 | 25 | #import 26 | @protocol GameTimerDelegate; 27 | 28 | @interface GameTimer : NSObject 29 | 30 | 31 | //The 'finer' resolution timer duration. This must be less than longInterval. 32 | //The behaviour of GameTimer is unpredictable otherwise. 33 | //The units are in seconds. It can be as small as 0.1 milliseconds. 34 | @property float shortInterval; 35 | 36 | //The overall timer duration. When this timer expires, then GameTimer deactivates by itself. 37 | //It automatically cleans its memory after deactivating the shortTimer AND longTimer. 38 | //The units are in seconds. It can be as small as 0.1 milliseconds. 39 | @property float longInterval; 40 | 41 | //The amount of time in seconds since GameTimer was started 42 | //excluding when the app enters the background state. 43 | @property (nonatomic, readonly) float time; 44 | 45 | //The delegate must respond to longTimerExpired: method. This is mandatory. 46 | //It is called when the longTimer expires. 47 | @property(nonatomic, weak) IBOutlet id delegate; 48 | 49 | //This method is used to initialise GameTimer. GameTimer MUST NOT be initialised using init: 50 | //Initialising GameTimer does not start the timer. 51 | // 52 | //An example: 53 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; 54 | // 55 | //The above example will create a GameTimer with a longInterval of 120 seconds. It will fire the 56 | //shortTimerExpired: method every 0.5 seconds. 57 | // 58 | //Always allocate GameTimer to the instance variable ONCE (such as in viewDidLoad method). If you need to 59 | //change the settings, stop the timer and then do so using the instance variables 60 | //(i.e. self.gameTimer.longInterval = 3*60;) and then start the timer (i.e. [self.gameTimer start];) 61 | //If you need to deallocate GameTimer, then stop GameTimer first. 62 | //(i.e. [self.gameTimer stop]; self.gameTimer = nil;) 63 | // 64 | //WARNING: If the reference to the newly created gameTimer is changed to point to a BRAND new gameTimer alloc, 65 | //then you must call the 'stopTimer' before reallocating. This is because NSRunLoop keeps a strong reference. 66 | //An example (CORRECT WAY - EVEN THOUGH I RECOMMEND NEVER REALLOCATING): 67 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; //First allocation 68 | // [self.gameTimer stop]; //Even if self.gameTimer is declared as a 'strong' reference, the original does not deallocate 69 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:5 andShortInterval:2 andDelegate:self]; //Second BRAND NEW allocation 70 | - (id)initWithLongInterval:(float)longInterval andShortInterval: (float)shortInterval andDelegate:(id ) delegate; 71 | 72 | //Stops the timer. This MUST be called from the same thread that called the startTimer method. 73 | - (void) stopTimer; 74 | 75 | //Restarts the timer. 76 | - (void) startTimer; 77 | 78 | //Pauses the timer 79 | -(void)pauseTime; 80 | 81 | //Unpauses the timer 82 | -(void)unPauseTime; 83 | 84 | @end 85 | 86 | 87 | @protocol GameTimerDelegate 88 | 89 | @required 90 | //This method is fired when the longTimer expires. GameTimer will stop at this point. You will need to 91 | //call the startTimer method to restart the timer. It is MANDATORY for the delegate to implement this. 92 | -(void)longTimerExpired: (GameTimer *)gameTimer; 93 | 94 | @optional 95 | //This method is fired when the shortTimer expires. It will continue to fire until the longTimer expires, 96 | //at which point GameTimer will stop. It is OPTIONAL for the delegate to implement this. 97 | //In order to update a progressbar, this method should be implemented and used in conjunction with 98 | //the 'time' value and the 'longInterval' value. 99 | //The method is not expected to fire at precise moments. It is only for tasks such as updating, polling etc. 100 | -(void)shortTimerExpired: (GameTimer *)gameTimer time: (float)time longInterval: (float)longInterval; 101 | @end 102 | 103 | -------------------------------------------------------------------------------- /Example/Example/GameTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameTimer.m 3 | // 4 | // Version 1.0 5 | // 6 | // Copyright (c) 2013 PJ Engineering and Business Solutions Pty. Ltd. 7 | // All rights reserved. 8 | // enquiries@pjebs.com.au 9 | // 10 | 11 | #import "GameTimer.h" 12 | 13 | @interface GameTimer () 14 | { 15 | NSDate *startingTime; 16 | NSDate *tempDate; 17 | BOOL currentlyPaused; 18 | } 19 | 20 | @property (strong, nonatomic) NSTimer *longTimer; 21 | @property (strong, nonatomic) NSTimer *shortTimer; 22 | 23 | -(void)longTimerExpired; 24 | -(void)shortTimerExpired; 25 | 26 | @end 27 | 28 | @implementation GameTimer 29 | 30 | - (id)initWithLongInterval:(float)longInterval andShortInterval: (float)shortInterval andDelegate:(id ) delegate; 31 | { 32 | self = [super init]; 33 | 34 | if (self) 35 | { 36 | self.shortInterval = shortInterval; 37 | self.longInterval = longInterval; 38 | self.delegate = delegate; 39 | startingTime = nil; 40 | tempDate = nil; 41 | currentlyPaused = NO; 42 | } 43 | 44 | return self; 45 | } 46 | 47 | - (float) time 48 | { 49 | return -[startingTime timeIntervalSinceNow]; 50 | } 51 | 52 | -(void)longTimerExpired 53 | { 54 | [self stopTimer]; 55 | [self shortTimerExpired]; 56 | if ([self.delegate respondsToSelector:@selector(longTimerExpired:)]) 57 | { 58 | [self.delegate longTimerExpired:self]; 59 | } 60 | } 61 | 62 | -(void)shortTimerExpired 63 | { 64 | if ( (self.longTimer != nil) && ([self.delegate respondsToSelector:@selector(shortTimerExpired:time:longInterval:)]) ) 65 | { 66 | [self.delegate shortTimerExpired:self time:self.time longInterval:self.longInterval]; 67 | } 68 | } 69 | 70 | - (void) stopTimer 71 | { 72 | [self.shortTimer invalidate]; 73 | [self.longTimer invalidate]; 74 | self.longTimer = nil; 75 | self.shortTimer = nil; 76 | startingTime = nil; 77 | currentlyPaused = NO; 78 | tempDate = nil; 79 | 80 | //Remove Notifications 81 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 82 | } 83 | 84 | - (void) startTimer 85 | { 86 | [self stopTimer]; 87 | self.shortTimer = [NSTimer scheduledTimerWithTimeInterval:self.shortInterval target:self selector:@selector(shortTimerExpired) userInfo:nil repeats:YES]; 88 | self.longTimer = [NSTimer scheduledTimerWithTimeInterval:self.longInterval target:self selector:@selector(longTimerExpired) userInfo:nil repeats:NO]; 89 | startingTime = [[NSDate alloc] init]; 90 | 91 | currentlyPaused = NO; 92 | tempDate = nil; 93 | 94 | //Start Notifications 95 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseTime) name:UIApplicationWillResignActiveNotification object:nil]; 96 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(unPauseTime) name:UIApplicationDidBecomeActiveNotification object:nil]; 97 | } 98 | 99 | -(void)pauseTime 100 | { 101 | [self.longTimer invalidate]; 102 | self.longTimer = nil; 103 | 104 | if (currentlyPaused == NO) tempDate = [[NSDate alloc] init]; 105 | currentlyPaused = YES; 106 | } 107 | 108 | -(void)unPauseTime 109 | { 110 | if (currentlyPaused == NO) return; 111 | 112 | NSTimeInterval totalElapsedTime = [startingTime timeIntervalSinceNow]; 113 | 114 | NSTimeInterval elapsedPauseTime = [tempDate timeIntervalSinceNow]; 115 | startingTime = [startingTime dateByAddingTimeInterval:-elapsedPauseTime]; 116 | tempDate = nil; 117 | 118 | //Create new longTimer 119 | self.longTimer = [NSTimer scheduledTimerWithTimeInterval:(self.longInterval + totalElapsedTime - elapsedPauseTime) target:self selector:@selector(longTimerExpired) userInfo:nil repeats:NO]; 120 | 121 | [self shortTimerExpired]; 122 | } 123 | 124 | - (void)dealloc 125 | { 126 | NSLog(@"dealloc"); 127 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 128 | [self.shortTimer invalidate]; 129 | [self.longTimer invalidate]; 130 | self.longTimer = nil; 131 | self.shortTimer = nil; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Example/Example/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Example 4 | // 5 | // Created by PJ on 2/12/13. 6 | // 7 | // 8 | 9 | #import 10 | #import "GameTimer.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Example/Example/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Example 4 | // 5 | // Created by PJ on 2/12/13. 6 | // 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | 12 | 13 | @interface ViewController () 14 | 15 | @property (weak, nonatomic) IBOutlet UIProgressView *progressBar; 16 | @property (strong, nonatomic) GameTimer *gameTimer; 17 | @property float timerDuration; 18 | 19 | - (IBAction)play:(id)sender; 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad 25 | { 26 | [super viewDidLoad]; 27 | 28 | self.timerDuration = 1; //The timer is set to 2 minutes 29 | self.gameTimer = [[GameTimer alloc] initWithLongInterval:self.timerDuration*60 andShortInterval:5 andDelegate:self]; 30 | } 31 | 32 | - (IBAction)play:(id)sender 33 | { 34 | [self.gameTimer startTimer]; 35 | } 36 | 37 | - (void)dealloc 38 | { 39 | //Since self.gameTimer is a 'strong' property we need to set it to nil to release memory. 40 | [self.gameTimer stop]; 41 | self.gameTimer = nil; 42 | } 43 | 44 | #pragma mark - GameTimer delegate methods 45 | 46 | -(void)longTimerExpired: (GameTimer *)gameTimer 47 | { 48 | //Time is up 49 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"GameTimer" message:@"Time is Up" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 50 | [alert show]; 51 | } 52 | 53 | -(void)shortTimerExpired: (GameTimer *)gameTimer time:(float)time longInterval:(float)longInterval 54 | { 55 | NSLog(@"Short Timer Fired %f", time); 56 | //Update progress bar 57 | [UIView setAnimationsEnabled:NO]; 58 | self.progressBar.progress = time/longInterval; 59 | [UIView setAnimationsEnabled:YES]; 60 | } 61 | @end 62 | -------------------------------------------------------------------------------- /Example/Example/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Example 4 | // 5 | // Created by PJ on 2/12/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Game-Timer-iOS.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint Game-Timer-iOS.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "Game-Timer-iOS" 12 | s.version = "1.0.0" 13 | s.summary = "Light-weight timer that you can use for iOS games." 14 | 15 | s.description = <<-DESC 16 | GameTimer incorporates 2 timers that work in unison - referred to as longTimer and shortTimer. The shortTimer acts as a 'finer' resolution timer that can be used to update a progressbar or continually poll a network connection (for example). It's interval is usually set to a fraction of the longTimer. 17 | 18 | GameTimer automatically pauses when the app enters the BACKGROUND and 'unpauses' when the app is ACTIVE again. 19 | DESC 20 | 21 | s.homepage = "https://github.com/pjebs/Game-Timer-iOS" 22 | 23 | s.license = { :type => "MIT", :file => "LICENSE" } 24 | s.author = { "PJ Engineering and Business Solutions Pty. Ltd." => "enquiries@pjebs.com.au" } 25 | 26 | s.platform = :ios, "6.0" 27 | s.source = { :git => "https://github.com/pjebs/Game-Timer-iOS.git", :tag => "v1.0.0" } 28 | s.source_files = "*.{h,m}" 29 | s.requires_arc = true 30 | 31 | end 32 | -------------------------------------------------------------------------------- /GameTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameTimer.h 3 | // 4 | // Version 1.0 5 | // 6 | // Copyright (c) 2013 PJ Engineering and Business Solutions Pty. Ltd. 7 | // All rights reserved. 8 | // enquiries@pjebs.com.au 9 | // 10 | 11 | //GameTimer incorporates 2 timers - referred to as longTimer and shortTimer. 12 | //The longTimer MUST have a longer timer duration than the shortTimer. 13 | //If that is not the case, then the behaviour of GameTimer is indeterminate and unpredictable. 14 | //The shortTimer acts as a 'finer' resolution timer that can be used to update a progressbar or 15 | //continually poll a network connection (for example). It's interval is usually set to a fraction 16 | //of the longTimer. 17 | // 18 | //Example: The longTimer can be set to 2 minutes (i.e. the time to boil an egg). The shortTimer can be 19 | //set to 1 second so the progressbar updates every 1 second. This represents a fraction of 1/120. 20 | // 21 | //GameTimer automatically pauses when the app enters the BACKGROUND and 'unpauses' when the app is 22 | //ACTIVE again. 23 | 24 | 25 | #import 26 | @protocol GameTimerDelegate; 27 | 28 | @interface GameTimer : NSObject 29 | 30 | 31 | //The 'finer' resolution timer duration. This must be less than longInterval. 32 | //The behaviour of GameTimer is unpredictable otherwise. 33 | //The units are in seconds. It can be as small as 0.1 milliseconds. 34 | @property float shortInterval; 35 | 36 | //The overall timer duration. When this timer expires, then GameTimer deactivates by itself. 37 | //It automatically cleans its memory after deactivating the shortTimer AND longTimer. 38 | //The units are in seconds. It can be as small as 0.1 milliseconds. 39 | @property float longInterval; 40 | 41 | //The amount of time in seconds since GameTimer was started 42 | //excluding when the app enters the background state. 43 | @property (nonatomic, readonly) float time; 44 | 45 | //The delegate must respond to longTimerExpired: method. This is mandatory. 46 | //It is called when the longTimer expires. 47 | @property(nonatomic, weak) IBOutlet id delegate; 48 | 49 | //This method is used to initialise GameTimer. GameTimer MUST NOT be initialised using init: 50 | //Initialising GameTimer does not start the timer. 51 | // 52 | //An example: 53 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; 54 | // 55 | //The above example will create a GameTimer with a longInterval of 120 seconds. It will fire the 56 | //shortTimerExpired: method every 0.5 seconds. 57 | // 58 | //Always allocate GameTimer to the instance variable ONCE (such as in viewDidLoad method). If you need to 59 | //change the settings, stop the timer and then do so using the instance variables 60 | //(i.e. self.gameTimer.longInterval = 3*60;) and then start the timer (i.e. [self.gameTimer start];) 61 | //If you need to deallocate GameTimer, then stop GameTimer first. 62 | //(i.e. [self.gameTimer stop]; self.gameTimer = nil;) 63 | // 64 | //WARNING: If the reference to the newly created gameTimer is changed to point to a BRAND new gameTimer alloc, 65 | //then you must call the 'stopTimer' before reallocating. This is because NSRunLoop keeps a strong reference. 66 | //An example (CORRECT WAY - EVEN THOUGH I RECOMMEND NEVER REALLOCATING): 67 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; //First allocation 68 | // [self.gameTimer stop]; //Even if self.gameTimer is declared as a 'strong' reference, the original does not deallocate 69 | // self.gameTimer = [[GameTimer alloc] initWithLongInterval:5 andShortInterval:2 andDelegate:self]; //Second BRAND NEW allocation 70 | - (id)initWithLongInterval:(float)longInterval andShortInterval: (float)shortInterval andDelegate:(id ) delegate; 71 | 72 | //Stops the timer. This MUST be called from the same thread that called the startTimer method. 73 | - (void) stopTimer; 74 | 75 | //Restarts the timer. 76 | - (void) startTimer; 77 | 78 | //Pauses the timer 79 | -(void)pauseTime; 80 | 81 | //Unpauses the timer 82 | -(void)unPauseTime; 83 | 84 | @end 85 | 86 | 87 | @protocol GameTimerDelegate 88 | 89 | @required 90 | //This method is fired when the longTimer expires. GameTimer will stop at this point. You will need to 91 | //call the startTimer method to restart the timer. It is MANDATORY for the delegate to implement this. 92 | -(void)longTimerExpired: (GameTimer *)gameTimer; 93 | 94 | @optional 95 | //This method is fired when the shortTimer expires. It will continue to fire until the longTimer expires, 96 | //at which point GameTimer will stop. It is OPTIONAL for the delegate to implement this. 97 | //In order to update a progressbar, this method should be implemented and used in conjunction with 98 | //the 'time' value and the 'longInterval' value. 99 | //The method is not expected to fire at precise moments. It is only for tasks such as updating, polling etc. 100 | -(void)shortTimerExpired: (GameTimer *)gameTimer time: (float)time longInterval: (float)longInterval; 101 | @end 102 | 103 | -------------------------------------------------------------------------------- /GameTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameTimer.m 3 | // 4 | // Version 1.0 5 | // 6 | // Copyright (c) 2013 PJ Engineering and Business Solutions Pty. Ltd. 7 | // All rights reserved. 8 | // enquiries@pjebs.com.au 9 | // 10 | 11 | #import "GameTimer.h" 12 | 13 | @interface GameTimer () 14 | { 15 | NSDate *startingTime; 16 | NSDate *tempDate; 17 | BOOL currentlyPaused; 18 | } 19 | 20 | @property (strong, nonatomic) NSTimer *longTimer; 21 | @property (strong, nonatomic) NSTimer *shortTimer; 22 | 23 | -(void)longTimerExpired; 24 | -(void)shortTimerExpired; 25 | 26 | @end 27 | 28 | @implementation GameTimer 29 | 30 | - (id)initWithLongInterval:(float)longInterval andShortInterval: (float)shortInterval andDelegate:(id ) delegate; 31 | { 32 | self = [super init]; 33 | 34 | if (self) 35 | { 36 | self.shortInterval = shortInterval; 37 | self.longInterval = longInterval; 38 | self.delegate = delegate; 39 | startingTime = nil; 40 | tempDate = nil; 41 | currentlyPaused = NO; 42 | } 43 | 44 | return self; 45 | } 46 | 47 | - (float) time 48 | { 49 | return -[startingTime timeIntervalSinceNow]; 50 | } 51 | 52 | -(void)longTimerExpired 53 | { 54 | [self stopTimer]; 55 | [self shortTimerExpired]; 56 | if ([self.delegate respondsToSelector:@selector(longTimerExpired:)]) 57 | { 58 | [self.delegate longTimerExpired:self]; 59 | } 60 | } 61 | 62 | -(void)shortTimerExpired 63 | { 64 | if ( (self.longTimer != nil) && ([self.delegate respondsToSelector:@selector(shortTimerExpired:time:longInterval:)]) ) 65 | { 66 | [self.delegate shortTimerExpired:self time:self.time longInterval:self.longInterval]; 67 | } 68 | } 69 | 70 | - (void) stopTimer 71 | { 72 | [self.shortTimer invalidate]; 73 | [self.longTimer invalidate]; 74 | self.longTimer = nil; 75 | self.shortTimer = nil; 76 | startingTime = nil; 77 | currentlyPaused = NO; 78 | tempDate = nil; 79 | 80 | //Remove Notifications 81 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 82 | } 83 | 84 | - (void) startTimer 85 | { 86 | [self stopTimer]; 87 | self.shortTimer = [NSTimer scheduledTimerWithTimeInterval:self.shortInterval target:self selector:@selector(shortTimerExpired) userInfo:nil repeats:YES]; 88 | self.longTimer = [NSTimer scheduledTimerWithTimeInterval:self.longInterval target:self selector:@selector(longTimerExpired) userInfo:nil repeats:NO]; 89 | startingTime = [[NSDate alloc] init]; 90 | 91 | currentlyPaused = NO; 92 | tempDate = nil; 93 | 94 | //Start Notifications 95 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseTime) name:UIApplicationWillResignActiveNotification object:nil]; 96 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(unPauseTime) name:UIApplicationDidBecomeActiveNotification object:nil]; 97 | } 98 | 99 | -(void)pauseTime 100 | { 101 | [self.longTimer invalidate]; 102 | self.longTimer = nil; 103 | 104 | if (currentlyPaused == NO) tempDate = [[NSDate alloc] init]; 105 | currentlyPaused = YES; 106 | } 107 | 108 | -(void)unPauseTime 109 | { 110 | if (currentlyPaused == NO) return; 111 | 112 | NSTimeInterval totalElapsedTime = [startingTime timeIntervalSinceNow]; 113 | 114 | NSTimeInterval elapsedPauseTime = [tempDate timeIntervalSinceNow]; 115 | startingTime = [startingTime dateByAddingTimeInterval:-elapsedPauseTime]; 116 | tempDate = nil; 117 | 118 | //Create new longTimer 119 | self.longTimer = [NSTimer scheduledTimerWithTimeInterval:(self.longInterval + totalElapsedTime - elapsedPauseTime) target:self selector:@selector(longTimerExpired) userInfo:nil repeats:NO]; 120 | 121 | [self shortTimerExpired]; 122 | } 123 | 124 | - (void)dealloc 125 | { 126 | NSLog(@"dealloc"); 127 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 128 | [self.shortTimer invalidate]; 129 | [self.longTimer invalidate]; 130 | self.longTimer = nil; 131 | self.shortTimer = nil; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 pjebs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Purpose 2 | -------------- 3 | 4 | GameTimer is a light-weight timer that you can use for iOS games. It can be enhanced easily for more complex projects. One use is to update a progressbar whilst the main timer is running. 5 | 6 | GameTimer incorporates 2 timers that work in unison - referred to as *longTimer* and *shortTimer*. 7 | The *shortTimer* acts as a 'finer' resolution timer that can be used to update a progressbar or continually poll a network connection (for example). It's interval is usually set to a fraction of the longTimer. 8 | 9 | GameTimer automatically pauses when the app enters the BACKGROUND and 'unpauses' when the app is ACTIVE again. 10 | 11 | 12 | Supported OS & SDK Versions 13 | ----------------------------- 14 | 15 | * Supported build target - iOS 7.0 16 | * Earliest supported deployment target - iOS 6.0 17 | 18 | ARC Compatibility 19 | ------------------ 20 | 21 | GameTimer requires ARC. 22 | 23 | If you wish to convert your whole project to ARC, then run the Edit > Refactor > Convert to Objective-C ARC... tool in Xcode and make sure all files that you wish to use ARC for are checked. 24 | 25 | Thread Safety 26 | -------------- 27 | 28 | The **stopTimer** method **must** be called from the same thread that called the **startTimer** method. 29 | 30 | Installation 31 | -------------- 32 | 33 | ### CocoaPods 34 | 35 | pod 'Game-Timer-iOS', '~> 1.0' 36 | 37 | ### Standard 38 | 39 | To install GameTimer into your app, drag the **GameTimer.h** and **GameTimer.m** files into your project. 40 | 41 | Add: 42 | ``` 43 | #import "GameTimer.h" 44 | ``` 45 | to the top of any class (*.h or *.m) that will use GameTimer. 46 | 47 | Make sure the class conforms to the GameTimerDelegate protocol: 48 | 49 | Modify .h file: 50 | 51 | @interface ViewController : UIViewController 52 | 53 | Implement Protocol methods such as: 54 | 55 | ``` 56 | -(void)longTimerExpired: (GameTimer *)gameTimer 57 | { 58 | //Time is up 59 | } 60 | ``` 61 | 62 | Declare a instance variable/property: 63 | 64 | ``` 65 | @property (strong, nonatomic) GameTimer *gameTimer; 66 | ``` 67 | 68 | Initialise the timer in a method such as **viewDidLoad**: 69 | 70 | ``` 71 | self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:5 andDelegate:self]; //2 minute timer with short intervals of 5 seconds 72 | ``` 73 | 74 | Start the timer when you want to start: 75 | 76 | ``` 77 | [self.gameTimer startTimer]; 78 | ``` 79 | 80 | Stop the timer prematurely if required: 81 | 82 | ``` 83 | [self.gameTimer stopTimer]; 84 | ``` 85 | 86 | Properties 87 | -------------- 88 | 89 | The 'finer' resolution timer duration. This must be less than *longInterval*. 90 | The behaviour of GameTimer is unpredictable otherwise. 91 | The units are in seconds. It can be as small as 0.1 milliseconds. 92 | 93 | @property float shortInterval; 94 | 95 | The overall timer duration. When this timer expires, then GameTimer deactivates by itself. 96 | It automatically cleans it's memory after deactivating the *shortTimer* **and** *longTimer*. 97 | The units are in seconds. It can be as small as 0.1 milliseconds. 98 | 99 | @property float longInterval; 100 | 101 | The amount of time in seconds since GameTimer was started, excluding when the app enters the BACKGROUND state. 102 | 103 | @property (nonatomic, readonly) float time; 104 | 105 | The delegate must respond to **longTimerExpired:** method. This is **mandatory**. 106 | It is called when the *longTimer* expires. 107 | 108 | @property(nonatomic, weak) IBOutlet id delegate; 109 | 110 | 111 | Methods 112 | -------------- 113 | 114 | This method is used to initialise GameTimer. GameTimer **MUST NOT** be initialised using **init**: 115 | Initialising GameTimer does not automatically begin the timer. 116 | 117 | An example: 118 | 119 | ``` 120 | self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; 121 | ``` 122 | 123 | The above example will create a GameTimer with a *longInterval* of 120 seconds. It will fire the 124 | **shortTimerExpired:** method every 0.5 seconds. 125 | 126 | Always allocate GameTimer to the instance variable **ONCE** (such as in **viewDidLoad** method). If you need to 127 | change the settings, stop the timer and then do so using the properties i.e. 128 | 129 | ``` 130 | [self.gameTimer stop]; 131 | self.gameTimer.longInterval = 3*60; //change settings 132 | [self.gameTimer start]; //then start the timer 133 | ``` 134 | 135 | If you need to deallocate GameTimer, then stop GameTimer first. 136 | i.e. 137 | ``` 138 | [self.gameTimer stop]; 139 | self.gameTimer = nil; 140 | ``` 141 | 142 | Class Initializer. 143 | 144 | - (id)initWithLongInterval:(float)longInterval andShortInterval: (float)shortInterval 145 | andDelegate:(id ) delegate; 146 | 147 | Stops the timer. This **MUST** be called from the same thread that called the **startTimer** method. 148 | 149 | - (void) stopTimer; 150 | 151 | Restarts the timer. 152 | 153 | - (void) startTimer; 154 | 155 | Pauses the timer 156 | 157 | -(void)pauseTime; 158 | 159 | Unpauses the timer 160 | 161 | -(void)unPauseTime; 162 | 163 | 164 | Delegate methods 165 | --------------- 166 | 167 | This method is fired when the *longTimer* expires. GameTimer will stop at this point. You will need to 168 | call the **startTimer** method to restart the timer. It is **mandatory** for the delegate to implement this. 169 | 170 | -(void)longTimerExpired: (GameTimer *)gameTimer; 171 | 172 | This method is fired when the *shortTimer* expires. It will continue to fire until the *longTimer* expires, 173 | at which point GameTimer will stop. It is **optional** for the delegate to implement this. 174 | In order to update a progressbar, this method should be implemented and used in conjunction with 175 | the *time* value and the *longInterval* value. 176 | The method is not expected to fire at precise moments. It is only for tasks such as updating, polling etc. 177 | 178 | -(void)shortTimerExpired: (GameTimer *)gameTimer time: (float)time longInterval: (float)longInterval; 179 | 180 | 181 | Example Projects 182 | --------------- 183 | 184 | The example project will demonstrate how to use the basic features of the class. It will update a progressbar. 185 | Obviously it can be extended to your game engine for example. Imagination is the key. 186 | 187 | 188 | Known Issues 189 | --------------- 190 | 191 | **WARNING:** If the reference to the newly created gameTimer is changed to point to a **brand** new [GameTimer alloc], 192 | then you must call the **stopTimer** method before reallocating. This is because **NSRunLoop** keeps a strong reference. 193 | 194 | An example (CORRECT WAY - EVEN THOUGH I RECOMMEND NEVER REALLOCATING): 195 | 196 | ``` 197 | self.gameTimer = [[GameTimer alloc] initWithLongInterval:2*60 andShortInterval:0.5 andDelegate:self]; //First allocation 198 | [self.gameTimer stop]; 199 | self.gameTimer = [[GameTimer alloc] initWithLongInterval:5 andShortInterval:2 andDelegate:self]; //Second NEW allocation 200 | ``` 201 | 202 | 203 | Any Questions 204 | --------------- 205 | 206 | Feel Free to suggest improvements, fork, report bugs or ask any questions. 207 | 208 | PJ Engineering and Business Solutions Pty. Ltd 209 | http://www.pjebs.com.au 210 | --------------------------------------------------------------------------------