├── .gitignore ├── .project ├── .settings └── com.aptana.editor.common.prefs ├── CHANGELOG.txt ├── Classes ├── .gitignore ├── AsyncImageView.h ├── AsyncImageView.m ├── ComCitytelecomTiparallaxheaderModule.h ├── ComCitytelecomTiparallaxheaderModule.m ├── ComCitytelecomTiparallaxheaderModuleAssets.h ├── ComCitytelecomTiparallaxheaderModuleAssets.m ├── NSObject+JRSwizzle.h ├── NSObject+JRSwizzle.m ├── TiUIListView+ParallaxHeader.h ├── TiUIListView+ParallaxHeader.m ├── TiUIListViewProxy+ProxyParallaxHeader.h ├── TiUIListViewProxy+ProxyParallaxHeader.m ├── UIScrollView+APParallaxHeader.h ├── UIScrollView+APParallaxHeader.m ├── UITableView+HeaderSectionPosition.h └── UITableView+HeaderSectionPosition.m ├── ComCitytelecomTiparallaxheader_Prefix.pch ├── LICENSE ├── LICENSE.txt ├── README.md ├── TiParallaxHeader.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── jameschow.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── jameschow.xcuserdatad │ └── xcschemes │ ├── Build & Test.xcscheme │ ├── TiParallaxHeader.xcscheme │ └── xcschememanagement.plist ├── assets ├── README └── com.citytelecom.tiparallaxheader.js ├── build.py ├── com.citytelecom.tiparallaxheader-iphone-0.1.zip ├── dist ├── com.citytelecom.tiparallaxheader-iphone-0.1.zip └── com.citytelecom.tiparallaxheader-iphone-0.2.zip ├── documentation └── index.md ├── example ├── ParallaxImage.jpg └── app.js ├── hooks ├── README ├── add.py ├── install.py ├── remove.py └── uninstall.py ├── manifest ├── metadata.json ├── module.xcconfig ├── platform └── README ├── timodule.xml └── titanium.xcconfig /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | bin 3 | build 4 | .DS_Store 5 | TiParallaxHeader.xcodeproj/project.xcworkspace/xcuserdata/jameschow.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TiParallaxHeader 4 | 5 | 6 | 7 | 8 | 9 | com.appcelerator.titanium.core.builder 10 | 11 | 12 | 13 | 14 | com.aptana.ide.core.unifiedBuilder 15 | 16 | 17 | 18 | 19 | 20 | com.appcelerator.titanium.mobile.module.nature 21 | com.aptana.projects.webnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/com.aptana.editor.common.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | selectUserAgents=com.appcelerator.titanium.mobile.module.nature\:iphone 3 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Place your change log text here. This file will be incorporated with your app at package time. -------------------------------------------------------------------------------- /Classes/.gitignore: -------------------------------------------------------------------------------- 1 | ComCitytelecomTiparallaxheader.h 2 | ComCitytelecomTiparallaxheader.m 3 | -------------------------------------------------------------------------------- /Classes/AsyncImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncImageView.h 3 | // 4 | // Version 1.5.1 5 | // 6 | // Created by Nick Lockwood on 03/04/2011. 7 | // Copyright (c) 2011 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/AsyncImageView 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import 35 | #pragma GCC diagnostic push 36 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 37 | 38 | 39 | extern NSString *const AsyncImageLoadDidFinish; 40 | extern NSString *const AsyncImageLoadDidFail; 41 | 42 | extern NSString *const AsyncImageImageKey; 43 | extern NSString *const AsyncImageURLKey; 44 | extern NSString *const AsyncImageCacheKey; 45 | extern NSString *const AsyncImageErrorKey; 46 | 47 | 48 | @interface AsyncImageLoader : NSObject 49 | 50 | + (AsyncImageLoader *)sharedLoader; 51 | + (NSCache *)defaultCache; 52 | 53 | @property (nonatomic, strong) NSCache *cache; 54 | @property (nonatomic, assign) NSUInteger concurrentLoads; 55 | @property (nonatomic, assign) NSTimeInterval loadingTimeout; 56 | 57 | - (void)loadImageWithURL:(NSURL *)URL target:(id)target success:(SEL)success failure:(SEL)failure; 58 | - (void)loadImageWithURL:(NSURL *)URL target:(id)target action:(SEL)action; 59 | - (void)loadImageWithURL:(NSURL *)URL; 60 | - (void)cancelLoadingURL:(NSURL *)URL target:(id)target action:(SEL)action; 61 | - (void)cancelLoadingURL:(NSURL *)URL target:(id)target; 62 | - (void)cancelLoadingURL:(NSURL *)URL; 63 | - (void)cancelLoadingImagesForTarget:(id)target action:(SEL)action; 64 | - (void)cancelLoadingImagesForTarget:(id)target; 65 | - (NSURL *)URLForTarget:(id)target action:(SEL)action; 66 | - (NSURL *)URLForTarget:(id)target; 67 | 68 | @end 69 | 70 | 71 | @interface UIImageView(AsyncImageView) 72 | 73 | @property (nonatomic, strong) NSURL *imageURL; 74 | 75 | @end 76 | 77 | 78 | @interface AsyncImageView : UIImageView 79 | 80 | @property (nonatomic, assign) BOOL showActivityIndicator; 81 | @property (nonatomic, assign) UIActivityIndicatorViewStyle activityIndicatorStyle; 82 | @property (nonatomic, assign) NSTimeInterval crossfadeDuration; 83 | 84 | @end 85 | 86 | 87 | #pragma GCC diagnostic pop 88 | 89 | -------------------------------------------------------------------------------- /Classes/AsyncImageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncImageView.m 3 | // 4 | // Version 1.5.1 5 | // 6 | // Created by Nick Lockwood on 03/04/2011. 7 | // Copyright (c) 2011 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/AsyncImageView 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import "AsyncImageView.h" 35 | #import 36 | #import 37 | 38 | 39 | #import 40 | #if !__has_feature(objc_arc) 41 | #error This class requires automatic reference counting 42 | #endif 43 | 44 | 45 | NSString *const AsyncImageLoadDidFinish = @"AsyncImageLoadDidFinish"; 46 | NSString *const AsyncImageLoadDidFail = @"AsyncImageLoadDidFail"; 47 | 48 | NSString *const AsyncImageImageKey = @"image"; 49 | NSString *const AsyncImageURLKey = @"URL"; 50 | NSString *const AsyncImageCacheKey = @"cache"; 51 | NSString *const AsyncImageErrorKey = @"error"; 52 | 53 | 54 | @interface AsyncImageConnection : NSObject 55 | 56 | @property (nonatomic, strong) NSURLConnection *connection; 57 | @property (nonatomic, strong) NSMutableData *data; 58 | @property (nonatomic, strong) NSURL *URL; 59 | @property (nonatomic, strong) NSCache *cache; 60 | @property (nonatomic, strong) id target; 61 | @property (nonatomic, assign) SEL success; 62 | @property (nonatomic, assign) SEL failure; 63 | @property (nonatomic, getter = isLoading) BOOL loading; 64 | @property (nonatomic, getter = isCancelled) BOOL cancelled; 65 | 66 | - (AsyncImageConnection *)initWithURL:(NSURL *)URL 67 | cache:(NSCache *)cache 68 | target:(id)target 69 | success:(SEL)success 70 | failure:(SEL)failure; 71 | 72 | - (void)start; 73 | - (void)cancel; 74 | - (BOOL)isInCache; 75 | 76 | @end 77 | 78 | 79 | @implementation AsyncImageConnection 80 | 81 | - (AsyncImageConnection *)initWithURL:(NSURL *)URL 82 | cache:(NSCache *)cache 83 | target:(id)target 84 | success:(SEL)success 85 | failure:(SEL)failure 86 | { 87 | if ((self = [self init])) 88 | { 89 | self.URL = URL; 90 | self.cache = cache; 91 | self.target = target; 92 | self.success = success; 93 | self.failure = failure; 94 | } 95 | return self; 96 | } 97 | 98 | - (UIImage *)cachedImage 99 | { 100 | if ([self.URL isFileURL]) 101 | { 102 | NSString *path = [[self.URL absoluteURL] path]; 103 | NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; 104 | if ([path hasPrefix:resourcePath]) 105 | { 106 | return [UIImage imageNamed:[path substringFromIndex:[resourcePath length]]]; 107 | } 108 | } 109 | return [self.cache objectForKey:self.URL]; 110 | } 111 | 112 | - (BOOL)isInCache 113 | { 114 | return [self cachedImage] != nil; 115 | } 116 | 117 | - (void)loadFailedWithError:(NSError *)error 118 | { 119 | self.loading = NO; 120 | self.cancelled = NO; 121 | [[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFail 122 | object:self.target 123 | userInfo:@{AsyncImageURLKey: self.URL, 124 | AsyncImageErrorKey: error}]; 125 | } 126 | 127 | - (void)cacheImage:(UIImage *)image 128 | { 129 | if (!self.cancelled) 130 | { 131 | if (image && self.URL) 132 | { 133 | BOOL storeInCache = YES; 134 | if ([self.URL isFileURL]) 135 | { 136 | if ([[[self.URL absoluteURL] path] hasPrefix:[[NSBundle mainBundle] resourcePath]]) 137 | { 138 | //do not store in cache 139 | storeInCache = NO; 140 | } 141 | } 142 | if (storeInCache) 143 | { 144 | [self.cache setObject:image forKey:self.URL]; 145 | } 146 | } 147 | 148 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: 149 | image, AsyncImageImageKey, 150 | self.URL, AsyncImageURLKey, 151 | nil]; 152 | if (self.cache) 153 | { 154 | userInfo[AsyncImageCacheKey] = self.cache; 155 | } 156 | 157 | self.loading = NO; 158 | [[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFinish 159 | object:self.target 160 | userInfo:[userInfo copy]]; 161 | } 162 | else 163 | { 164 | self.loading = NO; 165 | self.cancelled = NO; 166 | } 167 | } 168 | 169 | - (void)processDataInBackground:(NSData *)data 170 | { 171 | @synchronized ([self class]) 172 | { 173 | if (!self.cancelled) 174 | { 175 | UIImage *image = [[UIImage alloc] initWithData:data]; 176 | if (image) 177 | { 178 | //redraw to prevent deferred decompression 179 | UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); 180 | [image drawAtPoint:CGPointZero]; 181 | image = UIGraphicsGetImageFromCurrentImageContext(); 182 | UIGraphicsEndImageContext(); 183 | 184 | //add to cache (may be cached already but it doesn't matter) 185 | [self performSelectorOnMainThread:@selector(cacheImage:) 186 | withObject:image 187 | waitUntilDone:YES]; 188 | } 189 | else 190 | { 191 | @autoreleasepool 192 | { 193 | NSError *error = [NSError errorWithDomain:@"AsyncImageLoader" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Invalid image data"}]; 194 | [self performSelectorOnMainThread:@selector(loadFailedWithError:) withObject:error waitUntilDone:YES]; 195 | } 196 | } 197 | } 198 | else 199 | { 200 | //clean up 201 | [self performSelectorOnMainThread:@selector(cacheImage:) 202 | withObject:nil 203 | waitUntilDone:YES]; 204 | } 205 | } 206 | } 207 | 208 | - (void)connection:(__unused NSURLConnection *)connection didReceiveResponse:(__unused NSURLResponse *)response 209 | { 210 | self.data = [NSMutableData data]; 211 | } 212 | 213 | - (void)connection:(__unused NSURLConnection *)connection didReceiveData:(NSData *)data 214 | { 215 | //add data 216 | [self.data appendData:data]; 217 | } 218 | 219 | - (void)connectionDidFinishLoading:(__unused NSURLConnection *)connection 220 | { 221 | [self performSelectorInBackground:@selector(processDataInBackground:) withObject:self.data]; 222 | self.connection = nil; 223 | self.data = nil; 224 | } 225 | 226 | - (void)connection:(__unused NSURLConnection *)connection didFailWithError:(NSError *)error 227 | { 228 | self.connection = nil; 229 | self.data = nil; 230 | [self loadFailedWithError:error]; 231 | } 232 | 233 | - (void)start 234 | { 235 | if (self.loading && !self.cancelled) 236 | { 237 | return; 238 | } 239 | 240 | //begin loading 241 | self.loading = YES; 242 | self.cancelled = NO; 243 | 244 | //check for nil URL 245 | if (self.URL == nil) 246 | { 247 | [self cacheImage:nil]; 248 | return; 249 | } 250 | 251 | //check for cached image 252 | UIImage *image = [self cachedImage]; 253 | if (image) 254 | { 255 | //add to cache (cached already but it doesn't matter) 256 | [self performSelectorOnMainThread:@selector(cacheImage:) 257 | withObject:image 258 | waitUntilDone:NO]; 259 | return; 260 | } 261 | 262 | //begin load 263 | NSURLRequest *request = [NSURLRequest requestWithURL:self.URL 264 | cachePolicy:NSURLRequestReloadIgnoringLocalCacheData 265 | timeoutInterval:[AsyncImageLoader sharedLoader].loadingTimeout]; 266 | 267 | self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 268 | [self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 269 | [self.connection start]; 270 | } 271 | 272 | - (void)cancel 273 | { 274 | self.cancelled = YES; 275 | [self.connection cancel]; 276 | self.connection = nil; 277 | self.data = nil; 278 | } 279 | 280 | @end 281 | 282 | 283 | @interface AsyncImageLoader () 284 | 285 | @property (nonatomic, strong) NSMutableArray *connections; 286 | 287 | @end 288 | 289 | 290 | @implementation AsyncImageLoader 291 | 292 | + (AsyncImageLoader *)sharedLoader 293 | { 294 | static AsyncImageLoader *sharedInstance = nil; 295 | if (sharedInstance == nil) 296 | { 297 | sharedInstance = [(AsyncImageLoader *)[self alloc] init]; 298 | } 299 | return sharedInstance; 300 | } 301 | 302 | + (NSCache *)defaultCache 303 | { 304 | static NSCache *sharedCache = nil; 305 | if (sharedCache == nil) 306 | { 307 | sharedCache = [[NSCache alloc] init]; 308 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(__unused NSNotification *note) { 309 | 310 | [sharedCache removeAllObjects]; 311 | }]; 312 | } 313 | return sharedCache; 314 | } 315 | 316 | - (AsyncImageLoader *)init 317 | { 318 | if ((self = [super init])) 319 | { 320 | self.cache = [[self class] defaultCache]; 321 | _concurrentLoads = 2; 322 | _loadingTimeout = 60.0; 323 | _connections = [[NSMutableArray alloc] init]; 324 | [[NSNotificationCenter defaultCenter] addObserver:self 325 | selector:@selector(imageLoaded:) 326 | name:AsyncImageLoadDidFinish 327 | object:nil]; 328 | [[NSNotificationCenter defaultCenter] addObserver:self 329 | selector:@selector(imageFailed:) 330 | name:AsyncImageLoadDidFail 331 | object:nil]; 332 | } 333 | return self; 334 | } 335 | 336 | - (void)updateQueue 337 | { 338 | //start connections 339 | NSUInteger count = 0; 340 | for (AsyncImageConnection *connection in self.connections) 341 | { 342 | if (![connection isLoading]) 343 | { 344 | if ([connection isInCache]) 345 | { 346 | [connection start]; 347 | } 348 | else if (count < self.concurrentLoads) 349 | { 350 | count ++; 351 | [connection start]; 352 | } 353 | } 354 | } 355 | } 356 | 357 | - (void)imageLoaded:(NSNotification *)notification 358 | { 359 | //complete connections for URL 360 | NSURL *URL = (notification.userInfo)[AsyncImageURLKey]; 361 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 362 | { 363 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 364 | if (connection.URL == URL || [connection.URL isEqual:URL]) 365 | { 366 | //cancel earlier connections for same target/action 367 | for (NSInteger j = i - 1; j >= 0; j--) 368 | { 369 | AsyncImageConnection *earlier = self.connections[(NSUInteger)j]; 370 | if (earlier.target == connection.target && 371 | earlier.success == connection.success) 372 | { 373 | [earlier cancel]; 374 | [self.connections removeObjectAtIndex:(NSUInteger)j]; 375 | i--; 376 | } 377 | } 378 | 379 | //cancel connection (in case it's a duplicate) 380 | [connection cancel]; 381 | 382 | //perform action 383 | UIImage *image = (notification.userInfo)[AsyncImageImageKey]; 384 | ((void (*)(id, SEL, id, id))objc_msgSend)(connection.target, connection.success, image, connection.URL); 385 | 386 | //remove from queue 387 | [self.connections removeObjectAtIndex:(NSUInteger)i]; 388 | } 389 | } 390 | 391 | //update the queue 392 | [self updateQueue]; 393 | } 394 | 395 | - (void)imageFailed:(NSNotification *)notification 396 | { 397 | //remove connections for URL 398 | NSURL *URL = (notification.userInfo)[AsyncImageURLKey]; 399 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 400 | { 401 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 402 | if ([connection.URL isEqual:URL]) 403 | { 404 | //cancel connection (in case it's a duplicate) 405 | [connection cancel]; 406 | 407 | //perform failure action 408 | if (connection.failure) 409 | { 410 | NSError *error = (notification.userInfo)[AsyncImageErrorKey]; 411 | ((void (*)(id, SEL, id, id))objc_msgSend)(connection.target, connection.failure, error, URL); 412 | } 413 | 414 | //remove from queue 415 | [self.connections removeObjectAtIndex:(NSUInteger)i]; 416 | } 417 | } 418 | 419 | //update the queue 420 | [self updateQueue]; 421 | } 422 | 423 | - (void)loadImageWithURL:(NSURL *)URL target:(id)target success:(SEL)success failure:(SEL)failure 424 | { 425 | //check cache 426 | UIImage *image = [self.cache objectForKey:URL]; 427 | if (image) 428 | { 429 | [self cancelLoadingImagesForTarget:self action:success]; 430 | if (success) 431 | { 432 | dispatch_async(dispatch_get_main_queue(), ^(void) { 433 | 434 | ((void (*)(id, SEL, id, id))objc_msgSend)(target, success, image, URL); 435 | }); 436 | } 437 | return; 438 | } 439 | 440 | //create new connection 441 | AsyncImageConnection *connection = [[AsyncImageConnection alloc] initWithURL:URL 442 | cache:self.cache 443 | target:target 444 | success:success 445 | failure:failure]; 446 | BOOL added = NO; 447 | for (NSUInteger i = 0; i < [self.connections count]; i++) 448 | { 449 | AsyncImageConnection *existingConnection = self.connections[i]; 450 | if (!existingConnection.loading) 451 | { 452 | [self.connections insertObject:connection atIndex:i]; 453 | added = YES; 454 | break; 455 | } 456 | } 457 | if (!added) 458 | { 459 | [self.connections addObject:connection]; 460 | } 461 | 462 | [self updateQueue]; 463 | } 464 | 465 | - (void)loadImageWithURL:(NSURL *)URL target:(id)target action:(SEL)action 466 | { 467 | [self loadImageWithURL:URL target:target success:action failure:NULL]; 468 | } 469 | 470 | - (void)loadImageWithURL:(NSURL *)URL 471 | { 472 | [self loadImageWithURL:URL target:nil success:NULL failure:NULL]; 473 | } 474 | 475 | - (void)cancelLoadingURL:(NSURL *)URL target:(id)target action:(SEL)action 476 | { 477 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 478 | { 479 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 480 | if ([connection.URL isEqual:URL] && connection.target == target && connection.success == action) 481 | { 482 | [connection cancel]; 483 | [self.connections removeObjectAtIndex:(NSUInteger)i]; 484 | } 485 | } 486 | } 487 | 488 | - (void)cancelLoadingURL:(NSURL *)URL target:(id)target 489 | { 490 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 491 | { 492 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 493 | if ([connection.URL isEqual:URL] && connection.target == target) 494 | { 495 | [connection cancel]; 496 | [self.connections removeObjectAtIndex:(NSUInteger)i]; 497 | } 498 | } 499 | } 500 | 501 | - (void)cancelLoadingURL:(NSURL *)URL 502 | { 503 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 504 | { 505 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 506 | if ([connection.URL isEqual:URL]) 507 | { 508 | [connection cancel]; 509 | [self.connections removeObjectAtIndex:(NSUInteger)i]; 510 | } 511 | } 512 | } 513 | 514 | - (void)cancelLoadingImagesForTarget:(id)target action:(SEL)action 515 | { 516 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 517 | { 518 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 519 | if (connection.target == target && connection.success == action) 520 | { 521 | [connection cancel]; 522 | } 523 | } 524 | } 525 | 526 | - (void)cancelLoadingImagesForTarget:(id)target 527 | { 528 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 529 | { 530 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 531 | if (connection.target == target) 532 | { 533 | [connection cancel]; 534 | } 535 | } 536 | } 537 | 538 | - (NSURL *)URLForTarget:(id)target action:(SEL)action 539 | { 540 | //return the most recent image URL assigned to the target for the given action 541 | //this is not neccesarily the next image that will be assigned 542 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 543 | { 544 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 545 | if (connection.target == target && connection.success == action) 546 | { 547 | return connection.URL; 548 | } 549 | } 550 | return nil; 551 | } 552 | 553 | - (NSURL *)URLForTarget:(id)target 554 | { 555 | //return the most recent image URL assigned to the target 556 | //this is not neccesarily the next image that will be assigned 557 | for (NSInteger i = (NSInteger)[self.connections count] - 1; i >= 0; i--) 558 | { 559 | AsyncImageConnection *connection = self.connections[(NSUInteger)i]; 560 | if (connection.target == target) 561 | { 562 | return connection.URL; 563 | } 564 | } 565 | return nil; 566 | } 567 | 568 | - (void)dealloc 569 | { 570 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 571 | } 572 | 573 | @end 574 | 575 | 576 | @implementation UIImageView(AsyncImageView) 577 | 578 | - (void)setImageURL:(NSURL *)imageURL 579 | { 580 | [[AsyncImageLoader sharedLoader] loadImageWithURL:imageURL target:self action:@selector(setImage:)]; 581 | } 582 | 583 | - (NSURL *)imageURL 584 | { 585 | return [[AsyncImageLoader sharedLoader] URLForTarget:self action:@selector(setImage:)]; 586 | } 587 | 588 | @end 589 | 590 | 591 | @interface AsyncImageView () 592 | 593 | @property (nonatomic, strong) UIActivityIndicatorView *activityView; 594 | 595 | @end 596 | 597 | 598 | @implementation AsyncImageView 599 | 600 | - (void)setUp 601 | { 602 | self.showActivityIndicator = (self.image == nil); 603 | self.activityIndicatorStyle = UIActivityIndicatorViewStyleGray; 604 | self.crossfadeDuration = 0.4; 605 | } 606 | 607 | - (id)initWithFrame:(CGRect)frame 608 | { 609 | if ((self = [super initWithFrame:frame])) 610 | { 611 | [self setUp]; 612 | } 613 | return self; 614 | } 615 | 616 | - (id)initWithCoder:(NSCoder *)aDecoder 617 | { 618 | if ((self = [super initWithCoder:aDecoder])) 619 | { 620 | [self setUp]; 621 | } 622 | return self; 623 | } 624 | 625 | - (void)setImageURL:(NSURL *)imageURL 626 | { 627 | UIImage *image = [[AsyncImageLoader sharedLoader].cache objectForKey:imageURL]; 628 | if (image) 629 | { 630 | self.image = image; 631 | return; 632 | } 633 | super.imageURL = imageURL; 634 | if (self.showActivityIndicator && !self.image && imageURL) 635 | { 636 | if (self.activityView == nil) 637 | { 638 | self.activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorStyle]; 639 | self.activityView.hidesWhenStopped = YES; 640 | self.activityView.center = CGPointMake(self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f); 641 | self.activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; 642 | [self addSubview:self.activityView]; 643 | } 644 | [self.activityView startAnimating]; 645 | } 646 | } 647 | 648 | - (void)setActivityIndicatorStyle:(UIActivityIndicatorViewStyle)style 649 | { 650 | _activityIndicatorStyle = style; 651 | [self.activityView removeFromSuperview]; 652 | self.activityView = nil; 653 | } 654 | 655 | - (void)setImage:(UIImage *)image 656 | { 657 | if (image != self.image && self.crossfadeDuration) 658 | { 659 | //jump through a few hoops to avoid QuartzCore framework dependency 660 | CAAnimation *animation = [NSClassFromString(@"CATransition") animation]; 661 | [animation setValue:@"kCATransitionFade" forKey:@"type"]; 662 | animation.duration = self.crossfadeDuration; 663 | [self.layer addAnimation:animation forKey:nil]; 664 | } 665 | super.image = image; 666 | [self.activityView stopAnimating]; 667 | } 668 | 669 | - (void)dealloc 670 | { 671 | [[AsyncImageLoader sharedLoader] cancelLoadingURL:self.imageURL target:self]; 672 | } 673 | 674 | @end 675 | -------------------------------------------------------------------------------- /Classes/ComCitytelecomTiparallaxheaderModule.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Your Copyright Here 3 | * 4 | * Appcelerator Titanium is Copyright (c) 2009-2010 by Appcelerator, Inc. 5 | * and licensed under the Apache Public License (version 2) 6 | */ 7 | #import "TiModule.h" 8 | 9 | @interface ComCitytelecomTiparallaxheaderModule : TiModule 10 | { 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Classes/ComCitytelecomTiparallaxheaderModule.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Your Copyright Here 3 | * 4 | * Appcelerator Titanium is Copyright (c) 2009-2010 by Appcelerator, Inc. 5 | * and licensed under the Apache Public License (version 2) 6 | */ 7 | #import "ComCitytelecomTiparallaxheaderModule.h" 8 | #import "TiBase.h" 9 | #import "TiHost.h" 10 | #import "TiUtils.h" 11 | #import "TiUIListView+ParallaxHeader.h" 12 | #import "UITableView+HeaderSectionPosition.h" 13 | 14 | @implementation ComCitytelecomTiparallaxheaderModule 15 | 16 | #pragma mark Internal 17 | 18 | // this is generated for your module, please do not change it 19 | -(id)moduleGUID 20 | { 21 | return @"d9d5b1e0-b0fc-4814-bb86-41dc12db6808"; 22 | } 23 | 24 | // this is generated for your module, please do not change it 25 | -(NSString*)moduleId 26 | { 27 | return @"com.citytelecom.tiparallaxheader"; 28 | } 29 | 30 | #pragma mark Lifecycle 31 | 32 | -(void)startup 33 | { 34 | // this method is called when the module is first loaded 35 | // you *must* call the superclass 36 | [super startup]; 37 | 38 | //swizzle out the uitableview for the insets 39 | [UITableView swizzle]; 40 | 41 | //swizzle the listview 42 | [TiUIListView swizzle]; 43 | 44 | NSLog(@"[INFO] %@ loaded",self); 45 | } 46 | 47 | -(void)shutdown:(id)sender 48 | { 49 | // this method is called when the module is being unloaded 50 | // typically this is during shutdown. make sure you don't do too 51 | // much processing here or the app will be quit forceably 52 | 53 | // you *must* call the superclass 54 | [super shutdown:sender]; 55 | } 56 | 57 | #pragma mark Cleanup 58 | 59 | -(void)dealloc 60 | { 61 | // release any resources that have been retained by the module 62 | [super dealloc]; 63 | } 64 | 65 | #pragma mark Internal Memory Management 66 | 67 | -(void)didReceiveMemoryWarning:(NSNotification*)notification 68 | { 69 | // optionally release any resources that can be dynamically 70 | // reloaded once memory is available - such as caches 71 | [super didReceiveMemoryWarning:notification]; 72 | } 73 | 74 | #pragma mark Listener Notifications 75 | 76 | -(void)_listenerAdded:(NSString *)type count:(int)count 77 | { 78 | if (count == 1 && [type isEqualToString:@"my_event"]) 79 | { 80 | // the first (of potentially many) listener is being added 81 | // for event named 'my_event' 82 | } 83 | } 84 | 85 | -(void)_listenerRemoved:(NSString *)type count:(int)count 86 | { 87 | if (count == 0 && [type isEqualToString:@"my_event"]) 88 | { 89 | // the last listener called for event named 'my_event' has 90 | // been removed, we can optionally clean up any resources 91 | // since no body is listening at this point for that event 92 | } 93 | } 94 | 95 | #pragma Public APIs 96 | 97 | -(id)example:(id)args 98 | { 99 | // example method 100 | return @"hello world"; 101 | } 102 | 103 | -(id)exampleProp 104 | { 105 | // example property getter 106 | return @"hello world"; 107 | } 108 | 109 | -(void)setExampleProp:(id)value 110 | { 111 | // example property setter 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /Classes/ComCitytelecomTiparallaxheaderModuleAssets.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a generated file. Do not edit or your changes will be lost 3 | */ 4 | 5 | @interface ComCitytelecomTiparallaxheaderModuleAssets : NSObject 6 | { 7 | } 8 | - (NSData*) moduleAsset; 9 | - (NSData*) resolveModuleAsset:(NSString*)path; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Classes/ComCitytelecomTiparallaxheaderModuleAssets.m: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a generated file. Do not edit or your changes will be lost 3 | */ 4 | #import "ComCitytelecomTiparallaxheaderModuleAssets.h" 5 | 6 | extern NSData* filterDataInRange(NSData* thedata, NSRange range); 7 | 8 | @implementation ComCitytelecomTiparallaxheaderModuleAssets 9 | 10 | - (NSData*) moduleAsset 11 | { 12 | //##TI_AUTOGEN_BEGIN asset 13 | 14 | static UInt8 data[] = { 15 | 0xa0,0x50,0xfa,0xda,0xff,0xf0,0x21,0x77,0xeb,0x69,0x65,0x61,0xeb,0x2a,0x5b,0x5b,0x2f,0x06,0x60,0xdc 16 | ,0x4f,0x34,0xbf,0x49,0x00,0x12,0x57,0x6a,0x40,0x0a,0x0e,0x9c,0xf9,0x08,0xbb,0xb5,0x2b,0x86,0x45,0x85 17 | ,0xdd,0x68,0x75,0xb7,0x00,0x12,0x63,0x98,0x82,0xa7,0xf8,0x15,0xb3,0xcf,0x20,0x8c,0x98,0xa7,0x14,0x27 18 | ,0x53,0x96,0xc1,0xb4 }; 19 | static NSRange ranges[] = { 20 | {0,32} 21 | }; 22 | static NSDictionary *map = nil; 23 | if (map == nil) { 24 | map = [[NSDictionary alloc] initWithObjectsAndKeys: 25 | [NSNumber numberWithInteger:0], @"com_citytelecom_tiparallaxheader_js", 26 | nil]; 27 | } 28 | 29 | 30 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[0]); 31 | //##TI_AUTOGEN_END asset 32 | } 33 | 34 | - (NSData*) resolveModuleAsset:(NSString*)path 35 | { 36 | //##TI_AUTOGEN_BEGIN resolve_asset 37 | 38 | static UInt8 data[] = { 39 | 0x79,0xd0,0x56,0x08,0x95,0xbd,0xa3,0xa2,0x63,0x87,0xe2,0x38,0x47,0xf9,0x41,0x2b,0x0e,0x71,0xc6,0x71 40 | ,0xa8,0xc7,0x1e,0x6e,0x83,0xc7,0xce,0xb2,0x4b,0x00,0xa8,0xe2,0x39,0x2e,0xb2,0xc0,0x71,0xbd,0xdd,0x46 41 | ,0x4a,0x8f,0x44,0x72,0xfb,0x41,0x8a,0x72,0x36,0x61,0x0d,0xad,0x0f,0xc5,0x4f,0x42,0x3f,0x58,0xb3,0x5f 42 | ,0x20,0x40,0xb0,0x58 }; 43 | static NSRange ranges[] = { 44 | {0,32} 45 | }; 46 | static NSDictionary *map = nil; 47 | if (map == nil) { 48 | map = [[NSDictionary alloc] initWithObjectsAndKeys: 49 | [NSNumber numberWithInteger:0], @"com_citytelecom_tiparallaxheader_js", 50 | nil]; 51 | } 52 | 53 | 54 | NSNumber *index = [map objectForKey:path]; 55 | if (index == nil) { 56 | return nil; 57 | } 58 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[index.integerValue]); 59 | //##TI_AUTOGEN_END resolve_asset 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Classes/NSObject+JRSwizzle.h: -------------------------------------------------------------------------------- 1 | // JRSwizzle.h semver:1.0 2 | // Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com 3 | // Some rights reserved: http://opensource.org/licenses/MIT 4 | // https://github.com/rentzsch/jrswizzle 5 | 6 | #import 7 | 8 | @interface NSObject (JRSwizzle) 9 | 10 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_; 11 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Classes/NSObject+JRSwizzle.m: -------------------------------------------------------------------------------- 1 | // JRSwizzle.m semver:1.0 2 | // Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com 3 | // Some rights reserved: http://opensource.org/licenses/MIT 4 | // https://github.com/rentzsch/jrswizzle 5 | 6 | #import "NSObject+JRSwizzle.h" 7 | 8 | #if TARGET_OS_IPHONE 9 | #import 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | #define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \ 16 | if (ERROR_VAR) { \ 17 | NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \ 18 | *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ 19 | code:-1 \ 20 | userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ 21 | } 22 | #define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__) 23 | 24 | #if OBJC_API_VERSION >= 2 25 | #define GetClass(obj) object_getClass(obj) 26 | #else 27 | #define GetClass(obj) (obj ? obj->isa : Nil) 28 | #endif 29 | 30 | @implementation NSObject (JRSwizzle) 31 | 32 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ { 33 | #if OBJC_API_VERSION >= 2 34 | Method origMethod = class_getInstanceMethod(self, origSel_); 35 | if (!origMethod) { 36 | #if TARGET_OS_IPHONE 37 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); 38 | #else 39 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 40 | #endif 41 | return NO; 42 | } 43 | 44 | Method altMethod = class_getInstanceMethod(self, altSel_); 45 | if (!altMethod) { 46 | #if TARGET_OS_IPHONE 47 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]); 48 | #else 49 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 50 | #endif 51 | return NO; 52 | } 53 | 54 | class_addMethod(self, 55 | origSel_, 56 | class_getMethodImplementation(self, origSel_), 57 | method_getTypeEncoding(origMethod)); 58 | class_addMethod(self, 59 | altSel_, 60 | class_getMethodImplementation(self, altSel_), 61 | method_getTypeEncoding(altMethod)); 62 | 63 | method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); 64 | return YES; 65 | #else 66 | // Scan for non-inherited methods. 67 | Method directOriginalMethod = NULL, directAlternateMethod = NULL; 68 | 69 | void *iterator = NULL; 70 | struct objc_method_list *mlist = class_nextMethodList(self, &iterator); 71 | while (mlist) { 72 | int method_index = 0; 73 | for (; method_index < mlist->method_count; method_index++) { 74 | if (mlist->method_list[method_index].method_name == origSel_) { 75 | assert(!directOriginalMethod); 76 | directOriginalMethod = &mlist->method_list[method_index]; 77 | } 78 | if (mlist->method_list[method_index].method_name == altSel_) { 79 | assert(!directAlternateMethod); 80 | directAlternateMethod = &mlist->method_list[method_index]; 81 | } 82 | } 83 | mlist = class_nextMethodList(self, &iterator); 84 | } 85 | 86 | // If either method is inherited, copy it up to the target class to make it non-inherited. 87 | if (!directOriginalMethod || !directAlternateMethod) { 88 | Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; 89 | if (!directOriginalMethod) { 90 | inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); 91 | if (!inheritedOriginalMethod) { 92 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 93 | return NO; 94 | } 95 | } 96 | if (!directAlternateMethod) { 97 | inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); 98 | if (!inheritedAlternateMethod) { 99 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 100 | return NO; 101 | } 102 | } 103 | 104 | int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; 105 | struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); 106 | hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind 107 | hoisted_method_list->method_count = hoisted_method_count; 108 | Method hoisted_method = hoisted_method_list->method_list; 109 | 110 | if (!directOriginalMethod) { 111 | bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); 112 | directOriginalMethod = hoisted_method++; 113 | } 114 | if (!directAlternateMethod) { 115 | bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); 116 | directAlternateMethod = hoisted_method; 117 | } 118 | class_addMethods(self, hoisted_method_list); 119 | } 120 | 121 | // Swizzle. 122 | IMP temp = directOriginalMethod->method_imp; 123 | directOriginalMethod->method_imp = directAlternateMethod->method_imp; 124 | directAlternateMethod->method_imp = temp; 125 | 126 | return YES; 127 | #endif 128 | } 129 | 130 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ { 131 | return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_]; 132 | } 133 | 134 | @end -------------------------------------------------------------------------------- /Classes/TiUIListView+ParallaxHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // TiUIListView+ParallaxHeader.h 3 | // TiParallaxHeader 4 | // 5 | // Created by James Chow on 17/04/2014. 6 | // 7 | // 8 | 9 | #import "TiUIView.h" 10 | #import "TiUIListViewProxy.h" 11 | #import "TiUIListView.h" 12 | #import "TiViewProxy.h" 13 | 14 | @interface TiUIListView (ParallaxHeader) 15 | 16 | @property (nonatomic, retain) UIImage * currentImage; 17 | @property (nonatomic, retain) TiViewProxy * currentHeaderView; 18 | 19 | + (void) swizzle; 20 | 21 | -(void)addParallaxWithView:(TiViewProxy *)headerview withHeight: (CGFloat) height; 22 | -(void)addParallaxWithImage:(NSString *)url forHeight: (CGFloat) height; 23 | -(void)setSectionHeaderInset:(CGFloat) height; 24 | -(void)setFadeoutOverHeight:(NSNumber*) height; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Classes/TiUIListView+ParallaxHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // TiUIListView+ParallaxHeader.m 3 | // TiParallaxHeader 4 | // 5 | // Created by James Chow on 17/04/2014. 6 | // 7 | // 8 | 9 | #import "TiUIListView+ParallaxHeader.h" 10 | #import "TiUtils.h" 11 | #import "TiUIViewProxy.h" 12 | #import "UIScrollView+APParallaxHeader.h" 13 | #import "UITableView+HeaderSectionPosition.h" 14 | #import "NSObject+JRSwizzle.h" 15 | #import 16 | 17 | @implementation TiUIListView (ParallaxHeader) 18 | 19 | + (void) swizzle 20 | { 21 | NSError *error = nil; 22 | [TiUIListView jr_swizzleMethod:@selector(tableView:viewForHeaderInSection:) withMethod:@selector(swizzle_tableView:viewForHeaderInSection:) error:&error]; 23 | 24 | if (error != nil) { 25 | NSLog(@"[ERROR] %@", [error localizedDescription]); 26 | } 27 | } 28 | 29 | 30 | -(void)addParallaxWithView:(TiViewProxy *)headerview withHeight: (CGFloat) height 31 | { 32 | self.currentHeaderView = headerview; 33 | 34 | [self.tableView addParallaxWithView: headerview andHeight:height]; 35 | } 36 | 37 | -(void)addParallaxWithImage:(NSString *)url forHeight:(CGFloat)height 38 | { 39 | [self.tableView addParallaxWithImage:url andHeight:height]; 40 | } 41 | 42 | -(void)setSectionHeaderInset:(CGFloat) height 43 | { 44 | [self.tableView setHeaderViewInsets:UIEdgeInsetsMake(height, 0, 0, 0)]; 45 | } 46 | 47 | -(void)setFadeoutOverHeight:(NSNumber*) height 48 | { 49 | [self.tableView setFadeoutOverHeight:height]; 50 | } 51 | 52 | -(void)setCurrentImage:(UIImage *)_currentImage 53 | { 54 | objc_setAssociatedObject(self, @selector(currentImage), _currentImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 55 | }; 56 | 57 | -(UIImage*)currentImage 58 | { 59 | return objc_getAssociatedObject(self, @selector(currentImage)); 60 | } 61 | 62 | -(void)setCurrentHeaderView:(TiViewProxy *)_currentHeaderView 63 | { 64 | objc_setAssociatedObject(self, @selector(currentHeaderView), _currentHeaderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 65 | }; 66 | 67 | -(TiViewProxy*)currentHeaderView 68 | { 69 | return objc_getAssociatedObject(self, @selector(currentHeaderView)); 70 | } 71 | 72 | - (UIView *)swizzle_tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 73 | { 74 | 75 | [TiUIListView jr_swizzleMethod:@selector(tableView:viewForHeaderInSection:) withMethod:@selector(swizzle_tableView:viewForHeaderInSection:) error:nil]; 76 | 77 | //using a plain UI view we need to use tags 78 | //http://stackoverflow.com/questions/20188049/uitableview-headerviewforsection-returns-null 79 | 80 | UIView * sectionHeView = [self tableView:tableView viewForHeaderInSection:section]; 81 | [sectionHeView setTag:100+section]; 82 | 83 | [TiUIListView jr_swizzleMethod:@selector(tableView:viewForHeaderInSection:) withMethod:@selector(swizzle_tableView:viewForHeaderInSection:) error:nil]; 84 | 85 | return sectionHeView; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Classes/TiUIListViewProxy+ProxyParallaxHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // TiUIListViewProxy+ProxyParallaxHeader.h 3 | // TiParallaxHeader 4 | // 5 | // Created by James Chow on 17/04/2014. 6 | // 7 | // 8 | 9 | #import "TiUIListViewProxy.h" 10 | #import "TiUIViewProxy.h" 11 | #import "TiProxy.h" 12 | #import 13 | 14 | @interface TiUIListViewProxy (ProxyParallaxHeader) 15 | 16 | @property (nonatomic, retain) UIImage * currentImage; 17 | 18 | -(void)addParallaxWithView:(id)args; 19 | -(void)addParallaxWithImage:(id)args; 20 | -(void)setSectionHeaderInset:(id)args; 21 | -(void)setFadeoutOverHeight:(id)args; 22 | @end 23 | -------------------------------------------------------------------------------- /Classes/TiUIListViewProxy+ProxyParallaxHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // TiUIListViewProxy+ProxyParallaxHeader.m 3 | // TiParallaxHeader 4 | // 5 | // Created by James Chow on 17/04/2014. 6 | // 7 | // 8 | 9 | #import "TiUIListViewProxy+ProxyParallaxHeader.h" 10 | #import "TiUIListView+ParallaxHeader.h" 11 | #import "TiUIListViewProxy.h" 12 | #import "TiProxy.h" 13 | #import 14 | 15 | @implementation TiUIListViewProxy (ProxyParallaxHeader) 16 | 17 | 18 | -(void)setCurrentImage:(UIImage *)_currentImage 19 | { 20 | objc_setAssociatedObject(self, @selector(currentImage), _currentImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 21 | }; 22 | 23 | -(UIImage*)currentImage 24 | { 25 | return objc_getAssociatedObject(self, @selector(currentImage)); 26 | } 27 | 28 | -(void)addParallaxWithView:(id)args 29 | { 30 | ENSURE_UI_THREAD_1_ARG(args); 31 | 32 | TiViewProxy *headerView = nil; 33 | ENSURE_ARG_AT_INDEX(headerView,args,0,TiViewProxy); 34 | NSNumber *height = nil; 35 | 36 | ENSURE_ARG_AT_INDEX(height,args,1,NSNumber); 37 | 38 | TiUIListView *convert = (TiUIListView*) self.view; 39 | [convert addParallaxWithView:headerView withHeight:[height floatValue]]; 40 | } 41 | 42 | -(void)addParallaxWithImage:(id)args 43 | { 44 | ENSURE_UI_THREAD_1_ARG(args); 45 | 46 | NSString * url = nil; 47 | ENSURE_ARG_AT_INDEX(url,args,0,NSString); 48 | NSNumber * height = nil; 49 | ENSURE_ARG_AT_INDEX(height,args,1,NSNumber); 50 | 51 | TiUIListView *convert = (TiUIListView*) self.view; 52 | 53 | [convert addParallaxWithImage:url forHeight:[height floatValue]]; 54 | } 55 | 56 | -(void)setSectionHeaderInset:(id)args 57 | { 58 | ENSURE_UI_THREAD_1_ARG(args); 59 | ENSURE_SINGLE_ARG(args, NSNumber); 60 | 61 | CGFloat height = [(NSNumber*)args floatValue]; 62 | 63 | TiUIListView *convert = (TiUIListView*) self.view; 64 | [convert setSectionHeaderInset:height]; 65 | } 66 | 67 | -(void)setFadeoutOverHeight:(id)args 68 | { 69 | ENSURE_UI_THREAD_1_ARG(args); 70 | 71 | ENSURE_SINGLE_ARG(args, NSNumber); 72 | 73 | TiUIListView *convert = (TiUIListView*) self.view; 74 | 75 | [convert setFadeoutOverHeight:(NSNumber*)args]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Classes/UIScrollView+APParallaxHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+APParallaxHeader.h 3 | // 4 | // Created by Mathias Amnell on 2013-04-12. 5 | // Copyright (c) 2013 Apping AB. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class APParallaxView; 11 | @class APParallaxShadowView; 12 | 13 | @interface UIScrollView (APParallaxHeader) 14 | 15 | - (void)addParallaxWithImage:(NSString *)url andHeight:(CGFloat)height; 16 | - (void)addParallaxWithView:(TiViewProxy*)view andHeight:(CGFloat)height; 17 | - (void)setFadeoutOverHeight:(NSNumber*)height; 18 | 19 | @property (nonatomic, strong, readonly) APParallaxView *parallaxView; 20 | @property (nonatomic, assign) BOOL showsParallax; 21 | 22 | @end 23 | 24 | enum { 25 | APParallaxTrackingActive = 0, 26 | APParallaxTrackingInactive 27 | }; 28 | 29 | typedef NSUInteger APParallaxTrackingState; 30 | 31 | @interface APParallaxView : UIView 32 | 33 | @property (nonatomic, readonly) APParallaxTrackingState state; 34 | @property (nonatomic, strong) UIImageView *imageView; 35 | @property (nonatomic, strong) UIView *currentSubView; 36 | @property (nonatomic, strong) APParallaxShadowView *shadowView; 37 | @property (nonatomic, strong) NSNumber *fadeoutOverHeight; 38 | 39 | @end 40 | 41 | @interface APParallaxShadowView : UIView 42 | 43 | @end -------------------------------------------------------------------------------- /Classes/UIScrollView+APParallaxHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+APParallaxHeader.m 3 | // 4 | // Created by Mathias Amnell on 2013-04-12. 5 | // Copyright (c) 2013 Apping AB. All rights reserved. 6 | // 7 | 8 | #import "UIScrollView+APParallaxHeader.h" 9 | #import "AsyncImageView.h" 10 | 11 | #import 12 | 13 | @interface APParallaxView () 14 | 15 | @property (nonatomic, readwrite) APParallaxTrackingState state; 16 | 17 | @property (nonatomic) UIScrollView *scrollView; 18 | @property (nonatomic, readwrite) CGFloat originalTopInset; 19 | @property (nonatomic) CGFloat parallaxHeight; 20 | 21 | @property(nonatomic, assign) BOOL isObserving; 22 | 23 | @end 24 | 25 | 26 | 27 | #pragma mark - UIScrollView (APParallaxHeader) 28 | #import 29 | 30 | static char UIScrollViewParallaxView; 31 | 32 | @implementation UIScrollView (APParallaxHeader) 33 | 34 | - (void)addParallaxWithImage:(NSString *)url andHeight:(CGFloat)height { 35 | if(self.parallaxView) { 36 | if(self.parallaxView.currentSubView) [self.parallaxView.currentSubView removeFromSuperview]; 37 | [self.parallaxView.imageView setImageURL:[NSURL URLWithString:url]]; 38 | } 39 | else 40 | { 41 | APParallaxView *view = [[APParallaxView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, height)]; 42 | [view setClipsToBounds:YES]; 43 | [view.imageView setImageURL:[NSURL URLWithString:url]]; 44 | 45 | view.scrollView = self; 46 | view.parallaxHeight = height; 47 | [self addSubview:view]; 48 | 49 | view.originalTopInset = self.contentInset.top; 50 | 51 | UIEdgeInsets newInset = self.contentInset; 52 | newInset.top = height; 53 | self.contentInset = newInset; 54 | 55 | self.parallaxView = view; 56 | self.showsParallax = YES; 57 | } 58 | } 59 | 60 | - (void)setFadeoutOverHeight:(NSNumber*)height { 61 | self.parallaxView.fadeoutOverHeight = height; 62 | } 63 | 64 | - (void)addParallaxWithView:(TiViewProxy*)proxyView andHeight:(CGFloat)height { 65 | if(self.parallaxView) { 66 | [self.parallaxView.currentSubView removeFromSuperview]; 67 | //[proxyView.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 68 | [self.parallaxView addSubview:proxyView.view]; 69 | } 70 | else 71 | { 72 | APParallaxView *parallaxView = [[APParallaxView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(proxyView.view.frame), height)]; 73 | [parallaxView setClipsToBounds:YES]; 74 | //[proxyView.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 75 | [parallaxView addSubview:proxyView.view]; 76 | 77 | parallaxView.scrollView = self; 78 | parallaxView.parallaxHeight = height; 79 | [self addSubview:parallaxView]; 80 | 81 | parallaxView.originalTopInset = self.contentInset.top; 82 | 83 | UIEdgeInsets newInset = self.contentInset; 84 | newInset.top = height; 85 | self.contentInset = newInset; 86 | 87 | self.parallaxView = parallaxView; 88 | self.showsParallax = YES; 89 | } 90 | } 91 | 92 | - (void)setParallaxView:(APParallaxView *)parallaxView { 93 | objc_setAssociatedObject(self, &UIScrollViewParallaxView, 94 | parallaxView, 95 | OBJC_ASSOCIATION_ASSIGN); 96 | } 97 | 98 | - (APParallaxView *)parallaxView { 99 | return objc_getAssociatedObject(self, &UIScrollViewParallaxView); 100 | } 101 | 102 | - (void)setShowsParallax:(BOOL)showsParallax { 103 | self.parallaxView.hidden = !showsParallax; 104 | 105 | if(!showsParallax) { 106 | if (self.parallaxView.isObserving) { 107 | [self removeObserver:self.parallaxView forKeyPath:@"contentOffset"]; 108 | [self removeObserver:self.parallaxView forKeyPath:@"frame"]; 109 | self.parallaxView.isObserving = NO; 110 | } 111 | } 112 | else { 113 | if (!self.parallaxView.isObserving) { 114 | [self addObserver:self.parallaxView forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; 115 | [self addObserver:self.parallaxView forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 116 | self.parallaxView.isObserving = YES; 117 | } 118 | } 119 | } 120 | 121 | - (BOOL)showsParallax { 122 | return !self.parallaxView.hidden; 123 | } 124 | 125 | @end 126 | 127 | #pragma mark - ShadowLayer 128 | 129 | @implementation APParallaxShadowView 130 | - (id)initWithFrame:(CGRect)frame 131 | { 132 | self = [super initWithFrame:frame]; 133 | if (self) { 134 | [self setOpaque:NO]; 135 | } 136 | return self; 137 | } 138 | 139 | - (void)drawRect:(CGRect)rect 140 | { 141 | [super drawRect:rect]; 142 | 143 | //// General Declarations 144 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 145 | CGContextRef context = UIGraphicsGetCurrentContext(); 146 | 147 | 148 | //// Gradient Declarations 149 | NSArray* gradient3Colors = [NSArray arrayWithObjects: 150 | (id)[UIColor colorWithWhite:0 alpha:0.3].CGColor, 151 | (id)[UIColor clearColor].CGColor, nil]; 152 | CGFloat gradient3Locations[] = {0, 1}; 153 | CGGradientRef gradient3 = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradient3Colors, gradient3Locations); 154 | 155 | //// Rectangle Drawing 156 | UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0, 0, CGRectGetWidth(rect), 8)]; 157 | CGContextSaveGState(context); 158 | [rectanglePath addClip]; 159 | CGContextDrawLinearGradient(context, gradient3, CGPointMake(0, CGRectGetHeight(rect)), CGPointMake(0, 0), 0); 160 | CGContextRestoreGState(context); 161 | 162 | 163 | //// Cleanup 164 | CGGradientRelease(gradient3); 165 | CGColorSpaceRelease(colorSpace); 166 | 167 | } 168 | 169 | @end 170 | 171 | #pragma mark - APParallaxView 172 | 173 | @implementation APParallaxView 174 | 175 | - (id)initWithFrame:(CGRect)frame { 176 | if(self = [super initWithFrame:frame]) { 177 | 178 | // default styling values 179 | [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; 180 | [self setState:APParallaxTrackingActive]; 181 | [self setAutoresizesSubviews:YES]; 182 | 183 | self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame))]; 184 | [self.imageView setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; 185 | [self.imageView setContentMode:UIViewContentModeScaleAspectFill]; 186 | [self.imageView setClipsToBounds:YES]; 187 | [self addSubview:self.imageView]; 188 | 189 | self.shadowView = [[APParallaxShadowView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(frame)-8, CGRectGetWidth(frame), 8)]; 190 | [self.shadowView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin]; 191 | [self addSubview:self.shadowView]; 192 | 193 | self.fadeoutOverHeight = nil; 194 | self.currentSubView = nil; 195 | } 196 | 197 | return self; 198 | } 199 | 200 | - (void)willMoveToSuperview:(UIView *)newSuperview { 201 | if (self.superview && newSuperview == nil) { 202 | UIScrollView *scrollView = (UIScrollView *)self.superview; 203 | if (scrollView.showsParallax) { 204 | if (self.isObserving) { 205 | //If enter this branch, it is the moment just before "APParallaxView's dealloc", so remove observer here 206 | [scrollView removeObserver:self forKeyPath:@"contentOffset"]; 207 | [scrollView removeObserver:self forKeyPath:@"frame"]; 208 | self.isObserving = NO; 209 | } 210 | } 211 | } 212 | } 213 | 214 | - (void)addSubview:(UIView *)view 215 | { 216 | [super addSubview:view]; 217 | self.currentSubView = view; 218 | } 219 | 220 | #pragma mark - Observing 221 | 222 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 223 | if([keyPath isEqualToString:@"contentOffset"]) 224 | [self scrollViewDidScroll:[[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]]; 225 | else if([keyPath isEqualToString:@"frame"]) 226 | [self layoutSubviews]; 227 | } 228 | 229 | - (void)scrollViewDidScroll:(CGPoint)contentOffset { 230 | // We do not want to track when the parallax view is hidden 231 | if (contentOffset.y > 0) { 232 | [self setState:APParallaxTrackingInactive]; 233 | } else { 234 | [self setState:APParallaxTrackingActive]; 235 | } 236 | 237 | if(self.state == APParallaxTrackingActive) { 238 | CGFloat yOffset = contentOffset.y*-1; 239 | 240 | [self setFrame:CGRectMake(0, contentOffset.y, CGRectGetWidth(self.frame), yOffset)]; 241 | 242 | //size the TiView 243 | if (self.currentSubView != nil) 244 | { 245 | [self.currentSubView setFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), yOffset)]; 246 | } 247 | 248 | //Fade out TiView 249 | if (self.fadeoutOverHeight != nil) 250 | { 251 | CGFloat diff = 1; 252 | CGFloat fadeout = [self.fadeoutOverHeight floatValue]; 253 | if (yOffset < self.parallaxHeight) 254 | { 255 | diff = 1; 256 | } 257 | else if ( yOffset >= (self.parallaxHeight + fadeout) ) 258 | { 259 | diff = 0; 260 | } 261 | else if (yOffset < (self.parallaxHeight + fadeout) ) 262 | { 263 | diff = 1-((yOffset - self.parallaxHeight)/fadeout); 264 | } 265 | [self.currentSubView setAlpha:diff]; 266 | } 267 | } 268 | } 269 | 270 | @end -------------------------------------------------------------------------------- /Classes/UITableView+HeaderSectionPosition.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+HeaderSectionPosition.h 3 | // Category to extend UITableView, which TiUIListView uses 4 | // 5 | // Created by James Chow on 11/04/2014. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface UITableView (HeaderSectionPosition) 12 | 13 | @property (nonatomic, assign) UIEdgeInsets headerViewInsets; 14 | 15 | + (void) swizzle; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Classes/UITableView+HeaderSectionPosition.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+HeaderSectionPosition.m 3 | // Category to extend UITableView, which TiUIListView uses 4 | // 5 | // Created by James Chow on 11/04/2014. 6 | // http://b2cloud.com.au/tutorial/uitableview-section-header-positions/ 7 | // 8 | 9 | #import "UITableView+HeaderSectionPosition.h" 10 | #import "NSObject+JRSwizzle.h" 11 | #import 12 | 13 | @implementation UITableView (HeaderSectionPosition) 14 | 15 | BOOL shouldManuallyLayoutHeaderViews; 16 | 17 | + (void) swizzle 18 | { 19 | NSError *error = nil; 20 | [UITableView jr_swizzleMethod:@selector(layoutSubviews) withMethod:@selector(layoutSubviews_swizzle) error:&error]; 21 | 22 | if (error != nil) { 23 | NSLog(@"[ERROR] %@", [error localizedDescription]); 24 | } 25 | } 26 | 27 | #pragma mark - 28 | #pragma mark Self 29 | 30 | - (void) layoutSubviews_swizzle 31 | { 32 | [UITableView jr_swizzleMethod:@selector(layoutSubviews) withMethod:@selector(layoutSubviews_swizzle) error:nil]; 33 | 34 | [self layoutSubviews]; 35 | 36 | [UITableView jr_swizzleMethod:@selector(layoutSubviews) withMethod:@selector(layoutSubviews_swizzle) error:nil]; 37 | 38 | if(shouldManuallyLayoutHeaderViews) 39 | [self layoutHeaderViews]; 40 | } 41 | 42 | - (void) setHeaderViewInsets:(UIEdgeInsets )_headerViewInsets 43 | { 44 | NSNumber * topValue = [NSNumber numberWithFloat: _headerViewInsets.top]; 45 | 46 | objc_setAssociatedObject(self, @selector(headerViewInsets), topValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 47 | 48 | shouldManuallyLayoutHeaderViews = !UIEdgeInsetsEqualToEdgeInsets(_headerViewInsets, UIEdgeInsetsZero); 49 | 50 | [self setNeedsLayout]; 51 | } 52 | 53 | -(UIEdgeInsets ) headerViewInsets 54 | { 55 | NSNumber *topValue = objc_getAssociatedObject(self, @selector(headerViewInsets)); 56 | UIEdgeInsets convert = UIEdgeInsetsMake([topValue floatValue], 0, 0, 0); 57 | return convert; 58 | } 59 | 60 | #pragma mark Private 61 | 62 | - (void) layoutHeaderViews 63 | { 64 | const NSUInteger numberOfSections = self.numberOfSections; 65 | const UIEdgeInsets contentInset = self.contentInset; 66 | const CGPoint contentOffset = self.contentOffset; 67 | 68 | const CGFloat sectionViewMinimumOriginY = contentOffset.y + contentInset.top + self.headerViewInsets.top; 69 | 70 | // Layout each header view 71 | for(NSUInteger section = 0; section < numberOfSections; section++) 72 | { 73 | 74 | //headerViewForSection:section won't work due to titanium using plain uiviews 75 | 76 | UIView* sectionHeView = [self viewWithTag:100 + section]; 77 | 78 | if(sectionHeView == nil) 79 | continue; 80 | 81 | const CGRect sectionFrame = [self rectForSection:section]; 82 | 83 | CGRect sectionHeViewFrame = sectionHeView.frame; 84 | 85 | sectionHeViewFrame.origin.y = ((sectionFrame.origin.y < sectionViewMinimumOriginY) ? sectionViewMinimumOriginY : sectionFrame.origin.y); 86 | 87 | // If it's not last section, manually 'stick' it to the below section if needed 88 | if(section < numberOfSections - 1) 89 | { 90 | const CGRect nextSectionFrame = [self rectForSection:section + 1]; 91 | 92 | if(CGRectGetMaxY(sectionHeViewFrame) > CGRectGetMinY(nextSectionFrame)) 93 | sectionHeViewFrame.origin.y = nextSectionFrame.origin.y - sectionHeViewFrame.size.height; 94 | } 95 | 96 | [sectionHeView setFrame:sectionHeViewFrame]; 97 | } 98 | } 99 | @end 100 | -------------------------------------------------------------------------------- /ComCitytelecomTiparallaxheader_Prefix.pch: -------------------------------------------------------------------------------- 1 | 2 | #ifdef __OBJC__ 3 | #import 4 | #import "TiUIListView+ParallaxHeader.h" 5 | #endif 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: place your license here and we'll include it in the module distribution 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Place your license text here. This file will be incorporated with your app at package time. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parallax Header for ListView 2 | 3 | ## Description 4 | 5 | The Parallax Header module extends the Appcelerator Titanium ListView 6 | Wrapper module for 7 | 8 | https://github.com/apping/APParallaxHeader 9 | 10 | The module is licensed under the MIT license. 11 | 12 | ### Demo Video 13 | 14 | [![Gif of effect](http://updatedisplaylist.com/images/TiParallaxHeader-0_2.gif)](http://youtu.be/H_VSO-k_OLw) 15 | 16 | ## Quick Start 17 | 18 | ### Get it [![gitTio](http://gitt.io/badge.png)](http://gitt.io/component/com.citytelecom.tiparallaxheader) 19 | Download the latest distribution ZIP-file and consult the [Titanium Documentation](http://docs.appcelerator.com/titanium/latest/#!/guide/Using_a_Module) on how install it, or simply use the [gitTio CLI](http://gitt.io/cli): 20 | 21 | `$ gittio install com.citytelecom.tiparallaxheader` 22 | 23 | ## Author 24 | 25 | **James Chow** 26 | web: http://updatedisplaylist.com 27 | email: james@jc888.co.uk 28 | 29 | ## Credits 30 | [Mathias Amnell](http://twitter.com/amnell) at [Apping AB](http://apping.se) for APParallaxHeader 31 | 32 | http://b2cloud.com.au/tutorial/uitableview-section-header-positions/ 33 | 34 | [Mads Møller](http://www.napp.dk) for showing how to override Titanium components 35 | 36 | ## License 37 | 38 | Copyright (c) 2010-2014 James Chow 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy 41 | of this software and associated documentation files (the "Software"), to deal 42 | in the Software without restriction, including without limitation the rights 43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the Software is 45 | furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in 48 | all copies or substantial portions of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 56 | THE SOFTWARE. 57 | -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 24416B8111C4CA220047AFDD /* Build & Test */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 24416B8A11C4CA520047AFDD /* Build configuration list for PBXAggregateTarget "Build & Test" */; 13 | buildPhases = ( 14 | 24416B8011C4CA220047AFDD /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | 24416B8511C4CA280047AFDD /* PBXTargetDependency */, 18 | ); 19 | name = "Build & Test"; 20 | productName = "Build & test"; 21 | }; 22 | /* End PBXAggregateTarget section */ 23 | 24 | /* Begin PBXBuildFile section */ 25 | 24DD6CF91134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DD6CF71134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.h */; }; 26 | 24DD6CFA1134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DD6CF81134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.m */; }; 27 | 24DE9E1111C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DE9E0F11C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.h */; }; 28 | 24DE9E1211C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DE9E1011C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.m */; }; 29 | AA747D9F0F9514B9006C5449 /* ComCitytelecomTiparallaxheader_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* ComCitytelecomTiparallaxheader_Prefix.pch */; }; 30 | AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; 31 | B935DD5E19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD5C19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.h */; }; 32 | B935DD5F19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD5D19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.m */; }; 33 | B935DD6219003F420058B357 /* TiUIListView+ParallaxHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD6019003F420058B357 /* TiUIListView+ParallaxHeader.h */; }; 34 | B935DD6319003F420058B357 /* TiUIListView+ParallaxHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD6119003F420058B357 /* TiUIListView+ParallaxHeader.m */; }; 35 | B935DD67190040D10058B357 /* NSObject+JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD65190040D10058B357 /* NSObject+JRSwizzle.h */; }; 36 | B935DD68190040D10058B357 /* NSObject+JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD66190040D10058B357 /* NSObject+JRSwizzle.m */; }; 37 | B935DD6B190041390058B357 /* UITableView+HeaderSectionPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD69190041390058B357 /* UITableView+HeaderSectionPosition.h */; }; 38 | B935DD6C190041390058B357 /* UITableView+HeaderSectionPosition.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD6A190041390058B357 /* UITableView+HeaderSectionPosition.m */; }; 39 | B935DD6F190041BB0058B357 /* UIScrollView+APParallaxHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD6D190041BB0058B357 /* UIScrollView+APParallaxHeader.h */; }; 40 | B935DD70190041BB0058B357 /* UIScrollView+APParallaxHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD6E190041BB0058B357 /* UIScrollView+APParallaxHeader.m */; }; 41 | B935DD7B19004D840058B357 /* AsyncImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = B935DD7919004D840058B357 /* AsyncImageView.h */; }; 42 | B935DD7C19004D840058B357 /* AsyncImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = B935DD7A19004D840058B357 /* AsyncImageView.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXContainerItemProxy section */ 46 | 24416B8411C4CA280047AFDD /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = D2AAC07D0554694100DB518D; 51 | remoteInfo = TiParallaxHeader; 52 | }; 53 | /* End PBXContainerItemProxy section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 24DD6CF71134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ComCitytelecomTiparallaxheaderModule.h; path = Classes/ComCitytelecomTiparallaxheaderModule.h; sourceTree = ""; }; 57 | 24DD6CF81134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ComCitytelecomTiparallaxheaderModule.m; path = Classes/ComCitytelecomTiparallaxheaderModule.m; sourceTree = ""; }; 58 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = titanium.xcconfig; sourceTree = ""; }; 59 | 24DE9E0F11C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ComCitytelecomTiparallaxheaderModuleAssets.h; path = Classes/ComCitytelecomTiparallaxheaderModuleAssets.h; sourceTree = ""; }; 60 | 24DE9E1011C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ComCitytelecomTiparallaxheaderModuleAssets.m; path = Classes/ComCitytelecomTiparallaxheaderModuleAssets.m; sourceTree = ""; }; 61 | AA747D9E0F9514B9006C5449 /* ComCitytelecomTiparallaxheader_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComCitytelecomTiparallaxheader_Prefix.pch; sourceTree = SOURCE_ROOT; }; 62 | AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 63 | B935DD5C19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TiUIListViewProxy+ProxyParallaxHeader.h"; path = "Classes/TiUIListViewProxy+ProxyParallaxHeader.h"; sourceTree = ""; }; 64 | B935DD5D19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TiUIListViewProxy+ProxyParallaxHeader.m"; path = "Classes/TiUIListViewProxy+ProxyParallaxHeader.m"; sourceTree = ""; }; 65 | B935DD6019003F420058B357 /* TiUIListView+ParallaxHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TiUIListView+ParallaxHeader.h"; path = "Classes/TiUIListView+ParallaxHeader.h"; sourceTree = ""; }; 66 | B935DD6119003F420058B357 /* TiUIListView+ParallaxHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TiUIListView+ParallaxHeader.m"; path = "Classes/TiUIListView+ParallaxHeader.m"; sourceTree = ""; }; 67 | B935DD65190040D10058B357 /* NSObject+JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+JRSwizzle.h"; path = "Classes/NSObject+JRSwizzle.h"; sourceTree = ""; }; 68 | B935DD66190040D10058B357 /* NSObject+JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+JRSwizzle.m"; path = "Classes/NSObject+JRSwizzle.m"; sourceTree = ""; }; 69 | B935DD69190041390058B357 /* UITableView+HeaderSectionPosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UITableView+HeaderSectionPosition.h"; path = "Classes/UITableView+HeaderSectionPosition.h"; sourceTree = ""; }; 70 | B935DD6A190041390058B357 /* UITableView+HeaderSectionPosition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UITableView+HeaderSectionPosition.m"; path = "Classes/UITableView+HeaderSectionPosition.m"; sourceTree = ""; }; 71 | B935DD6D190041BB0058B357 /* UIScrollView+APParallaxHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+APParallaxHeader.h"; path = "Classes/UIScrollView+APParallaxHeader.h"; sourceTree = ""; }; 72 | B935DD6E190041BB0058B357 /* UIScrollView+APParallaxHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+APParallaxHeader.m"; path = "Classes/UIScrollView+APParallaxHeader.m"; sourceTree = ""; }; 73 | B935DD7919004D840058B357 /* AsyncImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncImageView.h; path = Classes/AsyncImageView.h; sourceTree = ""; }; 74 | B935DD7A19004D840058B357 /* AsyncImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AsyncImageView.m; path = Classes/AsyncImageView.m; sourceTree = ""; }; 75 | D2AAC07E0554694100DB518D /* libComCitytelecomTiparallaxheader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libComCitytelecomTiparallaxheader.a; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | D2AAC07C0554694100DB518D /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 034768DFFF38A50411DB9C8B /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | D2AAC07E0554694100DB518D /* libComCitytelecomTiparallaxheader.a */, 94 | ); 95 | name = Products; 96 | sourceTree = ""; 97 | }; 98 | 0867D691FE84028FC02AAC07 /* TiParallaxHeader */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 08FB77AEFE84172EC02AAC07 /* Classes */, 102 | 32C88DFF0371C24200C91783 /* Other Sources */, 103 | 0867D69AFE84028FC02AAC07 /* Frameworks */, 104 | 034768DFFF38A50411DB9C8B /* Products */, 105 | ); 106 | name = TiParallaxHeader; 107 | sourceTree = ""; 108 | }; 109 | 0867D69AFE84028FC02AAC07 /* Frameworks */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | AACBBE490F95108600F1A2B1 /* Foundation.framework */, 113 | ); 114 | name = Frameworks; 115 | sourceTree = ""; 116 | }; 117 | 08FB77AEFE84172EC02AAC07 /* Classes */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | B935DD7919004D840058B357 /* AsyncImageView.h */, 121 | B935DD7A19004D840058B357 /* AsyncImageView.m */, 122 | 24DE9E0F11C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.h */, 123 | 24DE9E1011C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.m */, 124 | 24DD6CF71134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.h */, 125 | 24DD6CF81134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.m */, 126 | B935DD5C19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.h */, 127 | B935DD5D19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.m */, 128 | B935DD6019003F420058B357 /* TiUIListView+ParallaxHeader.h */, 129 | B935DD6119003F420058B357 /* TiUIListView+ParallaxHeader.m */, 130 | B935DD65190040D10058B357 /* NSObject+JRSwizzle.h */, 131 | B935DD66190040D10058B357 /* NSObject+JRSwizzle.m */, 132 | B935DD69190041390058B357 /* UITableView+HeaderSectionPosition.h */, 133 | B935DD6A190041390058B357 /* UITableView+HeaderSectionPosition.m */, 134 | B935DD6D190041BB0058B357 /* UIScrollView+APParallaxHeader.h */, 135 | B935DD6E190041BB0058B357 /* UIScrollView+APParallaxHeader.m */, 136 | ); 137 | name = Classes; 138 | sourceTree = ""; 139 | }; 140 | 32C88DFF0371C24200C91783 /* Other Sources */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | AA747D9E0F9514B9006C5449 /* ComCitytelecomTiparallaxheader_Prefix.pch */, 144 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */, 145 | ); 146 | name = "Other Sources"; 147 | sourceTree = ""; 148 | }; 149 | /* End PBXGroup section */ 150 | 151 | /* Begin PBXHeadersBuildPhase section */ 152 | D2AAC07A0554694100DB518D /* Headers */ = { 153 | isa = PBXHeadersBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | B935DD6B190041390058B357 /* UITableView+HeaderSectionPosition.h in Headers */, 157 | B935DD5E19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.h in Headers */, 158 | B935DD67190040D10058B357 /* NSObject+JRSwizzle.h in Headers */, 159 | AA747D9F0F9514B9006C5449 /* ComCitytelecomTiparallaxheader_Prefix.pch in Headers */, 160 | 24DD6CF91134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.h in Headers */, 161 | B935DD6219003F420058B357 /* TiUIListView+ParallaxHeader.h in Headers */, 162 | B935DD7B19004D840058B357 /* AsyncImageView.h in Headers */, 163 | 24DE9E1111C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.h in Headers */, 164 | B935DD6F190041BB0058B357 /* UIScrollView+APParallaxHeader.h in Headers */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXHeadersBuildPhase section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | D2AAC07D0554694100DB518D /* TiParallaxHeader */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "TiParallaxHeader" */; 174 | buildPhases = ( 175 | D2AAC07A0554694100DB518D /* Headers */, 176 | D2AAC07B0554694100DB518D /* Sources */, 177 | D2AAC07C0554694100DB518D /* Frameworks */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = TiParallaxHeader; 184 | productName = TiParallaxHeader; 185 | productReference = D2AAC07E0554694100DB518D /* libComCitytelecomTiparallaxheader.a */; 186 | productType = "com.apple.product-type.library.static"; 187 | }; 188 | /* End PBXNativeTarget section */ 189 | 190 | /* Begin PBXProject section */ 191 | 0867D690FE84028FC02AAC07 /* Project object */ = { 192 | isa = PBXProject; 193 | attributes = { 194 | }; 195 | buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "TiParallaxHeader" */; 196 | compatibilityVersion = "Xcode 3.1"; 197 | developmentRegion = English; 198 | hasScannedForEncodings = 1; 199 | knownRegions = ( 200 | English, 201 | Japanese, 202 | French, 203 | German, 204 | ); 205 | mainGroup = 0867D691FE84028FC02AAC07 /* TiParallaxHeader */; 206 | productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; 207 | projectDirPath = ""; 208 | projectRoot = ""; 209 | targets = ( 210 | D2AAC07D0554694100DB518D /* TiParallaxHeader */, 211 | 24416B8111C4CA220047AFDD /* Build & Test */, 212 | ); 213 | }; 214 | /* End PBXProject section */ 215 | 216 | /* Begin PBXShellScriptBuildPhase section */ 217 | 24416B8011C4CA220047AFDD /* ShellScript */ = { 218 | isa = PBXShellScriptBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | ); 222 | inputPaths = ( 223 | ); 224 | outputPaths = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "# shell script goes here\n\npython \"${TITANIUM_SDK}/titanium.py\" run --dir=\"${PROJECT_DIR}\"\nexit $?\n"; 229 | }; 230 | /* End PBXShellScriptBuildPhase section */ 231 | 232 | /* Begin PBXSourcesBuildPhase section */ 233 | D2AAC07B0554694100DB518D /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 24DD6CFA1134B3F500162E58 /* ComCitytelecomTiparallaxheaderModule.m in Sources */, 238 | B935DD6C190041390058B357 /* UITableView+HeaderSectionPosition.m in Sources */, 239 | B935DD68190040D10058B357 /* NSObject+JRSwizzle.m in Sources */, 240 | 24DE9E1211C5FE74003F90F6 /* ComCitytelecomTiparallaxheaderModuleAssets.m in Sources */, 241 | B935DD70190041BB0058B357 /* UIScrollView+APParallaxHeader.m in Sources */, 242 | B935DD5F19003DEC0058B357 /* TiUIListViewProxy+ProxyParallaxHeader.m in Sources */, 243 | B935DD6319003F420058B357 /* TiUIListView+ParallaxHeader.m in Sources */, 244 | B935DD7C19004D840058B357 /* AsyncImageView.m in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXTargetDependency section */ 251 | 24416B8511C4CA280047AFDD /* PBXTargetDependency */ = { 252 | isa = PBXTargetDependency; 253 | target = D2AAC07D0554694100DB518D /* TiParallaxHeader */; 254 | targetProxy = 24416B8411C4CA280047AFDD /* PBXContainerItemProxy */; 255 | }; 256 | /* End PBXTargetDependency section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | 1DEB921F08733DC00010E9CD /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 262 | buildSettings = { 263 | CODE_SIGN_IDENTITY = "iPhone Developer"; 264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 265 | DSTROOT = /tmp/ComCitytelecomTiparallaxheader.dst; 266 | GCC_C_LANGUAGE_STANDARD = c99; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 269 | GCC_PREFIX_HEADER = ComCitytelecomTiparallaxheader_Prefix.pch; 270 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 271 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 272 | GCC_VERSION = ""; 273 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 274 | GCC_WARN_MISSING_PARENTHESES = NO; 275 | GCC_WARN_SHADOW = NO; 276 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_PARAMETER = NO; 279 | GCC_WARN_UNUSED_VALUE = NO; 280 | GCC_WARN_UNUSED_VARIABLE = NO; 281 | INSTALL_PATH = /usr/local/lib; 282 | LIBRARY_SEARCH_PATHS = ""; 283 | OTHER_CFLAGS = ( 284 | "-DDEBUG", 285 | "-DTI_POST_1_2", 286 | ); 287 | OTHER_LDFLAGS = "-ObjC"; 288 | PRODUCT_NAME = ComCitytelecomTiparallaxheader; 289 | PROVISIONING_PROFILE = ""; 290 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 291 | RUN_CLANG_STATIC_ANALYZER = NO; 292 | SDKROOT = iphoneos; 293 | USER_HEADER_SEARCH_PATHS = ""; 294 | }; 295 | name = Debug; 296 | }; 297 | 1DEB922008733DC00010E9CD /* Release */ = { 298 | isa = XCBuildConfiguration; 299 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | DSTROOT = /tmp/ComCitytelecomTiparallaxheader.dst; 303 | GCC_C_LANGUAGE_STANDARD = c99; 304 | GCC_MODEL_TUNING = G5; 305 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 306 | GCC_PREFIX_HEADER = ComCitytelecomTiparallaxheader_Prefix.pch; 307 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 308 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 309 | GCC_VERSION = ""; 310 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 311 | GCC_WARN_MISSING_PARENTHESES = NO; 312 | GCC_WARN_SHADOW = NO; 313 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_PARAMETER = NO; 316 | GCC_WARN_UNUSED_VALUE = NO; 317 | GCC_WARN_UNUSED_VARIABLE = NO; 318 | INSTALL_PATH = /usr/local/lib; 319 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 320 | LIBRARY_SEARCH_PATHS = ""; 321 | OTHER_CFLAGS = "-DTI_POST_1_2"; 322 | OTHER_LDFLAGS = "-ObjC"; 323 | PRODUCT_NAME = ComCitytelecomTiparallaxheader; 324 | RUN_CLANG_STATIC_ANALYZER = NO; 325 | SDKROOT = iphoneos; 326 | USER_HEADER_SEARCH_PATHS = ""; 327 | }; 328 | name = Release; 329 | }; 330 | 1DEB922308733DC00010E9CD /* Debug */ = { 331 | isa = XCBuildConfiguration; 332 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 333 | buildSettings = { 334 | CODE_SIGN_IDENTITY = "iPhone Developer"; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | DSTROOT = /tmp/ComCitytelecomTiparallaxheader.dst; 337 | GCC_C_LANGUAGE_STANDARD = c99; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = ComCitytelecomTiparallaxheader_Prefix.pch; 341 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 342 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 343 | GCC_VERSION = ""; 344 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 345 | GCC_WARN_MISSING_PARENTHESES = NO; 346 | GCC_WARN_SHADOW = NO; 347 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_PARAMETER = NO; 350 | GCC_WARN_UNUSED_VALUE = NO; 351 | GCC_WARN_UNUSED_VARIABLE = NO; 352 | INSTALL_PATH = /usr/local/lib; 353 | OTHER_CFLAGS = ( 354 | "-DDEBUG", 355 | "-DTI_POST_1_2", 356 | ); 357 | OTHER_LDFLAGS = "-ObjC"; 358 | PRODUCT_NAME = ComCitytelecomTiparallaxheader; 359 | PROVISIONING_PROFILE = ""; 360 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 361 | RUN_CLANG_STATIC_ANALYZER = NO; 362 | SDKROOT = iphoneos; 363 | USER_HEADER_SEARCH_PATHS = ""; 364 | }; 365 | name = Debug; 366 | }; 367 | 1DEB922408733DC00010E9CD /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 370 | buildSettings = { 371 | ALWAYS_SEARCH_USER_PATHS = NO; 372 | DSTROOT = /tmp/ComCitytelecomTiparallaxheader.dst; 373 | GCC_C_LANGUAGE_STANDARD = c99; 374 | GCC_MODEL_TUNING = G5; 375 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 376 | GCC_PREFIX_HEADER = ComCitytelecomTiparallaxheader_Prefix.pch; 377 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 378 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 379 | GCC_VERSION = ""; 380 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 381 | GCC_WARN_MISSING_PARENTHESES = NO; 382 | GCC_WARN_SHADOW = NO; 383 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_PARAMETER = NO; 386 | GCC_WARN_UNUSED_VALUE = NO; 387 | GCC_WARN_UNUSED_VARIABLE = NO; 388 | INSTALL_PATH = /usr/local/lib; 389 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 390 | OTHER_CFLAGS = "-DTI_POST_1_2"; 391 | OTHER_LDFLAGS = "-ObjC"; 392 | PRODUCT_NAME = ComCitytelecomTiparallaxheader; 393 | RUN_CLANG_STATIC_ANALYZER = NO; 394 | SDKROOT = iphoneos; 395 | USER_HEADER_SEARCH_PATHS = ""; 396 | }; 397 | name = Release; 398 | }; 399 | 24416B8211C4CA220047AFDD /* Debug */ = { 400 | isa = XCBuildConfiguration; 401 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 402 | buildSettings = { 403 | COPY_PHASE_STRIP = NO; 404 | GCC_DYNAMIC_NO_PIC = NO; 405 | GCC_OPTIMIZATION_LEVEL = 0; 406 | PRODUCT_NAME = "Build & test"; 407 | }; 408 | name = Debug; 409 | }; 410 | 24416B8311C4CA220047AFDD /* Release */ = { 411 | isa = XCBuildConfiguration; 412 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 413 | buildSettings = { 414 | COPY_PHASE_STRIP = YES; 415 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 416 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 417 | PRODUCT_NAME = "Build & test"; 418 | ZERO_LINK = NO; 419 | }; 420 | name = Release; 421 | }; 422 | /* End XCBuildConfiguration section */ 423 | 424 | /* Begin XCConfigurationList section */ 425 | 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "TiParallaxHeader" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | 1DEB921F08733DC00010E9CD /* Debug */, 429 | 1DEB922008733DC00010E9CD /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | defaultConfigurationName = Release; 433 | }; 434 | 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "TiParallaxHeader" */ = { 435 | isa = XCConfigurationList; 436 | buildConfigurations = ( 437 | 1DEB922308733DC00010E9CD /* Debug */, 438 | 1DEB922408733DC00010E9CD /* Release */, 439 | ); 440 | defaultConfigurationIsVisible = 0; 441 | defaultConfigurationName = Release; 442 | }; 443 | 24416B8A11C4CA520047AFDD /* Build configuration list for PBXAggregateTarget "Build & Test" */ = { 444 | isa = XCConfigurationList; 445 | buildConfigurations = ( 446 | 24416B8211C4CA220047AFDD /* Debug */, 447 | 24416B8311C4CA220047AFDD /* Release */, 448 | ); 449 | defaultConfigurationIsVisible = 0; 450 | defaultConfigurationName = Release; 451 | }; 452 | /* End XCConfigurationList section */ 453 | }; 454 | rootObject = 0867D690FE84028FC02AAC07 /* Project object */; 455 | } 456 | -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/project.xcworkspace/xcuserdata/jameschow.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jc888/TiParallaxHeader/ec3491422b49e384bd83d13e4cdf0aceb1fb1b7d/TiParallaxHeader.xcodeproj/project.xcworkspace/xcuserdata/jameschow.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/xcuserdata/jameschow.xcuserdatad/xcschemes/Build & Test.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/xcuserdata/jameschow.xcuserdatad/xcschemes/TiParallaxHeader.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /TiParallaxHeader.xcodeproj/xcuserdata/jameschow.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Build & Test.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | TiParallaxHeader.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 24416B8111C4CA220047AFDD 21 | 22 | primary 23 | 24 | 25 | D2AAC07D0554694100DB518D 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/README: -------------------------------------------------------------------------------- 1 | Place your assets like PNG files in this directory and they will be packaged with your module. 2 | 3 | If you create a file named com.citytelecom.tiparallaxheader.js in this directory, it will be 4 | compiled and used as your module. This allows you to run pure Javascript 5 | modules that are pre-compiled. 6 | 7 | -------------------------------------------------------------------------------- /assets/com.citytelecom.tiparallaxheader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author James Chow 3 | */ 4 | Ti.UI.createListView(); -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Appcelerator Titanium Module Packager 4 | # 5 | # 6 | import os, subprocess, sys, glob, string, optparse, subprocess 7 | import zipfile 8 | from datetime import date 9 | 10 | cwd = os.path.abspath(os.path.dirname(sys._getframe(0).f_code.co_filename)) 11 | os.chdir(cwd) 12 | required_module_keys = ['name','version','moduleid','description','copyright','license','copyright','platform','minsdk'] 13 | module_defaults = { 14 | 'description':'My module', 15 | 'author': 'Your Name', 16 | 'license' : 'Specify your license', 17 | 'copyright' : 'Copyright (c) %s by Your Company' % str(date.today().year), 18 | } 19 | module_license_default = "TODO: place your license here and we'll include it in the module distribution" 20 | 21 | def find_sdk(config): 22 | sdk = config['TITANIUM_SDK'] 23 | return os.path.expandvars(os.path.expanduser(sdk)) 24 | 25 | def replace_vars(config,token): 26 | idx = token.find('$(') 27 | while idx != -1: 28 | idx2 = token.find(')',idx+2) 29 | if idx2 == -1: break 30 | key = token[idx+2:idx2] 31 | if not config.has_key(key): break 32 | token = token.replace('$(%s)' % key, config[key]) 33 | idx = token.find('$(') 34 | return token 35 | 36 | 37 | def read_ti_xcconfig(): 38 | contents = open(os.path.join(cwd,'titanium.xcconfig')).read() 39 | config = {} 40 | for line in contents.splitlines(False): 41 | line = line.strip() 42 | if line[0:2]=='//': continue 43 | idx = line.find('=') 44 | if idx > 0: 45 | key = line[0:idx].strip() 46 | value = line[idx+1:].strip() 47 | config[key] = replace_vars(config,value) 48 | return config 49 | 50 | def generate_doc(config): 51 | docdir = os.path.join(cwd,'documentation') 52 | if not os.path.exists(docdir): 53 | warn("Couldn't find documentation file at: %s" % docdir) 54 | return None 55 | 56 | try: 57 | import markdown2 as markdown 58 | except ImportError: 59 | import markdown 60 | documentation = [] 61 | for file in os.listdir(docdir): 62 | if file in ignoreFiles or os.path.isdir(os.path.join(docdir, file)): 63 | continue 64 | md = open(os.path.join(docdir,file)).read() 65 | html = markdown.markdown(md) 66 | documentation.append({file:html}); 67 | return documentation 68 | 69 | def compile_js(manifest,config): 70 | js_file = os.path.join(cwd,'assets','com.citytelecom.tiparallaxheader.js') 71 | if not os.path.exists(js_file): return 72 | 73 | from compiler import Compiler 74 | try: 75 | import json 76 | except: 77 | import simplejson as json 78 | 79 | compiler = Compiler(cwd, manifest['moduleid'], manifest['name'], 'commonjs') 80 | root_asset, module_assets = compiler.compile_module() 81 | 82 | root_asset_content = """ 83 | %s 84 | 85 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[0]); 86 | """ % root_asset 87 | 88 | module_asset_content = """ 89 | %s 90 | 91 | NSNumber *index = [map objectForKey:path]; 92 | if (index == nil) { 93 | return nil; 94 | } 95 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[index.integerValue]); 96 | """ % module_assets 97 | 98 | from tools import splice_code 99 | 100 | assets_router = os.path.join(cwd,'Classes','ComCitytelecomTiparallaxheaderModuleAssets.m') 101 | splice_code(assets_router, 'asset', root_asset_content) 102 | splice_code(assets_router, 'resolve_asset', module_asset_content) 103 | 104 | # Generate the exports after crawling all of the available JS source 105 | exports = open('metadata.json','w') 106 | json.dump({'exports':compiler.exports }, exports) 107 | exports.close() 108 | 109 | def die(msg): 110 | print msg 111 | sys.exit(1) 112 | 113 | def info(msg): 114 | print "[INFO] %s" % msg 115 | 116 | def warn(msg): 117 | print "[WARN] %s" % msg 118 | 119 | def validate_license(): 120 | c = open(os.path.join(cwd,'LICENSE')).read() 121 | if c.find(module_license_default)!=-1: 122 | warn('please update the LICENSE file with your license text before distributing') 123 | 124 | def validate_manifest(): 125 | path = os.path.join(cwd,'manifest') 126 | f = open(path) 127 | if not os.path.exists(path): die("missing %s" % path) 128 | manifest = {} 129 | for line in f.readlines(): 130 | line = line.strip() 131 | if line[0:1]=='#': continue 132 | if line.find(':') < 0: continue 133 | key,value = line.split(':') 134 | manifest[key.strip()]=value.strip() 135 | for key in required_module_keys: 136 | if not manifest.has_key(key): die("missing required manifest key '%s'" % key) 137 | if module_defaults.has_key(key): 138 | defvalue = module_defaults[key] 139 | curvalue = manifest[key] 140 | if curvalue==defvalue: warn("please update the manifest key: '%s' to a non-default value" % key) 141 | return manifest,path 142 | 143 | ignoreFiles = ['.DS_Store','.gitignore','libTitanium.a','titanium.jar','README'] 144 | ignoreDirs = ['.DS_Store','.svn','.git','CVSROOT'] 145 | 146 | def zip_dir(zf,dir,basepath,ignoreExt=[]): 147 | if not os.path.exists(dir): return 148 | for root, dirs, files in os.walk(dir): 149 | for name in ignoreDirs: 150 | if name in dirs: 151 | dirs.remove(name) # don't visit ignored directories 152 | for file in files: 153 | if file in ignoreFiles: continue 154 | e = os.path.splitext(file) 155 | if len(e) == 2 and e[1] in ignoreExt: continue 156 | from_ = os.path.join(root, file) 157 | to_ = from_.replace(dir, '%s/%s'%(basepath,dir), 1) 158 | zf.write(from_, to_) 159 | 160 | def glob_libfiles(): 161 | files = [] 162 | for libfile in glob.glob('build/**/*.a'): 163 | if libfile.find('Release-')!=-1: 164 | files.append(libfile) 165 | return files 166 | 167 | def build_module(manifest,config): 168 | from tools import ensure_dev_path 169 | ensure_dev_path() 170 | 171 | rc = os.system("xcodebuild -sdk iphoneos -configuration Release") 172 | if rc != 0: 173 | die("xcodebuild failed") 174 | rc = os.system("xcodebuild -sdk iphonesimulator -configuration Release") 175 | if rc != 0: 176 | die("xcodebuild failed") 177 | # build the merged library using lipo 178 | moduleid = manifest['moduleid'] 179 | libpaths = '' 180 | for libfile in glob_libfiles(): 181 | libpaths+='%s ' % libfile 182 | 183 | os.system("lipo %s -create -output build/lib%s.a" %(libpaths,moduleid)) 184 | 185 | def generate_apidoc(apidoc_build_path): 186 | global options 187 | 188 | if options.skip_docs: 189 | info("Skipping documentation generation.") 190 | return False 191 | else: 192 | info("Module apidoc generation can be skipped using --skip-docs") 193 | apidoc_path = os.path.join(cwd, "apidoc") 194 | if not os.path.exists(apidoc_path): 195 | warn("Skipping apidoc generation. No apidoc folder found at: %s" % apidoc_path) 196 | return False 197 | 198 | if not os.path.exists(apidoc_build_path): 199 | os.makedirs(apidoc_build_path) 200 | ti_root = string.strip(subprocess.check_output(["echo $TI_ROOT"], shell=True)) 201 | if not len(ti_root) > 0: 202 | warn("Not generating documentation from the apidoc folder. The titanium_mobile repo could not be found.") 203 | warn("Set the TI_ROOT environment variable to the parent folder where the titanium_mobile repo resides (eg.'export TI_ROOT=/Path').") 204 | return False 205 | docgen = os.path.join(ti_root, "titanium_mobile", "apidoc", "docgen.py") 206 | if not os.path.exists(docgen): 207 | warn("Not generating documentation from the apidoc folder. Couldn't find docgen.py at: %s" % docgen) 208 | return False 209 | 210 | info("Generating documentation from the apidoc folder.") 211 | rc = os.system("\"%s\" --format=jsca,modulehtml --css=styles.css -o \"%s\" -e \"%s\"" % (docgen, apidoc_build_path, apidoc_path)) 212 | if rc != 0: 213 | die("docgen failed") 214 | return True 215 | 216 | def package_module(manifest,mf,config): 217 | name = manifest['name'].lower() 218 | moduleid = manifest['moduleid'].lower() 219 | version = manifest['version'] 220 | modulezip = '%s-iphone-%s.zip' % (moduleid,version) 221 | if os.path.exists(modulezip): os.remove(modulezip) 222 | zf = zipfile.ZipFile(modulezip, 'w', zipfile.ZIP_DEFLATED) 223 | modulepath = 'modules/iphone/%s/%s' % (moduleid,version) 224 | zf.write(mf,'%s/manifest' % modulepath) 225 | libname = 'lib%s.a' % moduleid 226 | zf.write('build/%s' % libname, '%s/%s' % (modulepath,libname)) 227 | docs = generate_doc(config) 228 | if docs!=None: 229 | for doc in docs: 230 | for file, html in doc.iteritems(): 231 | filename = string.replace(file,'.md','.html') 232 | zf.writestr('%s/documentation/%s'%(modulepath,filename),html) 233 | 234 | apidoc_build_path = os.path.join(cwd, "build", "apidoc") 235 | if generate_apidoc(apidoc_build_path): 236 | for file in os.listdir(apidoc_build_path): 237 | if file in ignoreFiles or os.path.isdir(os.path.join(apidoc_build_path, file)): 238 | continue 239 | zf.write(os.path.join(apidoc_build_path, file), '%s/documentation/apidoc/%s' % (modulepath, file)) 240 | 241 | zip_dir(zf,'assets',modulepath,['.pyc','.js']) 242 | zip_dir(zf,'example',modulepath,['.pyc']) 243 | zip_dir(zf,'platform',modulepath,['.pyc','.js']) 244 | zf.write('LICENSE','%s/LICENSE' % modulepath) 245 | zf.write('module.xcconfig','%s/module.xcconfig' % modulepath) 246 | exports_file = 'metadata.json' 247 | if os.path.exists(exports_file): 248 | zf.write(exports_file, '%s/%s' % (modulepath, exports_file)) 249 | zf.close() 250 | 251 | 252 | if __name__ == '__main__': 253 | global options 254 | 255 | parser = optparse.OptionParser() 256 | parser.add_option("-s", "--skip-docs", 257 | dest="skip_docs", 258 | action="store_true", 259 | help="Will skip building documentation in apidoc folder", 260 | default=False) 261 | (options, args) = parser.parse_args() 262 | 263 | manifest,mf = validate_manifest() 264 | validate_license() 265 | config = read_ti_xcconfig() 266 | 267 | sdk = find_sdk(config) 268 | sys.path.insert(0,os.path.join(sdk,'iphone')) 269 | sys.path.append(os.path.join(sdk, "common")) 270 | 271 | compile_js(manifest,config) 272 | build_module(manifest,config) 273 | package_module(manifest,mf,config) 274 | sys.exit(0) 275 | 276 | -------------------------------------------------------------------------------- /com.citytelecom.tiparallaxheader-iphone-0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jc888/TiParallaxHeader/ec3491422b49e384bd83d13e4cdf0aceb1fb1b7d/com.citytelecom.tiparallaxheader-iphone-0.1.zip -------------------------------------------------------------------------------- /dist/com.citytelecom.tiparallaxheader-iphone-0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jc888/TiParallaxHeader/ec3491422b49e384bd83d13e4cdf0aceb1fb1b7d/dist/com.citytelecom.tiparallaxheader-iphone-0.1.zip -------------------------------------------------------------------------------- /dist/com.citytelecom.tiparallaxheader-iphone-0.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jc888/TiParallaxHeader/ec3491422b49e384bd83d13e4cdf0aceb1fb1b7d/dist/com.citytelecom.tiparallaxheader-iphone-0.2.zip -------------------------------------------------------------------------------- /documentation/index.md: -------------------------------------------------------------------------------- 1 | # TiParallaxHeader Module 2 | 3 | ### Demo Video 4 | 5 | [![Gif of effect](http://updatedisplaylist.com/images/TiParallaxHeader-0_2.gif)](http://youtu.be/H_VSO-k_OLw) 6 | 7 | ## Description 8 | 9 | Insert a slightly bouncy parallax scrolling header for a Titanium ListView. 10 | Similar effect to Spotify/Path apps. 11 | 12 | ## Referencing the module in your Ti mobile application 13 | 14 | Simply add the following lines to your `tiapp.xml` file: 15 | 16 | 17 | com.citytelecom.tiparallaxheader 18 | 19 | 20 | ## Accessing the TiParallaxHeader Module 21 | 22 | To access this module from JavaScript, you would do the following: 23 | 24 | ```javascript 25 | var TiParallaxHeader = require("com.citytelecom.tiparallaxheader"); 26 | ``` 27 | 28 | Do this only once for your app, preferably in your app.js / alloy.js file. 29 | This will extend ListView with additional methods 30 | 31 | 32 | ## Usage 33 | 34 | ListView will be extended with the following methods. Only call them after the ListView has finished renderering, by listening to the 'postlayout' event 35 | 36 | ```javascript 37 | function onListViewPostlayout(e) { 38 | // attach the parallax header 39 | listView.addParallaxWithImage('http://example.com/image.png', 350); 40 | } 41 | //must wait till ListView has sized itself 42 | listView.addEventListener('postlayout',onListViewPostlayout); 43 | ``` 44 | ## ListView functions 45 | 46 | ### addParallaxWithImage 47 | 48 | Add an expanding parallax image for the background of the header 49 | 50 | local file 51 | ```javascript 52 | var url = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'ParallaxImage.jpg').nativePath; 53 | var headerHeight = 350; 54 | listView.addParallaxWithImage(url, headerHeight); 55 | ``` 56 | 57 | Remote file 58 | ```javascript 59 | listView.addParallaxWithImage('http://example.com/image.png', 350); 60 | ``` 61 | 62 | ### addParallaxWithView 63 | Add a titanium View to the header, automatically set to resize within header area 64 | 65 | ```javascript 66 | var headerView = Ti.UI.createView({ 67 | backgroundColor:'red' 68 | }); 69 | 70 | //this will reparent the headerView 71 | listView.addParallaxWithView(headerView, 350); 72 | ``` 73 | 74 | ### setSectionHeaderInset 75 | Insetting the header into the listview, causes all sticky ListView section headers to stick below the header height. 76 | This method allows giving an offset to the Y position of the headers. 77 | 78 | ```javascript 79 | listView.setSectionHeaderInset(-350); 80 | ``` 81 | 82 | ### setFadeoutOverHeight 83 | Calling this method will create a fade out effect of the HeaderView, when scrolling greater then the header height. 84 | Accepts a single parameter, of a height in pixels. 85 | 86 | ```javascript 87 | //pulling the scroll so that the header expands beyond the height (>350) within 50 pixels the view will gradually fade 88 | listView.setFadeoutOverHeight(50) 89 | ``` 90 | 91 | ## Author 92 | 93 | **James Chow** 94 | web: http://updatedisplaylist.com 95 | email: james@jc888.co.uk 96 | 97 | ## License 98 | 99 | Copyright (c) 2010-2014 James Chow 100 | 101 | Permission is hereby granted, free of charge, to any person obtaining a copy 102 | of this software and associated documentation files (the "Software"), to deal 103 | in the Software without restriction, including without limitation the rights 104 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 105 | copies of the Software, and to permit persons to whom the Software is 106 | furnished to do so, subject to the following conditions: 107 | 108 | The above copyright notice and this permission notice shall be included in 109 | all copies or substantial portions of the Software. 110 | 111 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 112 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 113 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 114 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 115 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 116 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 117 | THE SOFTWARE. 118 | -------------------------------------------------------------------------------- /example/ParallaxImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jc888/TiParallaxHeader/ec3491422b49e384bd83d13e4cdf0aceb1fb1b7d/example/ParallaxImage.jpg -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | //import the module 2 | //NOTE : IMPORT ONLY ONCE, for Alloy run in app.js 3 | var TiParallaxHeader = require('com.citytelecom.tiparallaxheader'); 4 | 5 | // open a single window 6 | var win = Ti.UI.createWindow({ 7 | backgroundColor : 'white' 8 | }); 9 | 10 | // ==== NavBar ==== 11 | var NAVBAR_HEIGHT = 50; 12 | var navbar = Ti.UI.createLabel({top : 0, width : '100%', height : NAVBAR_HEIGHT, opacity:0.8, backgroundColor : '#0000ff', text:'See through Navbar'}); 13 | // ==== NavBar ==== 14 | 15 | //height of the Parallax header, does not except DP 16 | var PARALLAX_HEADER_HEIGHT = 350; 17 | 18 | // ==== Parallax Header View ==== 19 | var headerView = Ti.UI.createView({ 20 | width : Ti.UI.FILL, 21 | height : PARALLAX_HEADER_HEIGHT 22 | }); 23 | 24 | var shadowView = Ti.UI.createView({ 25 | width : Ti.UI.FILL, 26 | height : Ti.UI.FILL, 27 | backgroundColor:'black', 28 | opacity:'0.5' 29 | }); 30 | 31 | var view1 = Ti.UI.createLabel({width : '150dp', height : '150dp', backgroundColor : '#ffdddd', text:'Swipe Me right'}); 32 | var view2 = Ti.UI.createLabel({width : '150dp', height : '150dp',backgroundColor : '#ddffdd', text:'Swipe Me left'}); 33 | 34 | var scrollableView = Ti.UI.createScrollableView({ 35 | width : Ti.UI.FILL, 36 | height : '150dp', 37 | views : [view1, view2] 38 | }); 39 | 40 | headerView.add(scrollableView); 41 | headerView.add(shadowView); 42 | 43 | // ==== List View ==== 44 | var listView = Ti.UI.createListView({ 45 | width : Ti.UI.FILL, 46 | height : Ti.UI.FILL 47 | }); 48 | 49 | var section = Ti.UI.createListSection(); 50 | 51 | //Floating section header view 52 | var sectionHeaderView = Ti.UI.createLabel({ 53 | width : Ti.UI.FILL, 54 | height:'50dp', 55 | backgroundColor:'red', 56 | text:'Section Header' 57 | }); 58 | section.setHeaderView(sectionHeaderView); 59 | 60 | //rows 61 | var items = []; 62 | for (var i = 0; i < 20; i++) { 63 | items[i] = {properties : {title : 'Item' + i}}; 64 | } 65 | section.setItems(items); 66 | listView.setSections([section]); 67 | 68 | function onListViewPostlayout(e) { 69 | 70 | //Parallax Image Background Image, remote / local urls accepted 71 | var imagePath = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'ParallaxImage.jpg').nativePath; 72 | listView.addParallaxWithImage(imagePath, PARALLAX_HEADER_HEIGHT); 73 | 74 | //Parallax HeaderView 75 | listView.addParallaxWithView(headerView, PARALLAX_HEADER_HEIGHT); 76 | 77 | // === Sticky ListView section header === 78 | // adding a parallax header will cause the sticky section headers in your ListView, 79 | // to stick below the parallax header height, use this method to offset their sticky position. 80 | listView.setSectionHeaderInset(-PARALLAX_HEADER_HEIGHT + NAVBAR_HEIGHT); 81 | 82 | //Fadeout headerview within the space of X pixels above the header height 83 | listView.setFadeoutOverHeight(50); 84 | 85 | //Scroll to first item to force redraw of list 86 | listView.scrollToItem(0, 0); 87 | } 88 | 89 | //must wait till ListView has sized itself 90 | win.addEventListener('open',onListViewPostlayout); 91 | 92 | win.add(listView); 93 | win.add(navbar); 94 | win.open(); 95 | 96 | -------------------------------------------------------------------------------- /hooks/README: -------------------------------------------------------------------------------- 1 | These files are not yet supported as of 1.4.0 but will be in a near future release. 2 | -------------------------------------------------------------------------------- /hooks/add.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project add hook that will be 4 | # called when your module is added to a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your add hook here (optional) 26 | 27 | 28 | # exit 29 | sys.exit(0) 30 | 31 | 32 | 33 | if __name__ == '__main__': 34 | main(sys.argv,len(sys.argv)) 35 | 36 | -------------------------------------------------------------------------------- /hooks/install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module install hook that will be 4 | # called when your module is first installed 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your install hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | 17 | if __name__ == '__main__': 18 | main(sys.argv,len(sys.argv)) 19 | 20 | -------------------------------------------------------------------------------- /hooks/remove.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project remove hook that will be 4 | # called when your module is remove from a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your remove hook here (optional) 26 | 27 | # exit 28 | sys.exit(0) 29 | 30 | 31 | 32 | if __name__ == '__main__': 33 | main(sys.argv,len(sys.argv)) 34 | 35 | -------------------------------------------------------------------------------- /hooks/uninstall.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module uninstall hook that will be 4 | # called when your module is uninstalled 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your uninstall hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | if __name__ == '__main__': 17 | main(sys.argv,len(sys.argv)) 18 | 19 | -------------------------------------------------------------------------------- /manifest: -------------------------------------------------------------------------------- 1 | # 2 | # this is your module manifest and used by Titanium 3 | # during compilation, packaging, distribution, etc. 4 | # 5 | version: 0.2 6 | apiversion: 3 7 | description: Parallax HeaderView for the Titanium ListView 8 | author: James Chow 9 | license: MIT 10 | copyright: City Telecom Services ltd 11 | 12 | 13 | # these should not be edited 14 | name: TiParallaxHeader 15 | moduleid: com.citytelecom.tiparallaxheader 16 | guid: d9d5b1e0-b0fc-4814-bb86-41dc12db6808 17 | platform: iphone 18 | minsdk: 3.2.1.GA 19 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | {"exports": ["UI.createListView", "UI.createListView"]} -------------------------------------------------------------------------------- /module.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE 3 | // PICKED UP DURING THE APP BUILD FOR YOUR MODULE 4 | // 5 | // see the following webpage for instructions on the settings 6 | // for this file: 7 | // http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeBuildSystem/400-Build_Configurations/build_configs.html 8 | // 9 | 10 | // 11 | // How to add a Framework (example) 12 | // 13 | // OTHER_LDFLAGS=$(inherited) -framework Foo 14 | // 15 | // Adding a framework for a specific version(s) of iPhone: 16 | // 17 | // OTHER_LDFLAGS[sdk=iphoneos4*]=$(inherited) -framework Foo 18 | // OTHER_LDFLAGS[sdk=iphonesimulator4*]=$(inherited) -framework Foo 19 | // 20 | // 21 | // How to add a compiler define: 22 | // 23 | // OTHER_CFLAGS=$(inherited) -DFOO=1 24 | // 25 | // 26 | // IMPORTANT NOTE: always use $(inherited) in your overrides 27 | // 28 | -------------------------------------------------------------------------------- /platform/README: -------------------------------------------------------------------------------- 1 | You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module. 2 | 3 | When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder. 4 | -------------------------------------------------------------------------------- /timodule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /titanium.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // CHANGE THESE VALUES TO REFLECT THE VERSION (AND LOCATION IF DIFFERENT) 4 | // OF YOUR TITANIUM SDK YOU'RE BUILDING FOR 5 | // 6 | // 7 | TITANIUM_SDK_VERSION = 3.2.1.GA 8 | 9 | 10 | // 11 | // THESE SHOULD BE OK GENERALLY AS-IS 12 | // 13 | TITANIUM_SDK = ~/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION) 14 | TITANIUM_BASE_SDK = "$(TITANIUM_SDK)/iphone/include" 15 | TITANIUM_BASE_SDK2 = "$(TITANIUM_SDK)/iphone/include/TiCore" 16 | HEADER_SEARCH_PATHS= $(TITANIUM_BASE_SDK) $(TITANIUM_BASE_SDK2) 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------