├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── STXDynamicTableView ├── Buttons │ ├── STXButton.h │ └── STXButton.m ├── Categories │ ├── NSString+Emoji.h │ ├── NSString+Emoji.m │ ├── UIButton+STXButton.h │ ├── UIButton+STXButton.m │ ├── UIImage+STXImage.h │ ├── UIImage+STXImage.m │ ├── UIImageView+Masking.h │ ├── UIImageView+Masking.m │ ├── UIViewController+Indicator.h │ ├── UIViewController+Indicator.m │ ├── UIViewController+Sharing.h │ └── UIViewController+Sharing.m ├── Cells │ ├── STXCaptionCell.h │ ├── STXCaptionCell.m │ ├── STXCommentCell.h │ ├── STXCommentCell.m │ ├── STXFeedPhotoCell.h │ ├── STXFeedPhotoCell.m │ ├── STXFeedPhotoCell.xib │ ├── STXLikesCell.h │ ├── STXLikesCell.m │ ├── STXUserActionCell.h │ ├── STXUserActionCell.m │ └── STXUserActionCell.xib ├── Items │ ├── STXCommentItem.h │ ├── STXPostItem.h │ └── STXUserItem.h ├── Labels │ ├── STXAttributedLabel.h │ ├── STXAttributedLabel.m │ ├── STXLabel.h │ └── STXLabel.m ├── Protocols │ ├── STXFeedTableViewDataSource.h │ ├── STXFeedTableViewDataSource.m │ ├── STXFeedTableViewDelegate.h │ └── STXFeedTableViewDelegate.m └── STXDynamicTableView.h ├── STXDynamicTableViewExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── STXDynamicTableViewExample.xcscheme ├── STXDynamicTableViewExample.xcworkspace └── contents.xcworkspacedata ├── STXDynamicTableViewExample ├── Feed.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── LaunchScreen.storyboard ├── Models │ ├── STXComment.h │ ├── STXComment.m │ ├── STXPost.h │ ├── STXPost.m │ ├── STXUser.h │ └── STXUser.m ├── Resources │ └── instagram_media_popular.json ├── STXAppDelegate.h ├── STXAppDelegate.m ├── STXDynamicTableViewExample-Info.plist ├── STXDynamicTableViewExample-Prefix.pch ├── STXFeedViewController.h ├── STXFeedViewController.m ├── en.lproj │ └── InfoPlist.strings └── main.m ├── STXDynamicTableViewExampleTests ├── STXDynamicTableViewExampleTests-Info.plist ├── STXDynamicTableViewExampleTests.m └── en.lproj │ └── InfoPlist.strings ├── bin └── setup └── build.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X Finder 2 | .DS_Store 3 | 4 | # Xcode per-user config 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | xcuserdata 12 | *.xccheckout 13 | 14 | # Build products 15 | build/ 16 | *.o 17 | *.LinkFileList 18 | *.hmap 19 | 20 | # Automatic backup files 21 | *~.nib/ 22 | *.swp 23 | *~ 24 | *.dat 25 | *.dep 26 | 27 | # CocoaPods 28 | # 29 | # We recommend against adding the Pods directory to your .gitignore. However 30 | # you should judge for yourself, the pros and cons are mentioned at: 31 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 32 | # 33 | Pods/ 34 | 35 | # AppCode specific files 36 | .idea/ 37 | *.iml 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_project: STXDynamicTableViewExample.xcodeproj 3 | cache: 4 | directories: 5 | - ./vendor/bundle 6 | - ./Pods 7 | before_install: 8 | - bundle update 9 | install: 10 | - bundle exec pod install 11 | script: 12 | - "./build.sh" 13 | osx_image: xcode7 14 | env: 15 | global: 16 | - REPO_SLUG="2359media/STXDynamicTableView" 17 | - PRODUCT_NAME="STXDynamicTableViewExample" 18 | - PROJECT_NAME="STXDynamicTableViewExample.xcworkspace" 19 | - SCHEME_NAME="STXDynamicTableViewExample" 20 | - CONFIGURATION_NAME="Debug" 21 | - INFO_PLIST_PATH="STXDynamicTableViewExample/STXDynamicTableViewExample-Info.plist" 22 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods' 4 | gem 'xcpretty' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 2359 Media Pte Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, "8.0" 3 | 4 | target "STXDynamicTableViewExample" do 5 | pod 'TTTAttributedLabel' 6 | pod 'PureLayout' 7 | pod 'KZPropertyMapper' 8 | pod 'MHPrettyDate' 9 | pod 'AFNetworking' 10 | pod 'UALogger' 11 | end 12 | 13 | target "STXDynamicTableViewExampleTests" do 14 | 15 | end 16 | 17 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.5.0): 3 | - AFNetworking/NSURLConnection (= 2.5.0) 4 | - AFNetworking/NSURLSession (= 2.5.0) 5 | - AFNetworking/Reachability (= 2.5.0) 6 | - AFNetworking/Security (= 2.5.0) 7 | - AFNetworking/Serialization (= 2.5.0) 8 | - AFNetworking/UIKit (= 2.5.0) 9 | - AFNetworking/NSURLConnection (2.5.0): 10 | - AFNetworking/Reachability 11 | - AFNetworking/Security 12 | - AFNetworking/Serialization 13 | - AFNetworking/NSURLSession (2.5.0): 14 | - AFNetworking/Reachability 15 | - AFNetworking/Security 16 | - AFNetworking/Serialization 17 | - AFNetworking/Reachability (2.5.0) 18 | - AFNetworking/Security (2.5.0) 19 | - AFNetworking/Serialization (2.5.0) 20 | - AFNetworking/UIKit (2.5.0): 21 | - AFNetworking/NSURLConnection 22 | - AFNetworking/NSURLSession 23 | - KZPropertyMapper (2.5.1) 24 | - MHPrettyDate (1.1.1) 25 | - PureLayout (2.0.4) 26 | - TTTAttributedLabel (1.13.0) 27 | - UALogger (0.3) 28 | 29 | DEPENDENCIES: 30 | - AFNetworking 31 | - KZPropertyMapper 32 | - MHPrettyDate 33 | - PureLayout 34 | - TTTAttributedLabel 35 | - UALogger 36 | 37 | SPEC CHECKSUMS: 38 | AFNetworking: 96ac9bf3eda33582701cb1fcc5b896aa1e20311e 39 | KZPropertyMapper: e80e83aa3bc6d37a6dd55e3d04a01afcb8c23d3c 40 | MHPrettyDate: e0e5975041f0210c0542f4c5d7e3e44d1a8537be 41 | PureLayout: 39ab7d09edec14750ba3982bf19126916861bba5 42 | TTTAttributedLabel: 8aeaf67481bb38970711e2641211cba912bd5239 43 | UALogger: ccc041e5982dbd274ab5805f914076fef9bf29cf 44 | 45 | COCOAPODS: 0.38.2 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/2359media/STXDynamicTableView.svg?branch=master)](https://travis-ci.org/2359media/STXDynamicTableView) 2 | 3 | # STXDynamicTableView 4 | 5 | `STXDynamicTableView` is designed to solve the common use case to display a feed of photos with their corresponding likes, caption, and comments. It's inspired by Instagram feed table view. 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | --- 19 | ## Example Project 20 | 21 | We're using [cocoapods](http://cocoapods.org/) to update the existing 3rd party libraries (Pods) in the sample code: 22 | 23 | $ pod install 24 | 25 | Then, open `STXDynamicTableViewExample.xcworkspace` to build and run. 26 | 27 | --- 28 | ## Usage 29 | 30 | Import the whole `STXDynamicTableView` source files into your project, and import the main header file: 31 | 32 | #import "STXDynamicTableView.h" 33 | 34 | Supply your table view in the view controller, then set the delegate and data source: 35 | 36 | STXFeedTableViewDataSource *dataSource = [[STXFeedTableViewDataSource alloc] initWithController:self tableView:self.tableView]; 37 | self.tableView.dataSource = dataSource; 38 | self.tableViewDataSource = dataSource; 39 | 40 | STXFeedTableViewDelegate *delegate = [[STXFeedTableViewDelegate alloc] initWithController:self]; 41 | self.tableView.delegate = delegate; 42 | self.tableViewDelegate = delegate; 43 | 44 | Populate your data models to the table view data source: 45 | 46 | NSDictionary *instagramPopularMediaDictionary = jsonObject; 47 | if (instagramPopularMediaDictionary) { 48 | id data = [instagramPopularMediaDictionary valueForKey:@"data"]; 49 | NSArray *mediaDataArray = data; 50 | 51 | NSMutableArray *posts = [NSMutableArray array]; 52 | for (NSDictionary *mediaDictionary in mediaDataArray) { 53 | STXPost *post = [[STXPost alloc] initWithDictionary:mediaDictionary]; 54 | [posts addObject:post]; 55 | } 56 | 57 | self.tableViewDataSource.posts = [posts copy]; 58 | 59 | [self.tableView reloadData]; 60 | } 61 | 62 | Your data models need to conform to `STXPostItem`, `STXCommentItem`, and `STXUserItem` to be able to use the built-in table view data source and delegate. 63 | 64 | --- 65 | ## Background 66 | 67 | Read [Rebuilding Instagram feed table 68 | view](http://engineering.2359media.net/blog/2014/04/16/rebuilding-instagram-feed-table-view/) to understand the challenges, difficulties, and how do we solve the issue of rebuilding the table view style popularized by Instagram app with Auto Layout. 69 | 70 | --- 71 | ## TODO 72 | 73 | * Migrate to Swift with better value types for data models and protocols 74 | * Using alternative architecture such as [AsyncDisplayKit](http://asyncdisplaykit.org/), [ComponentKit](http://componentkit.org/), or [React Native](http://www.reactnative.com/). 75 | 76 | --- 77 | ## Disclaimer 78 | `STXDynamicTableView` is simply a reusable code that you can use in your own project for any purpose as outlined in the LICENSE file. It's not a fully-fledged library, although we're taking steps to go there as time allows. 79 | 80 | ___ 81 | ##Feedback 82 | We'd love to hear feedback. Create Github issues, pull requests, or hit us up on [Twitter](http://twitter.com/2359eng). 83 | 84 | --- 85 | ## Credits 86 | This project uses the following 3rd party libraries: 87 | 88 | * Tyler Fox for [PureLayout](https://github.com/smileyborg/PureLayout) 89 | * Mattt Thompson for [AFNetworking](https://github.com/AFNetworking/AFNetworking) and [TTTAttributedLabel](https://github.com/mattt/TTTAttributedLabel) 90 | * Krzysztof Zabłocki for [KZPropertyMapper](https://github.com/krzysztofzablocki/KZPropertyMapper) 91 | * Bobby Williams for [MHPrettyDate](https://github.com/bobjustbob/MHPrettyDate) 92 | * Urban Apps for [UALogger](https://github.com/UrbanApps/UALogger) 93 | 94 | ___ 95 | ## License 96 | `STXDynamicTableView` is available under the MIT license. See the LICENSE file for more info. 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /STXDynamicTableView/Buttons/STXButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXButton.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 4/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @interface STXButton : UIButton 10 | 11 | - (void)stx_reverseButtonStateAppearance; 12 | 13 | - (void)stx_resetButtonStateAppearance; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STXDynamicTableView/Buttons/STXButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXButton.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 4/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXButton.h" 10 | 11 | @interface STXButton () 12 | 13 | @property (strong, nonatomic) UIImage *stx_highlightedImage; 14 | @property (strong, nonatomic) UIImage *stx_normalImage; 15 | 16 | @end 17 | 18 | @implementation STXButton 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame 21 | { 22 | self = [super initWithFrame:frame]; 23 | if (self) { 24 | [self stx_setupAppearance]; 25 | } 26 | return self; 27 | } 28 | 29 | - (instancetype)init 30 | { 31 | self = [super init]; 32 | if (self) { 33 | [self stx_setupAppearance]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)awakeFromNib 39 | { 40 | [super awakeFromNib]; 41 | 42 | [self stx_setupAppearance]; 43 | } 44 | 45 | - (void)layoutSubviews 46 | { 47 | [super layoutSubviews]; 48 | 49 | // Keep the image for each state initially when laying out subviews 50 | if (self.stx_normalImage == nil) { 51 | self.stx_normalImage = [self imageForState:UIControlStateNormal]; 52 | } 53 | 54 | if (self.stx_highlightedImage == nil) { 55 | self.stx_highlightedImage = [self imageForState:UIControlStateHighlighted]; 56 | } 57 | } 58 | 59 | - (void)stx_setupAppearance 60 | { 61 | } 62 | 63 | - (void)stx_reverseButtonStateAppearance 64 | { 65 | } 66 | 67 | - (void)stx_resetButtonStateAppearance 68 | { 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/NSString+Emoji.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Emoji.h 3 | // 4 | // Credit to: https://gist.github.com/cihancimen/4146056 5 | // 6 | // 7 | 8 | @import Foundation; 9 | 10 | @interface NSString (Emoji) 11 | 12 | - (BOOL)stringContainsEmoji; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/NSString+Emoji.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Emoji.h 3 | // 4 | // Credit to: https://gist.github.com/cihancimen/4146056 5 | // 6 | // 7 | 8 | #import "NSString+Emoji.h" 9 | 10 | @implementation NSString (Emoji) 11 | 12 | - (BOOL)stringContainsEmoji { 13 | __block BOOL returnValue = NO; 14 | [self enumerateSubstringsInRange:NSMakeRange(0, [self length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock: 15 | ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { 16 | 17 | const unichar hs = [substring characterAtIndex:0]; 18 | // surrogate pair 19 | if (0xd800 <= hs && hs <= 0xdbff) { 20 | if (substring.length > 1) { 21 | const unichar ls = [substring characterAtIndex:1]; 22 | const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; 23 | if (0x1d000 <= uc && uc <= 0x1f77f) { 24 | returnValue = YES; 25 | } 26 | } 27 | } else if (substring.length > 1) { 28 | const unichar ls = [substring characterAtIndex:1]; 29 | if (ls == 0x20e3) { 30 | returnValue = YES; 31 | } 32 | 33 | } else { 34 | // non surrogate 35 | if (0x2100 <= hs && hs <= 0x27ff) { 36 | returnValue = YES; 37 | } else if (0x2B05 <= hs && hs <= 0x2b07) { 38 | returnValue = YES; 39 | } else if (0x2934 <= hs && hs <= 0x2935) { 40 | returnValue = YES; 41 | } else if (0x3297 <= hs && hs <= 0x3299) { 42 | returnValue = YES; 43 | } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) { 44 | returnValue = YES; 45 | } 46 | } 47 | }]; 48 | 49 | return returnValue; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIButton+STXButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+STXButton.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface UIButton (STXButton) 12 | 13 | - (void)stx_setNormalAppearance; 14 | 15 | - (void)stx_setSelectedAppearance; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIButton+STXButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+STXButton.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "UIButton+STXButton.h" 10 | 11 | @implementation UIButton (STXButton) 12 | 13 | - (void)stx_setNormalAppearance 14 | { 15 | [self setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; 16 | [self setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted]; 17 | [self setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; 18 | 19 | self.titleLabel.backgroundColor = self.backgroundColor; 20 | } 21 | 22 | - (void)stx_setSelectedAppearance 23 | { 24 | [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 25 | [self setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted]; 26 | [self setTitleColor:[UIColor lightGrayColor] forState:UIControlStateSelected]; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIImage+STXImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+STXImage.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface UIImage (STXImage) 12 | 13 | + (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)imageSize; 14 | 15 | + (UIImage *)cropImageWithInfo:(NSDictionary *)info; 16 | 17 | - (UIImage *)squareAspectFilledImage; 18 | 19 | - (UIImage *)circleBorderedAtWidth:(CGFloat)width forImageWithSize:(CGSize)imageSize; 20 | 21 | - (UIImage *)watermarkedImage; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIImage+STXImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+STXImage.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "UIImage+STXImage.h" 10 | 11 | @implementation UIImage (STXImage) 12 | 13 | + (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)imageSize 14 | { 15 | CGRect rect = CGRectMake(0.f, 0.f, imageSize.width, imageSize.height); 16 | UIGraphicsBeginImageContext(rect.size); 17 | CGContextRef context = UIGraphicsGetCurrentContext(); 18 | 19 | CGContextSetFillColorWithColor(context, [color CGColor]); 20 | CGContextFillRect(context, rect); 21 | 22 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 23 | UIGraphicsEndImageContext(); 24 | 25 | return image; 26 | } 27 | 28 | + (UIImage *)cropImageWithInfo:(NSDictionary *)info 29 | { 30 | UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage]; 31 | CGRect cropRect = [[info objectForKey:UIImagePickerControllerCropRect] CGRectValue]; 32 | CGAffineTransform rotateTransform = CGAffineTransformIdentity; 33 | 34 | switch (originalImage.imageOrientation) { 35 | case UIImageOrientationDown: 36 | rotateTransform = CGAffineTransformRotate(rotateTransform, M_PI); 37 | rotateTransform = CGAffineTransformTranslate(rotateTransform, -originalImage.size.width, -originalImage.size.height); 38 | break; 39 | 40 | case UIImageOrientationLeft: 41 | rotateTransform = CGAffineTransformRotate(rotateTransform, M_PI_2); 42 | rotateTransform = CGAffineTransformTranslate(rotateTransform, 0.0, -originalImage.size.height); 43 | break; 44 | 45 | case UIImageOrientationRight: 46 | rotateTransform = CGAffineTransformRotate(rotateTransform, -M_PI_2); 47 | rotateTransform = CGAffineTransformTranslate(rotateTransform, -originalImage.size.width, 0.0); 48 | break; 49 | 50 | default: 51 | break; 52 | } 53 | 54 | CGRect rotatedCropRect = CGRectApplyAffineTransform(cropRect, rotateTransform); 55 | 56 | CGImageRef croppedImage = CGImageCreateWithImageInRect([originalImage CGImage], rotatedCropRect); 57 | UIImage *result = [UIImage imageWithCGImage:croppedImage scale:[UIScreen mainScreen].scale orientation:originalImage.imageOrientation]; 58 | CGImageRelease(croppedImage); 59 | 60 | return result; 61 | } 62 | 63 | - (UIImage *)squareAspectFilledImage 64 | { 65 | // This calculates the crop area. 66 | CGFloat originalWidth = self.size.width * self.scale; 67 | CGFloat originalHeight = self.size.height * self.scale; 68 | 69 | CGFloat edge = fminf(originalWidth, originalHeight); 70 | 71 | CGFloat posX = (originalWidth - edge) / 2.f; 72 | CGFloat posY = (originalHeight - edge) / 2.f; 73 | 74 | CGRect cropSquare = CGRectMake(posX, posY, edge, edge); 75 | CGImageRef croppedCGImage = CGImageCreateWithImageInRect(self.CGImage, cropSquare); 76 | UIImage *croppedImage = [UIImage imageWithCGImage:croppedCGImage scale:self.scale orientation:self.imageOrientation]; 77 | CGImageRelease(croppedCGImage); 78 | return croppedImage; 79 | } 80 | 81 | - (UIImage *)circleBorderedAtWidth:(CGFloat)width forImageWithSize:(CGSize)imageSize 82 | { 83 | UIImage *squareAspectFilledImage = [self squareAspectFilledImage]; 84 | 85 | UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); 86 | 87 | [squareAspectFilledImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)]; 88 | 89 | CGContextRef context = UIGraphicsGetCurrentContext(); 90 | 91 | [[UIColor whiteColor] setStroke]; 92 | 93 | CGContextSetLineWidth(context, width * [[UIScreen mainScreen] scale]); 94 | 95 | CGRect circleRect = CGRectMake(0, 0, imageSize.width, imageSize.height); 96 | CGContextStrokeEllipseInRect(context, circleRect); 97 | 98 | UIImage *circledImage = UIGraphicsGetImageFromCurrentImageContext(); 99 | 100 | UIGraphicsEndImageContext(); 101 | 102 | return circledImage; 103 | } 104 | 105 | - (UIImage *)watermarkedImage 106 | { 107 | UIImage *watermarkImage = [UIImage imageNamed:@"Watermark"]; 108 | CGSize watermarkSize = watermarkImage.size; 109 | 110 | CGSize totalSize = CGSizeMake(self.size.width, self.size.height + watermarkSize.height); 111 | 112 | UIGraphicsBeginImageContextWithOptions(totalSize, YES, 0); 113 | 114 | CGRect drawRect = CGRectMake(0, 0, totalSize.width, self.size.height); 115 | [self drawInRect:drawRect]; 116 | 117 | CGRect watermarkRect = CGRectMake(0, self.size.height, CGRectGetWidth(drawRect), watermarkSize.height); 118 | [watermarkImage drawInRect:watermarkRect]; 119 | 120 | UIImage *watermarkedImage = UIGraphicsGetImageFromCurrentImageContext(); 121 | return watermarkedImage; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIImageView+Masking.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Masking.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 7/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @interface UIImageView (Masking) 10 | 11 | - (void)setCircleImageWithURL:(NSURL *)imageURL placeholderImage:(UIImage *)placeholderImage; 12 | 13 | - (void)setCircleImageWithURL:(NSURL *)imageURL placeholderImage:(UIImage *)placeholderImage borderWidth:(CGFloat)borderWidth; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIImageView+Masking.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Masking.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 7/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "UIImageView+Masking.h" 10 | #import "UIImage+STXImage.h" 11 | 12 | #import 13 | #import 14 | 15 | @interface STXImageCache : NSCache 16 | @end 17 | 18 | #pragma mark - 19 | 20 | static char kSTXSharedImageCacheKey; 21 | static char kSTXCircleImageOperationKey; 22 | 23 | @interface UIImageView (_Masking) 24 | @property (readwrite, nonatomic, strong, setter = stx_setCircleImageOperation:) NSOperation *stx_circleImageOperation; 25 | @end 26 | 27 | @implementation UIImageView (_Masking) 28 | 29 | + (NSOperationQueue *)stx_sharedCircleImageOperationQueue 30 | { 31 | static NSOperationQueue *_stx_sharedCircleImageOperationQueue = nil; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | _stx_sharedCircleImageOperationQueue = [[NSOperationQueue alloc] init]; 35 | _stx_sharedCircleImageOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; 36 | }); 37 | 38 | return _stx_sharedCircleImageOperationQueue; 39 | } 40 | 41 | - (NSOperation *)stx_circleImageOperation 42 | { 43 | return (NSOperation *)objc_getAssociatedObject(self, &kSTXCircleImageOperationKey); 44 | } 45 | 46 | - (void)stx_setCircleImageOperation:(NSOperation *)circleImageOperation 47 | { 48 | objc_setAssociatedObject(self, &kSTXCircleImageOperationKey, circleImageOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 49 | } 50 | 51 | @end 52 | 53 | @implementation UIImageView (Masking) 54 | 55 | + (id)sharedImageCache { 56 | static STXImageCache *_stx_defaultImageCache = nil; 57 | static dispatch_once_t oncePredicate; 58 | dispatch_once(&oncePredicate, ^{ 59 | _stx_defaultImageCache = [[STXImageCache alloc] init]; 60 | 61 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) { 62 | [_stx_defaultImageCache removeAllObjects]; 63 | }]; 64 | }); 65 | 66 | #pragma clang diagnostic push 67 | #pragma clang diagnostic ignored "-Wgnu" 68 | return objc_getAssociatedObject(self, &kSTXSharedImageCacheKey) ?: _stx_defaultImageCache; 69 | #pragma clang diagnostic pop 70 | } 71 | 72 | + (void)setSharedImageCache:(id)imageCache { 73 | objc_setAssociatedObject(self, &kSTXSharedImageCacheKey, imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 74 | } 75 | 76 | - (void)setCircleImageWithURL:(NSURL *)imageURL placeholderImage:(UIImage *)placeholderImage 77 | { 78 | [self setCircleImageWithURL:imageURL placeholderImage:placeholderImage borderWidth:3]; 79 | } 80 | 81 | - (void)setCircleImageWithURL:(NSURL *)imageURL placeholderImage:(UIImage *)placeholderImage borderWidth:(CGFloat)borderWidth; 82 | { 83 | [self cancelCircleImageOperation]; 84 | 85 | [self addCircleMask]; 86 | 87 | CGSize imageSize = CGSizeMake(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)); 88 | 89 | __weak UIImageView *weakSelf = self; 90 | 91 | NSURLRequest *request = [NSURLRequest requestWithURL:imageURL]; 92 | UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:request]; 93 | if (cachedImage) { 94 | self.image = cachedImage; 95 | } else { 96 | __block NSOperationQueue *circleImageOperationQueue = [[self class] stx_sharedCircleImageOperationQueue]; 97 | id imageCache = [[self class] sharedImageCache]; 98 | 99 | [self setImageWithURLRequest:request placeholderImage:placeholderImage success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { 100 | __strong __typeof(weakSelf)strongSelf = weakSelf; 101 | 102 | strongSelf.stx_circleImageOperation = [NSBlockOperation blockOperationWithBlock:^{ 103 | UIImage *circledImage = [image circleBorderedAtWidth:borderWidth forImageWithSize:imageSize]; 104 | [imageCache cacheImage:circledImage forRequest:request]; 105 | 106 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 107 | strongSelf.image = circledImage; 108 | }]; 109 | }]; 110 | 111 | [circleImageOperationQueue addOperation:strongSelf.stx_circleImageOperation]; 112 | 113 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { 114 | 115 | __strong __typeof(weakSelf)strongSelf = weakSelf; 116 | 117 | if (strongSelf.image != nil) { 118 | return; 119 | } 120 | 121 | strongSelf.stx_circleImageOperation = [NSBlockOperation blockOperationWithBlock:^{ 122 | UIImage *circledImage = [placeholderImage circleBorderedAtWidth:borderWidth forImageWithSize:imageSize]; 123 | 124 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 125 | strongSelf.image = circledImage; 126 | }]; 127 | }]; 128 | 129 | [circleImageOperationQueue addOperation:strongSelf.stx_circleImageOperation]; 130 | }]; 131 | } 132 | } 133 | 134 | - (void)addCircleMask 135 | { 136 | self.clipsToBounds = YES; 137 | 138 | self.backgroundColor = [UIColor whiteColor]; 139 | self.contentMode = UIViewContentModeScaleAspectFill; 140 | self.layer.cornerRadius = CGRectGetWidth(self.bounds)/2; 141 | self.layer.masksToBounds = YES; 142 | } 143 | 144 | - (void)cancelCircleImageOperation 145 | { 146 | [self.stx_circleImageOperation cancel]; 147 | self.stx_circleImageOperation = nil; 148 | } 149 | 150 | @end 151 | 152 | #pragma mark - STXImageCache 153 | 154 | @implementation STXImageCache 155 | 156 | static inline NSString * STXImageCacheKeyFromURLRequest(NSURLRequest *request) { 157 | return [[request URL] absoluteString]; 158 | } 159 | 160 | - (UIImage *)cachedImageForRequest:(NSURLRequest *)request { 161 | switch ([request cachePolicy]) { 162 | case NSURLRequestReloadIgnoringCacheData: 163 | case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: 164 | return nil; 165 | default: 166 | break; 167 | } 168 | 169 | return [self objectForKey:STXImageCacheKeyFromURLRequest(request)]; 170 | } 171 | 172 | - (void)cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request 173 | { 174 | if (image && request) { 175 | [self setObject:image forKey:STXImageCacheKeyFromURLRequest(request)]; 176 | } 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIViewController+Indicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Indicator.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @interface UIViewController (Indicator) 10 | 11 | /** 12 | * Return a new UIActivityIndicatorView at the middle of the UIViewController's view 13 | * It will be positioned on top of contentView if provided, if not just add it to self.view. 14 | */ 15 | 16 | - (UIActivityIndicatorView *)activityIndicatorViewOnView:(UIView *)contentView; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIViewController+Indicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Indicator.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 28/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+Indicator.h" 10 | 11 | #import 12 | 13 | @implementation UIViewController (Indicator) 14 | 15 | - (UIActivityIndicatorView *)activityIndicatorViewOnView:(UIView *)contentView 16 | { 17 | UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 18 | activityIndicatorView.translatesAutoresizingMaskIntoConstraints = NO; 19 | activityIndicatorView.hidesWhenStopped = YES; 20 | 21 | if (contentView) 22 | [self.view insertSubview:activityIndicatorView aboveSubview:contentView]; 23 | else 24 | [self.view addSubview:activityIndicatorView]; 25 | 26 | [activityIndicatorView autoCenterInSuperview]; 27 | return activityIndicatorView; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIViewController+Sharing.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXViewController+Sharing.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 18/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @interface UIViewController (Sharing) 10 | 11 | - (void)shareImage:(UIImage *)image text:(NSString *)text url:(NSURL *)url; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STXDynamicTableView/Categories/UIViewController+Sharing.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXViewController+Sharing.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 18/2/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+Sharing.h" 10 | 11 | @implementation UIViewController (Sharing) 12 | 13 | - (void)shareImage:(UIImage *)image text:(NSString *)text url:(NSURL *)url 14 | { 15 | NSMutableArray *activityItems = [NSMutableArray array]; 16 | if (image) 17 | [activityItems addObject:image]; 18 | if (text) 19 | [activityItems addObject:text]; 20 | if (url) 21 | [activityItems addObject:url]; 22 | 23 | UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil]; 24 | [self presentViewController:activityViewController animated:YES completion:nil]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXCaptionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXCaptionCell.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @class STXCaptionCell; 12 | 13 | @protocol STXCaptionCellDelegate 14 | 15 | @optional 16 | 17 | - (void)captionCell:(STXCaptionCell *)captionCell didSelectHashtag:(NSString *)hashtag; 18 | - (void)captionCell:(STXCaptionCell *)captionCell didSelectMention:(NSString *)mention; 19 | - (void)captionCell:(STXCaptionCell *)captionCell didSelectPoster:(id)userItem; 20 | 21 | @end 22 | 23 | @interface STXCaptionCell : UITableViewCell 24 | 25 | @property (copy, nonatomic) NSDictionary *caption; 26 | 27 | @property (weak, nonatomic) id delegate; 28 | 29 | - (id)initWithCaption:(NSDictionary *)caption reuseIdentifier:(NSString *)reuseIdentifier; 30 | 31 | @end -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXCaptionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXCaptionCell.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXCaptionCell.h" 10 | #import "STXAttributedLabel.h" 11 | 12 | static CGFloat STXCaptionViewLeadingEdgeInset = 10.f; 13 | static CGFloat STXCaptionViewTrailingEdgeInset = 10.f; 14 | 15 | static NSString *HashTagAndMentionRegex = @"(#|@)(\\w+)"; 16 | 17 | @interface STXCaptionCell () 18 | 19 | @property (strong, nonatomic) STXAttributedLabel *captionLabel; 20 | @property (nonatomic) BOOL didSetupConstraints; 21 | 22 | @end 23 | 24 | @implementation STXCaptionCell 25 | 26 | - (id)initWithCaption:(NSDictionary *)caption reuseIdentifier:(NSString *)reuseIdentifier 27 | { 28 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; 29 | if (self) { 30 | NSString *text = [caption valueForKey:@"text"]; 31 | _captionLabel = [self captionLabelWithText:text]; 32 | 33 | [self.contentView addSubview:_captionLabel]; 34 | _captionLabel.translatesAutoresizingMaskIntoConstraints = NO; 35 | 36 | self.selectionStyle = UITableViewCellSelectionStyleNone; 37 | } 38 | 39 | return self; 40 | } 41 | 42 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 43 | { 44 | [super setSelected:selected animated:animated]; 45 | } 46 | 47 | - (void)layoutSubviews 48 | { 49 | [super layoutSubviews]; 50 | 51 | self.captionLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.frame) - (STXCaptionViewLeadingEdgeInset + STXCaptionViewTrailingEdgeInset); 52 | } 53 | 54 | - (void)updateConstraints 55 | { 56 | if (!self.didSetupConstraints) { 57 | [self.captionLabel autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, STXCaptionViewLeadingEdgeInset, 0, STXCaptionViewTrailingEdgeInset)]; 58 | 59 | self.didSetupConstraints = YES; 60 | } 61 | 62 | [super updateConstraints]; 63 | } 64 | 65 | - (void)setCaption:(NSDictionary *)caption 66 | { 67 | if (_caption != caption) { 68 | _caption = caption; 69 | 70 | NSString *text = [caption valueForKey:@"text"]; 71 | [self setCaptionLabel:self.captionLabel text:text]; 72 | } 73 | } 74 | 75 | #pragma mark - Attributed Label 76 | 77 | - (void)setCaptionLabel:(STXAttributedLabel *)captionLabel text:(NSString *)text 78 | { 79 | NSString *trimmedText = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 80 | 81 | NSMutableArray *textCheckingResults = [NSMutableArray array]; 82 | [captionLabel setText:trimmedText afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 83 | NSError *error; 84 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:HashTagAndMentionRegex 85 | options:NSRegularExpressionCaseInsensitive 86 | error:&error]; 87 | if (error) { 88 | UALog(@"%@", error); 89 | } 90 | 91 | [regex enumerateMatchesInString:[mutableAttributedString string] options:0 range:NSMakeRange(0, [mutableAttributedString length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 92 | [textCheckingResults addObject:result]; 93 | }]; 94 | 95 | return mutableAttributedString; 96 | }]; 97 | 98 | for (NSTextCheckingResult *result in textCheckingResults) { 99 | [captionLabel addLinkWithTextCheckingResult:result]; 100 | } 101 | } 102 | 103 | - (STXAttributedLabel *)captionLabelWithText:(NSString *)text 104 | { 105 | STXAttributedLabel *captionLabel = [[STXAttributedLabel alloc] initForParagraphStyleWithText:text]; 106 | captionLabel.textColor = [UIColor blackColor]; 107 | captionLabel.lineBreakMode = NSLineBreakByWordWrapping; 108 | captionLabel.numberOfLines = 0; 109 | captionLabel.delegate = self; 110 | captionLabel.userInteractionEnabled = YES; 111 | 112 | captionLabel.linkAttributes = @{ (NSString *)kCTForegroundColorAttributeName: [UIColor blueColor], 113 | (NSString *)kCTFontAttributeName: [UIFont boldSystemFontOfSize:14], 114 | (NSString *)kCTUnderlineStyleAttributeName : @(kCTUnderlineStyleNone) }; 115 | captionLabel.activeLinkAttributes = captionLabel.linkAttributes; 116 | captionLabel.inactiveLinkAttributes = captionLabel.linkAttributes; 117 | [self setCaptionLabel:captionLabel text:text]; 118 | 119 | return captionLabel; 120 | } 121 | 122 | - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result 123 | { 124 | NSString *hashtagOrMention = [[label.attributedText string] substringWithRange:result.range]; 125 | UALog(@"%@", hashtagOrMention); 126 | 127 | if ([hashtagOrMention hasPrefix:@"#"]) { 128 | NSString *hashtag = [hashtagOrMention substringFromIndex:1]; 129 | 130 | if ([self.delegate respondsToSelector:@selector(captionCell:didSelectHashtag:)]) { 131 | [self.delegate captionCell:self didSelectHashtag:hashtag]; 132 | } 133 | } else if ([hashtagOrMention hasPrefix:@"@"]) { 134 | NSString *mention = [hashtagOrMention substringFromIndex:1]; 135 | 136 | if ([self.delegate respondsToSelector:@selector(captionCell:didSelectMention:)]) { 137 | [self.delegate captionCell:self didSelectMention:mention]; 138 | } 139 | } 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXCommentCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXCommentCell.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | typedef NS_ENUM(int16_t, STXCommentCellStyle) { 12 | STXCommentCellStyleSingleComment, 13 | STXCommentCellStyleShowAllComments 14 | }; 15 | 16 | @class STXCommentCell; 17 | 18 | @protocol STXCommentCellDelegate 19 | 20 | @optional 21 | 22 | - (void)commentCellWillShowAllComments:(STXCommentCell *)commentCell; 23 | - (void)commentCell:(STXCommentCell *)commentCell willShowCommenter:(id)commenter; 24 | - (void)commentCell:(STXCommentCell *)commentCell didSelectURL:(NSURL *)url; 25 | - (void)commentCell:(STXCommentCell *)commentCell didSelectHashtag:(NSString *)hashtag; 26 | - (void)commentCell:(STXCommentCell *)commentCell didSelectMention:(NSString *)mention; 27 | 28 | @end 29 | 30 | @interface STXCommentCell : UITableViewCell 31 | 32 | @property (copy, nonatomic) id comment; 33 | @property (nonatomic) NSInteger totalComments; 34 | 35 | @property (weak, nonatomic) id delegate; 36 | 37 | - (instancetype)initWithStyle:(STXCommentCellStyle)style comment:(id)comment reuseIdentifier:(NSString *)reuseIdentifier; 38 | - (instancetype)initWithStyle:(STXCommentCellStyle)style totalComments:(NSInteger)totalComments reuseIdentifier:(NSString *)reuseIdentifier; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXCommentCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXCommentCell.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXCommentCell.h" 10 | #import "STXAttributedLabel.h" 11 | 12 | static CGFloat STXCommentViewLeadingEdgeInset = 10.f; 13 | static CGFloat STXCommentViewTrailingEdgeInset = 10.f; 14 | 15 | static NSString *HashTagAndMentionRegex = @"(#|@)(\\w+)"; 16 | 17 | @interface STXCommentCell () 18 | 19 | @property (nonatomic) STXCommentCellStyle cellStyle; 20 | @property (strong, nonatomic) STXAttributedLabel *commentLabel; 21 | 22 | @property (nonatomic) BOOL didSetupConstraints; 23 | 24 | @end 25 | 26 | @implementation STXCommentCell 27 | 28 | - (id)initWithStyle:(STXCommentCellStyle)style comment:(id)comment totalComments:(NSInteger)totalComments reuseIdentifier:(NSString *)reuseIdentifier 29 | { 30 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; 31 | if (self) { 32 | _cellStyle = style; 33 | 34 | if (style == STXCommentCellStyleShowAllComments) { 35 | NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Show %d comments", nil), totalComments]; 36 | _commentLabel = [self allCommentsLabelWithTitle:title]; 37 | } else { 38 | id commenter = [comment from]; 39 | _commentLabel = [self commentLabelWithText:[comment text] commenter:[commenter username]]; 40 | } 41 | 42 | [self.contentView addSubview:_commentLabel]; 43 | _commentLabel.translatesAutoresizingMaskIntoConstraints = NO; 44 | 45 | self.selectionStyle = UITableViewCellSelectionStyleNone; 46 | } 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithStyle:(STXCommentCellStyle)style comment:(id)comment reuseIdentifier:(NSString *)reuseIdentifier 51 | { 52 | return [self initWithStyle:style comment:comment totalComments:0 reuseIdentifier:reuseIdentifier]; 53 | } 54 | 55 | - (instancetype)initWithStyle:(STXCommentCellStyle)style totalComments:(NSInteger)totalComments reuseIdentifier:(NSString *)reuseIdentifier 56 | { 57 | return [self initWithStyle:style comment:nil totalComments:totalComments reuseIdentifier:reuseIdentifier]; 58 | } 59 | 60 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 61 | { 62 | [super setSelected:selected animated:animated]; 63 | } 64 | 65 | - (void)layoutSubviews 66 | { 67 | [super layoutSubviews]; 68 | 69 | self.commentLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.frame) - (STXCommentViewLeadingEdgeInset + STXCommentViewTrailingEdgeInset); 70 | } 71 | 72 | - (void)updateConstraints 73 | { 74 | if (!self.didSetupConstraints) { 75 | [self.commentLabel autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, STXCommentViewLeadingEdgeInset, 0, STXCommentViewTrailingEdgeInset)]; 76 | 77 | self.didSetupConstraints = YES; 78 | } 79 | 80 | [super updateConstraints]; 81 | } 82 | 83 | - (void)setComment:(id)comment 84 | { 85 | if (_comment != comment) { 86 | _comment = comment; 87 | 88 | id commenter = [comment from]; 89 | [self setCommentLabel:self.commentLabel text:[comment text] commenter:[commenter username]]; 90 | } 91 | } 92 | 93 | - (void)setTotalComments:(NSInteger)totalComments 94 | { 95 | if (_totalComments != totalComments) { 96 | NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Show %d comments", nil), totalComments]; 97 | [self setAllCommentsLabel:self.commentLabel title:title]; 98 | } 99 | } 100 | 101 | #pragma mark - Attributed Label 102 | 103 | - (void)setAllCommentsLabel:(STXAttributedLabel *)commentLabel title:(NSString *)title 104 | { 105 | [commentLabel setText:title]; 106 | } 107 | 108 | - (void)setCommentLabel:(STXAttributedLabel *)commentLabel text:(NSString *)text commenter:(NSString *)commenter 109 | { 110 | NSString *commentText = [NSString stringWithFormat:@"%@ %@", commenter, text]; 111 | 112 | NSMutableArray *textCheckingResults = [NSMutableArray array]; 113 | [commentLabel setText:commentText afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 114 | NSRange searchRange = NSMakeRange(0, [mutableAttributedString length]); 115 | 116 | NSRange currentRange = [[mutableAttributedString string] rangeOfString:commenter options:NSLiteralSearch range:searchRange]; 117 | NSTextCheckingResult *textCheckingResult = [NSTextCheckingResult linkCheckingResultWithRange:currentRange URL:nil]; 118 | [textCheckingResults addObject:textCheckingResult]; 119 | 120 | NSError *error; 121 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:HashTagAndMentionRegex 122 | options:NSRegularExpressionCaseInsensitive 123 | error:&error]; 124 | if (error) { 125 | UALog(@"%@", error); 126 | } 127 | 128 | [regex enumerateMatchesInString:[mutableAttributedString string] options:0 range:NSMakeRange(0, [mutableAttributedString length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 129 | [textCheckingResults addObject:result]; 130 | }]; 131 | 132 | return mutableAttributedString; 133 | }]; 134 | 135 | for (NSTextCheckingResult *result in textCheckingResults) { 136 | [commentLabel addLinkWithTextCheckingResult:result]; 137 | } 138 | } 139 | 140 | - (STXAttributedLabel *)allCommentsLabelWithTitle:(NSString *)title 141 | { 142 | STXAttributedLabel *allCommentsLabel = [STXAttributedLabel newAutoLayoutView]; 143 | allCommentsLabel.delegate = self; 144 | allCommentsLabel.textColor = [UIColor lightGrayColor]; 145 | allCommentsLabel.enabledTextCheckingTypes = NSTextCheckingTypeLink; 146 | 147 | allCommentsLabel.linkAttributes = @{ (NSString *)kCTFontAttributeName: allCommentsLabel.font, 148 | (NSString *)kCTForegroundColorAttributeName: allCommentsLabel.textColor}; 149 | allCommentsLabel.activeLinkAttributes = allCommentsLabel.linkAttributes; 150 | allCommentsLabel.inactiveLinkAttributes = allCommentsLabel.linkAttributes; 151 | [allCommentsLabel setText:title]; 152 | 153 | NSTextCheckingResult *textCheckingResult = [NSTextCheckingResult linkCheckingResultWithRange:NSMakeRange(0, [title length]) URL:nil]; 154 | [allCommentsLabel addLinkWithTextCheckingResult:textCheckingResult]; 155 | 156 | return allCommentsLabel; 157 | } 158 | 159 | - (STXAttributedLabel *)commentLabelWithText:(NSString *)text commenter:(NSString *)commenter 160 | { 161 | NSString *trimmedText = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 162 | NSString *commentText = [[commenter stringByAppendingString:@" "] stringByAppendingString:trimmedText]; 163 | 164 | STXAttributedLabel *commentLabel = [[STXAttributedLabel alloc] initForParagraphStyleWithText:commentText]; 165 | commentLabel.delegate = self; 166 | commentLabel.numberOfLines = 0; 167 | commentLabel.lineBreakMode = NSLineBreakByWordWrapping; 168 | 169 | commentLabel.linkAttributes = @{ (NSString *)kCTForegroundColorAttributeName: [UIColor blueColor] }; 170 | commentLabel.activeLinkAttributes = commentLabel.linkAttributes; 171 | commentLabel.inactiveLinkAttributes = commentLabel.linkAttributes; 172 | 173 | [self setCommentLabel:commentLabel text:text commenter:commenter]; 174 | 175 | return commentLabel; 176 | } 177 | 178 | - (void)showAllComments:(id)sender 179 | { 180 | if ([self.delegate respondsToSelector:@selector(commentCellWillShowAllComments:)]) 181 | [self.delegate commentCellWillShowAllComments:self]; 182 | } 183 | 184 | #pragma mark - Attributed Label 185 | 186 | - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result 187 | { 188 | NSString *selectedText = [[label.attributedText string] substringWithRange:result.range]; 189 | UALog(@"%@", selectedText); 190 | 191 | if ([selectedText hasPrefix:@"http://"] || [selectedText hasPrefix:@"https://"]) { 192 | NSURL *selectedURL = [NSURL URLWithString:selectedText]; 193 | 194 | if (selectedURL) { 195 | if ([self.delegate respondsToSelector:@selector(commentCell:didSelectURL:)]) { 196 | [self.delegate commentCell:self didSelectURL:selectedURL]; 197 | } 198 | return; 199 | } 200 | } else if ([selectedText hasPrefix:@"#"]) { 201 | NSString *hashtag = [selectedText substringFromIndex:1]; 202 | 203 | if ([self.delegate respondsToSelector:@selector(commentCell:didSelectHashtag:)]) { 204 | [self.delegate commentCell:self didSelectHashtag:hashtag]; 205 | } 206 | } else if ([selectedText hasPrefix:@"@"]) { 207 | NSString *mention = [selectedText substringFromIndex:1]; 208 | 209 | if ([self.delegate respondsToSelector:@selector(commentCell:didSelectMention:)]) { 210 | [self.delegate commentCell:self didSelectMention:mention]; 211 | } 212 | } 213 | 214 | if (self.cellStyle == STXCommentCellStyleShowAllComments) { 215 | [self showAllComments:label]; 216 | } else { 217 | if ([self.delegate respondsToSelector:@selector(commentCell:willShowCommenter:)]) { 218 | [self.delegate commentCell:self willShowCommenter:[self.comment from]]; 219 | } 220 | } 221 | } 222 | 223 | @end 224 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXFeedPhotoCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedPhotoCell.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Triệu Khang on 24/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @protocol STXFeedPhotoCellDelegate 12 | 13 | @optional 14 | 15 | - (void)feedCellWillShowPoster:(id )poster; 16 | 17 | @end 18 | 19 | @interface STXFeedPhotoCell : UITableViewCell 20 | 21 | @property (strong, nonatomic) NSIndexPath *indexPath; 22 | 23 | @property (strong, nonatomic) id postItem; 24 | @property (strong, nonatomic) UIImage *photoImage; 25 | 26 | @property (weak, nonatomic) id delegate; 27 | 28 | - (void)cancelImageLoading; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXFeedPhotoCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedPhotoCell.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Triệu Khang on 24/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXFeedPhotoCell.h" 10 | 11 | #import "UIImageView+Masking.h" 12 | 13 | @interface STXFeedPhotoCell () 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *profileLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *dateLabel; 17 | 18 | @property (weak, nonatomic) IBOutlet UIImageView *profileImageView; 19 | @property (weak, nonatomic) IBOutlet UIImageView *postImageView; 20 | 21 | @end 22 | 23 | @implementation STXFeedPhotoCell 24 | 25 | - (void)awakeFromNib 26 | { 27 | [super awakeFromNib]; 28 | 29 | self.profileImageView.userInteractionEnabled = YES; 30 | 31 | UITapGestureRecognizer *imageGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(profileTapped:)]; 32 | [self.profileImageView addGestureRecognizer:imageGestureRecognizer]; 33 | 34 | self.profileLabel.userInteractionEnabled = YES; 35 | 36 | UITapGestureRecognizer *labelGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(profileTapped:)]; 37 | [self.profileLabel addGestureRecognizer:labelGestureRecognizer]; 38 | 39 | self.dateLabel.backgroundColor = [self.dateLabel superview].backgroundColor; 40 | 41 | self.profileImageView.clipsToBounds = YES; 42 | 43 | self.postImageView.clipsToBounds = YES; 44 | } 45 | 46 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 47 | { 48 | [super setSelected:selected animated:animated]; 49 | } 50 | 51 | - (void)setPostItem:(id)postItem 52 | { 53 | if (_postItem != postItem) { 54 | _postItem = postItem; 55 | 56 | self.dateLabel.textColor = [UIColor grayColor]; 57 | self.dateLabel.text = [MHPrettyDate prettyDateFromDate:postItem.postDate withFormat:MHPrettyDateLongRelativeTime]; 58 | 59 | id userItem = [postItem user]; 60 | NSString *name = [userItem fullname]; 61 | self.profileLabel.text = name; 62 | NSURL *profilePhotoURL = [userItem profilePictureURL]; 63 | 64 | [self.profileImageView setCircleImageWithURL:profilePhotoURL placeholderImage:[UIImage imageNamed:@"ProfilePlaceholder"] borderWidth:2]; 65 | 66 | [self.postImageView setImageWithURL:postItem.photoURL]; 67 | } 68 | } 69 | 70 | - (UIImage *)photoImage 71 | { 72 | _photoImage = self.postImageView.image; 73 | return _photoImage; 74 | } 75 | 76 | - (void)cancelImageLoading 77 | { 78 | [self.profileImageView cancelImageRequestOperation]; 79 | [self.profileImageView setCircleImageWithURL:nil placeholderImage:[UIImage imageNamed:@"ProfilePlaceholder"]]; 80 | 81 | [self.postImageView cancelImageRequestOperation]; 82 | self.postImageView.image = nil; 83 | } 84 | 85 | #pragma mark - Actions 86 | 87 | - (void)profileTapped:(UIGestureRecognizer *)recognizer 88 | { 89 | if ([self.delegate respondsToSelector:@selector(feedCellWillShowPoster:)]) 90 | [self.delegate feedCellWillShowPoster:[self.postItem user]]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXFeedPhotoCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXLikesCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXLikesCell.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | typedef NS_ENUM(NSUInteger, STXLikesCellStyle) { 12 | STXLikesCellStyleLikesCount, 13 | STXLikesCellStyleLikers 14 | }; 15 | 16 | @class STXLikesCell; 17 | 18 | @protocol STXLikesCellDelegate 19 | 20 | @optional 21 | 22 | - (void)likesCellWillShowLikes:(STXLikesCell *)likesCell; 23 | - (void)likesCellDidSelectLiker:(NSString *)liker; 24 | 25 | @end 26 | 27 | @interface STXLikesCell : UITableViewCell 28 | 29 | @property (copy, nonatomic) NSDictionary *likes; 30 | 31 | @property (weak, nonatomic) id delegate; 32 | 33 | - (instancetype)initWithStyle:(STXLikesCellStyle)style likes:(NSDictionary *)likes reuseIdentifier:(NSString *)reuseIdentifier; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXLikesCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXLikesCell.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXLikesCell.h" 10 | 11 | static CGFloat STXLikesViewLeadingEdgeInset = 10.f; 12 | static CGFloat STXLikesViewTrailingEdgeInset = 10.f; 13 | 14 | @interface STXLikesCell () 15 | 16 | @property (nonatomic) BOOL didSetupConstraints; 17 | 18 | @property (nonatomic) STXLikesCellStyle cellStyle; 19 | @property (strong, nonatomic) TTTAttributedLabel *likesLabel; 20 | 21 | @end 22 | 23 | @implementation STXLikesCell 24 | 25 | - (instancetype)initWithStyle:(STXLikesCellStyle)style likes:(NSDictionary *)likes reuseIdentifier:(NSString *)reuseIdentifier 26 | { 27 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; 28 | if (self) { 29 | _cellStyle = style; 30 | 31 | if (style == STXLikesCellStyleLikesCount) { 32 | NSInteger count = [[likes valueForKey:@"count"] integerValue]; 33 | _likesLabel = [self likesLabelForCount:count]; 34 | } else { 35 | NSArray *likers = [likes valueForKey:@"data"]; 36 | _likesLabel = [self likersLabelForLikers:likers]; 37 | } 38 | 39 | [self.contentView addSubview:_likesLabel]; 40 | _likesLabel.translatesAutoresizingMaskIntoConstraints = NO; 41 | 42 | self.selectionStyle = UITableViewCellSelectionStyleNone; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 48 | { 49 | [super setSelected:selected animated:animated]; 50 | } 51 | 52 | - (void)layoutSubviews 53 | { 54 | [super layoutSubviews]; 55 | 56 | self.likesLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.frame) - (STXLikesViewLeadingEdgeInset + STXLikesViewTrailingEdgeInset); 57 | } 58 | 59 | - (void)updateConstraints 60 | { 61 | if (!self.didSetupConstraints) { 62 | [self.likesLabel autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, STXLikesViewLeadingEdgeInset, 0, STXLikesViewTrailingEdgeInset)]; 63 | 64 | self.didSetupConstraints = YES; 65 | } 66 | 67 | [super updateConstraints]; 68 | } 69 | 70 | - (void)setLikes:(NSDictionary *)likes 71 | { 72 | if (_likes != likes) { 73 | _likes = likes; 74 | 75 | NSInteger count = [[likes valueForKey:@"count"] integerValue]; 76 | if (count > 2) { 77 | [self setLikesLabel:self.likesLabel count:count]; 78 | } else { 79 | NSArray *likers = [likes valueForKey:@"data"]; 80 | [self setLikersLabel:self.likesLabel likers:likers]; 81 | } 82 | } 83 | } 84 | 85 | #pragma mark - Attributed Label 86 | 87 | - (void)setLikesLabel:(TTTAttributedLabel *)likesLabel count:(NSInteger)count 88 | { 89 | NSString *countString = [@(count) stringValue]; 90 | NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%@ loves", nil), countString]; 91 | likesLabel.text = title; 92 | } 93 | 94 | - (void)setLikersLabel:(TTTAttributedLabel *)likersLabel likers:(NSArray *)likers 95 | { 96 | NSMutableString *likersString = [NSMutableString stringWithCapacity:0]; 97 | NSUInteger likerIndex = 0; 98 | NSArray *likerNames = [likers valueForKey:@"name"]; 99 | for (NSString *likerName in likerNames) { 100 | if ([likerName length] > 0) { 101 | if (likerIndex == 0) 102 | [likersString setString:likerName]; 103 | else 104 | [likersString appendFormat:NSLocalizedString(@" and %@", nil), likerName]; 105 | } 106 | 107 | ++likerIndex; 108 | } 109 | 110 | if ([likersString length] > 0) { 111 | if ([likerNames count] == 1) { 112 | [likersString appendString:NSLocalizedString(@" loves this", nil)]; 113 | } else { 114 | [likersString appendString:NSLocalizedString(@" love this", nil)]; 115 | } 116 | } 117 | 118 | NSMutableArray *textCheckingResults = [NSMutableArray array]; 119 | 120 | NSString *likersText = [likersString copy]; 121 | 122 | [likersLabel setText:likersText afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 123 | 124 | NSRange searchRange = NSMakeRange(0, [mutableAttributedString length]); 125 | for (NSString *likerName in likerNames) { 126 | NSRange currentRange = [[mutableAttributedString string] rangeOfString:likerName options:NSLiteralSearch range:searchRange]; 127 | 128 | NSTextCheckingResult *result = [NSTextCheckingResult linkCheckingResultWithRange:currentRange URL:nil]; 129 | [textCheckingResults addObject:result]; 130 | 131 | searchRange = NSMakeRange(currentRange.length, [mutableAttributedString length] - currentRange.length); 132 | } 133 | 134 | return mutableAttributedString; 135 | }]; 136 | 137 | 138 | for (NSTextCheckingResult *result in textCheckingResults) 139 | [likersLabel addLinkWithTextCheckingResult:result]; 140 | } 141 | 142 | - (TTTAttributedLabel *)likesLabelForCount:(NSInteger)count 143 | { 144 | TTTAttributedLabel *likesLabel = [TTTAttributedLabel newAutoLayoutView]; 145 | likesLabel.textColor = [UIColor blueColor]; 146 | likesLabel.delegate = self; 147 | 148 | [self setLikesLabel:likesLabel count:count]; 149 | 150 | UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showLikers:)]; 151 | [likesLabel addGestureRecognizer:recognizer]; 152 | 153 | return likesLabel; 154 | } 155 | 156 | - (TTTAttributedLabel *)likersLabelForLikers:(NSArray *)likers 157 | { 158 | TTTAttributedLabel *likersLabel = [TTTAttributedLabel newAutoLayoutView]; 159 | likersLabel.enabledTextCheckingTypes = NSTextCheckingTypeLink; 160 | likersLabel.numberOfLines = 0; 161 | likersLabel.lineBreakMode = NSLineBreakByWordWrapping; 162 | likersLabel.delegate = self; 163 | 164 | likersLabel.linkAttributes = @{ (NSString *)kCTForegroundColorAttributeName: [UIColor blueColor], 165 | (NSString *)kCTUnderlineStyleAttributeName : @(kCTUnderlineStyleNone) }; 166 | likersLabel.activeLinkAttributes = likersLabel.linkAttributes; 167 | likersLabel.inactiveLinkAttributes = likersLabel.linkAttributes; 168 | 169 | [self setLikersLabel:likersLabel likers:likers]; 170 | 171 | return likersLabel; 172 | } 173 | 174 | #pragma mark - Actions 175 | 176 | - (void)showLikers:(id)sender 177 | { 178 | if ([self.delegate respondsToSelector:@selector(likesCellWillShowLikes:)]) 179 | [self.delegate likesCellWillShowLikes:self]; 180 | } 181 | 182 | #pragma mark - Attributed Label 183 | 184 | - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result 185 | { 186 | NSString *string = [[label.attributedText string] substringWithRange:result.range]; 187 | UALog(@"%@", string); 188 | 189 | if ([self.delegate respondsToSelector:@selector(likesCellDidSelectLiker:)]) 190 | [self.delegate likesCellDidSelectLiker:string]; 191 | } 192 | 193 | @end 194 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXUserActionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXUserActionCell.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @class STXUserActionCell; 12 | 13 | @protocol STXUserActionDelegate 14 | 15 | - (void)userWillComment:(STXUserActionCell *)userActionCell; 16 | - (void)userWillShare:(STXUserActionCell *)userActionCell; 17 | 18 | - (void)userDidLike:(STXUserActionCell *)userActionCell; 19 | - (void)userDidUnlike:(STXUserActionCell *)userActionCell; 20 | 21 | @end 22 | 23 | @interface STXUserActionCell : UITableViewCell 24 | 25 | @property (weak, nonatomic) id delegate; 26 | 27 | @property (copy, nonatomic) id postItem; 28 | @property (copy, nonatomic) NSIndexPath *indexPath; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXUserActionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXUserActionCell.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 10/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXUserActionCell.h" 10 | 11 | #import "UIButton+STXButton.h" 12 | 13 | @interface STXUserActionCell () 14 | 15 | @property (weak, nonatomic) IBOutlet UIButton *likeButton; 16 | @property (weak, nonatomic) IBOutlet UIButton *commentButton; 17 | @property (weak, nonatomic) IBOutlet UIButton *shareButton; 18 | @property (weak, nonatomic) IBOutlet UIView *buttonDividerLeft; 19 | @property (weak, nonatomic) IBOutlet UIView *buttonDividerRight; 20 | 21 | @end 22 | 23 | @implementation STXUserActionCell 24 | 25 | - (void)awakeFromNib 26 | { 27 | self.buttonDividerLeft.backgroundColor = [UIColor grayColor]; 28 | self.buttonDividerRight.backgroundColor = [UIColor grayColor]; 29 | 30 | self.selectionStyle = UITableViewCellSelectionStyleNone; 31 | } 32 | 33 | - (void)layoutSubviews 34 | { 35 | [super layoutSubviews]; 36 | 37 | if ([self.postItem liked]) { 38 | [self setButton:self.likeButton toLoved:YES]; 39 | } else { 40 | [self setButton:self.likeButton toLoved:NO]; 41 | } 42 | } 43 | 44 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 45 | { 46 | [super setSelected:selected animated:animated]; 47 | } 48 | 49 | - (void)setPostItem:(id)postItem 50 | { 51 | _postItem = postItem; 52 | 53 | NSString *likeButtonTitle = postItem.liked ? NSLocalizedString(@"Loved", nil) : NSLocalizedString(@"Love", nil); 54 | [self.likeButton setTitle:likeButtonTitle forState:UIControlStateNormal]; 55 | 56 | if (postItem.liked) { 57 | [self.likeButton stx_setSelectedAppearance]; 58 | } else { 59 | [self.likeButton stx_setNormalAppearance]; 60 | } 61 | } 62 | 63 | #pragma mark - Action 64 | 65 | - (void)setButton:(UIButton *)button toLoved:(BOOL)loved 66 | { 67 | if (loved) { 68 | [button setTitle:NSLocalizedString(@"Loved", nil) forState:UIControlStateNormal]; 69 | [button stx_setSelectedAppearance]; 70 | } else { 71 | [button setTitle:NSLocalizedString(@"Love", nil) forState:UIControlStateNormal]; 72 | [button stx_setNormalAppearance]; 73 | } 74 | } 75 | 76 | - (IBAction)like:(id)sender 77 | { 78 | // Need to return here if it's disabled, or the buttons may do a delayed 79 | // update. 80 | if (![sender isUserInteractionEnabled]) { 81 | return; 82 | } 83 | 84 | // Prevent rapid interaction 85 | [sender setUserInteractionEnabled:NO]; 86 | 87 | if (![self.postItem liked]) { 88 | [self setButton:sender toLoved:YES]; 89 | 90 | if ([self.delegate respondsToSelector:@selector(userDidLike:)]) { 91 | [self.delegate userDidLike:self]; 92 | } 93 | } else { 94 | [self setButton:sender toLoved:NO]; 95 | 96 | if ([self.delegate respondsToSelector:@selector(userDidUnlike:)]) { 97 | [self.delegate userDidUnlike:self]; 98 | } 99 | } 100 | } 101 | 102 | - (IBAction)comment:(id)sender 103 | { 104 | if ([self.delegate respondsToSelector:@selector(userWillComment:)]) { 105 | [self.delegate userWillComment:self]; 106 | } 107 | } 108 | 109 | - (IBAction)share:(id)sender 110 | { 111 | if ([self.delegate respondsToSelector:@selector(userWillShare:)]) { 112 | [self.delegate userWillShare:self]; 113 | } 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /STXDynamicTableView/Cells/STXUserActionCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /STXDynamicTableView/Items/STXCommentItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXCommentItem.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 3/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @protocol STXUserItem; 12 | 13 | @protocol STXCommentItem 14 | 15 | - (id)from; 16 | 17 | - (NSString *)commentID; 18 | - (NSString *)text; 19 | - (NSDate *)postDate; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /STXDynamicTableView/Items/STXPostItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXPostItem.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 3/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @protocol STXUserItem; 12 | 13 | @protocol STXPostItem 14 | 15 | - (NSString *)postID; 16 | - (NSDate *)postDate; 17 | - (NSURL *)sharedURL; 18 | - (NSURL *)photoURL; 19 | - (NSString *)captionText; 20 | - (NSDictionary *)caption; 21 | - (NSDictionary *)likes; 22 | - (NSArray *)comments; 23 | 24 | - (BOOL)liked; 25 | 26 | - (NSInteger)totalLikes; 27 | - (NSInteger)totalComments; 28 | 29 | - (id)user; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /STXDynamicTableView/Items/STXUserItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXUserItem.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 3/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @protocol STXUserItem 12 | 13 | - (NSString *)userID; 14 | - (NSString *)username; 15 | - (NSString *)fullname; 16 | - (NSURL *)profilePictureURL; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /STXDynamicTableView/Labels/STXAttributedLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXAttributedLabel.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 19/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "TTTAttributedLabel.h" 10 | 11 | @interface STXAttributedLabel : TTTAttributedLabel 12 | 13 | - (instancetype)initForParagraphStyleWithText:(NSString *)text; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STXDynamicTableView/Labels/STXAttributedLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXAttributedLabel.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 19/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXAttributedLabel.h" 10 | #import "NSString+Emoji.h" 11 | 12 | static CGFloat STXAttributedLabelHeightPadding = 4.f; 13 | 14 | @implementation STXAttributedLabel 15 | 16 | - (instancetype)initForParagraphStyleWithText:(NSString *)text 17 | { 18 | self = [super init]; 19 | if (self) { 20 | if ([text stringContainsEmoji]) { 21 | UIFont *commentFont = self.font; 22 | UIFont *emojiFont = [UIFont fontWithName:@"AppleColorEmoji" size:commentFont.pointSize]; 23 | if (emojiFont.lineHeight > commentFont.lineHeight) { 24 | self.minimumLineHeight = emojiFont.lineHeight; 25 | self.lineHeightMultiple = emojiFont.lineHeight / commentFont.lineHeight; 26 | } 27 | } 28 | } 29 | 30 | return self; 31 | } 32 | 33 | - (CGSize)intrinsicContentSize 34 | { 35 | CGSize intrinsicContentSize = [super intrinsicContentSize]; 36 | return CGSizeMake(intrinsicContentSize.width, intrinsicContentSize.height + STXAttributedLabelHeightPadding); 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /STXDynamicTableView/Labels/STXLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXLabel.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Hoang Ta on 3/19/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface STXLabel : UILabel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STXDynamicTableView/Labels/STXLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXLabel.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Hoang Ta on 3/19/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXLabel.h" 10 | 11 | #import "NSString+Emoji.h" 12 | 13 | @implementation STXLabel 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame 16 | { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | [self stx_setupAppearance]; 20 | } 21 | return self; 22 | } 23 | 24 | - (instancetype)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | [self stx_setupAppearance]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)awakeFromNib 34 | { 35 | [super awakeFromNib]; 36 | 37 | [self stx_setupAppearance]; 38 | } 39 | 40 | - (void)stx_setupAppearance 41 | { 42 | } 43 | 44 | - (CGSize)intrinsicContentSize 45 | { 46 | CGSize intrinsicContentSize = [super intrinsicContentSize]; 47 | if ([self.text stringContainsEmoji]) { 48 | UIFont *emojiFont = [UIFont fontWithName:@"AppleColorEmoji" size:self.font.pointSize]; 49 | intrinsicContentSize.height = emojiFont.lineHeight; 50 | } 51 | 52 | return intrinsicContentSize; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /STXDynamicTableView/Protocols/STXFeedTableViewDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedTableViewDataSource.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | @import UIKit; 11 | 12 | @protocol STXFeedPhotoCellDelegate; 13 | @protocol STXLikesCellDelegate; 14 | @protocol STXCaptionCellDelegate; 15 | @protocol STXCommentCellDelegate; 16 | @protocol STXUserActionDelegate; 17 | 18 | @class STXLikesCell; 19 | @class STXCaptionCell; 20 | @class STXCommentCell; 21 | 22 | @interface STXFeedTableViewDataSource : NSObject 23 | 24 | @property (copy, nonatomic) NSArray *posts; 25 | 26 | - (instancetype)initWithController:(id)controller 27 | tableView:(UITableView *)tableView; 28 | 29 | - (STXLikesCell *)likesCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath; 30 | - (STXCaptionCell *)captionCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath; 31 | - (STXCommentCell *)commentCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /STXDynamicTableView/Protocols/STXFeedTableViewDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedTableViewDataSource.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXFeedTableViewDataSource.h" 10 | 11 | #import "STXFeedPhotoCell.h" 12 | #import "STXLikesCell.h" 13 | #import "STXCaptionCell.h" 14 | #import "STXCommentCell.h" 15 | #import "STXUserActionCell.h" 16 | 17 | #define PHOTO_CELL_ROW 0 18 | #define LIKES_CELL_ROW 1 19 | #define CAPTION_CELL_ROW 2 20 | 21 | #define NUMBER_OF_STATIC_ROWS 4 22 | #define MAX_NUMBER_OF_COMMENTS 5 23 | 24 | @interface STXFeedTableViewDataSource () 25 | 26 | @property (weak, nonatomic) id controller; 27 | 28 | @end 29 | 30 | @implementation STXFeedTableViewDataSource 31 | 32 | - (instancetype)initWithController:(id)controller 33 | tableView:(UITableView *)tableView 34 | { 35 | self = [super init]; 36 | if (self) { 37 | _controller = controller; 38 | 39 | NSString *feedPhotoCellIdentifier = NSStringFromClass([STXFeedPhotoCell class]); 40 | UINib *feedPhotoCellNib = [UINib nibWithNibName:feedPhotoCellIdentifier bundle:nil]; 41 | [tableView registerNib:feedPhotoCellNib forCellReuseIdentifier:feedPhotoCellIdentifier]; 42 | 43 | NSString *userActionCellIdentifier = NSStringFromClass([STXUserActionCell class]); 44 | UINib *userActionCellNib = [UINib nibWithNibName:userActionCellIdentifier bundle:nil]; 45 | [tableView registerNib:userActionCellNib forCellReuseIdentifier:userActionCellIdentifier]; 46 | } 47 | 48 | return self; 49 | } 50 | 51 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 52 | { 53 | return [self.posts count]; 54 | } 55 | 56 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 57 | { 58 | id postItem = self.posts[section]; 59 | NSInteger commentsCount = MIN(MAX_NUMBER_OF_COMMENTS, [postItem totalComments]); 60 | return NUMBER_OF_STATIC_ROWS + commentsCount; 61 | } 62 | 63 | - (STXFeedPhotoCell *)photoCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath 64 | { 65 | NSString *CellIdentifier = NSStringFromClass([STXFeedPhotoCell class]); 66 | STXFeedPhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 67 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 68 | 69 | if (cell.indexPath != nil && cell.indexPath.section != indexPath.section) { 70 | [cell cancelImageLoading]; 71 | } 72 | 73 | cell.indexPath = indexPath; 74 | 75 | if (indexPath.section < [self.posts count]) { 76 | id post = self.posts[indexPath.section]; 77 | cell.postItem = post; 78 | cell.delegate = self.controller; 79 | } 80 | 81 | return cell; 82 | } 83 | 84 | - (STXLikesCell *)likesCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath 85 | { 86 | id post = self.posts[indexPath.section]; 87 | STXLikesCell *cell; 88 | 89 | if (cell == nil) { 90 | NSDictionary *likes = [post likes]; 91 | NSInteger count = [[likes valueForKey:@"count"] integerValue]; 92 | if (count > 2) { 93 | static NSString *CellIdentifier = @"STXLikesCountCell"; 94 | cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 95 | if (cell == nil) { 96 | cell = [[STXLikesCell alloc] initWithStyle:STXLikesCellStyleLikesCount likes:likes reuseIdentifier:CellIdentifier]; 97 | } 98 | } else { 99 | static NSString *CellIdentifier = @"STXLikersCell"; 100 | cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 101 | if (cell == nil) { 102 | cell = [[STXLikesCell alloc] initWithStyle:STXLikesCellStyleLikers likes:likes reuseIdentifier:CellIdentifier]; 103 | } 104 | } 105 | 106 | cell.delegate = self.controller; 107 | } 108 | 109 | cell.likes = [post likes]; 110 | 111 | return cell; 112 | } 113 | 114 | - (STXCaptionCell *)captionCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath 115 | { 116 | id post = self.posts[indexPath.section]; 117 | 118 | NSString *CellIdentifier = NSStringFromClass([STXCaptionCell class]); 119 | STXCaptionCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 120 | if (cell == nil) { 121 | cell = [[STXCaptionCell alloc] initWithCaption:[post caption] reuseIdentifier:CellIdentifier]; 122 | cell.delegate = self.controller; 123 | } 124 | 125 | cell.caption = [post caption]; 126 | 127 | return cell; 128 | } 129 | 130 | - (STXCommentCell *)commentCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath 131 | { 132 | id post = self.posts[indexPath.section]; 133 | STXCommentCell *cell; 134 | 135 | if (indexPath.row == 0 && [post totalComments] > MAX_NUMBER_OF_COMMENTS) { 136 | 137 | static NSString *AllCommentsCellIdentifier = @"STXAllCommentsCell"; 138 | cell = [tableView dequeueReusableCellWithIdentifier:AllCommentsCellIdentifier]; 139 | 140 | if (cell == nil) { 141 | cell = [[STXCommentCell alloc] initWithStyle:STXCommentCellStyleShowAllComments 142 | totalComments:[post totalComments] 143 | reuseIdentifier:AllCommentsCellIdentifier]; 144 | } else { 145 | cell.totalComments = [post totalComments]; 146 | } 147 | 148 | } else { 149 | static NSString *CellIdentifier = @"STXSingleCommentCell"; 150 | cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 151 | 152 | NSArray *comments = [post comments]; 153 | id comment = comments[indexPath.row]; 154 | 155 | if (indexPath.row < [comments count]) { 156 | if (cell == nil) { 157 | cell = [[STXCommentCell alloc] initWithStyle:STXCommentCellStyleSingleComment 158 | comment:comment 159 | reuseIdentifier:CellIdentifier]; 160 | } else { 161 | cell.comment = comment; 162 | } 163 | } 164 | } 165 | 166 | cell.delegate = self.controller; 167 | 168 | return cell; 169 | } 170 | 171 | - (STXUserActionCell *)userActionCellForTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath 172 | { 173 | STXUserActionCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([STXUserActionCell class]) forIndexPath:indexPath]; 174 | cell.delegate = self.controller; 175 | return cell; 176 | } 177 | 178 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 179 | { 180 | UITableViewCell *cell; 181 | 182 | NSInteger captionRowOffset = 3; 183 | 184 | id postItem = self.posts[indexPath.section]; 185 | NSInteger commentsCount = MIN(MAX_NUMBER_OF_COMMENTS, [postItem totalComments]); 186 | NSInteger commentsRowLimit = captionRowOffset + commentsCount; 187 | 188 | if (indexPath.row == PHOTO_CELL_ROW) { 189 | cell = [self photoCellForTableView:tableView atIndexPath:indexPath]; 190 | } else if (indexPath.row == LIKES_CELL_ROW) { 191 | cell = [self likesCellForTableView:tableView atIndexPath:indexPath]; 192 | } else if (indexPath.row == CAPTION_CELL_ROW) { 193 | cell = [self captionCellForTableView:tableView atIndexPath:indexPath]; 194 | } else if (indexPath.row > CAPTION_CELL_ROW && indexPath.row < commentsRowLimit) { 195 | NSIndexPath *commentIndexPath = [NSIndexPath indexPathForRow:indexPath.row-captionRowOffset inSection:indexPath.section]; 196 | cell = [self commentCellForTableView:tableView atIndexPath:commentIndexPath]; 197 | } else { 198 | cell = [self userActionCellForTableView:tableView atIndexPath:indexPath]; 199 | } 200 | 201 | [cell setNeedsUpdateConstraints]; 202 | [cell updateConstraintsIfNeeded]; 203 | 204 | return cell; 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /STXDynamicTableView/Protocols/STXFeedTableViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedTableViewDelegate.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | @protocol STXFeedPhotoCellDelegate; 12 | @protocol STXLikesCellDelegate; 13 | @protocol STXCaptionCellDelegate; 14 | @protocol STXCommentCellDelegate; 15 | @protocol STXUserActionDelegate; 16 | 17 | @interface STXFeedTableViewDelegate : NSObject 18 | 19 | @property (nonatomic) BOOL insertingRow; 20 | 21 | - (instancetype)initWithController:(id)controller; 22 | 23 | - (void)reloadAtIndexPath:(NSIndexPath *)indexPath forTableView:(UITableView *)tableView; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /STXDynamicTableView/Protocols/STXFeedTableViewDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedTableViewDelegate.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/3/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXFeedTableViewDelegate.h" 10 | #import "STXFeedTableViewDataSource.h" 11 | 12 | #import "STXFeedPhotoCell.h" 13 | #import "STXLikesCell.h" 14 | #import "STXCaptionCell.h" 15 | #import "STXCommentCell.h" 16 | #import "STXUserActionCell.h" 17 | 18 | #import "STXPostItem.h" 19 | 20 | #define PHOTO_CELL_ROW 0 21 | #define LIKES_CELL_ROW 1 22 | #define CAPTION_CELL_ROW 2 23 | 24 | #define MAX_NUMBER_OF_COMMENTS 5 25 | 26 | static CGFloat const PhotoCellRowHeight = 344; 27 | static CGFloat const UserActionCellHeight = 44; 28 | 29 | @interface STXFeedTableViewDelegate () 30 | 31 | @property (weak, nonatomic) id controller; 32 | 33 | @end 34 | 35 | @implementation STXFeedTableViewDelegate 36 | 37 | - (instancetype)initWithController:(id)controller 38 | { 39 | self = [super init]; 40 | if (self) { 41 | _controller = controller; 42 | } 43 | 44 | return self; 45 | } 46 | 47 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 48 | { 49 | if (indexPath.row == PHOTO_CELL_ROW) { 50 | return PhotoCellRowHeight; 51 | } 52 | 53 | return UITableViewAutomaticDimension; 54 | } 55 | 56 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 57 | { 58 | if (indexPath.row == PHOTO_CELL_ROW) { 59 | return PhotoCellRowHeight; 60 | } 61 | 62 | return UserActionCellHeight; 63 | } 64 | 65 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 66 | { 67 | [tableView deselectRowAtIndexPath:indexPath animated:NO]; 68 | } 69 | 70 | #pragma mark - Row Updates 71 | 72 | - (void)reloadAtIndexPath:(NSIndexPath *)indexPath forTableView:(UITableView *)tableView 73 | { 74 | self.insertingRow = YES; 75 | 76 | [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationNone]; 77 | } 78 | 79 | #pragma mark - Scroll View 80 | 81 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 82 | { 83 | id scrollViewDelegate = (id)self.controller; 84 | if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) { 85 | [scrollViewDelegate scrollViewDidScroll:scrollView]; 86 | } 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /STXDynamicTableView/STXDynamicTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXDynamicTableView.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 3/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXUserItem.h" 10 | #import "STXPostItem.h" 11 | #import "STXCommentItem.h" 12 | 13 | #import "STXFeedTableViewDelegate.h" 14 | #import "STXFeedTableViewDataSource.h" 15 | 16 | #import "UIViewController+Indicator.h" 17 | #import "UIViewController+Sharing.h" 18 | 19 | #import "STXFeedPhotoCell.h" 20 | #import "STXLikesCell.h" 21 | #import "STXCaptionCell.h" 22 | #import "STXCommentCell.h" 23 | #import "STXUserActionCell.h" 24 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 010BA83218EC11D40030AF40 /* STXFeedPhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA82318EC11D40030AF40 /* STXFeedPhotoCell.m */; }; 11 | 010BA83318EC11D40030AF40 /* STXFeedPhotoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 010BA82418EC11D40030AF40 /* STXFeedPhotoCell.xib */; }; 12 | 010BA83618EC11D40030AF40 /* STXAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA82C18EC11D40030AF40 /* STXAttributedLabel.m */; }; 13 | 010BA83718EC11D40030AF40 /* STXLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA82E18EC11D40030AF40 /* STXLabel.m */; }; 14 | 010BA83C18ED10800030AF40 /* NSString+Emoji.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA83B18ED10800030AF40 /* NSString+Emoji.m */; }; 15 | 010BA83D18ED10800030AF40 /* NSString+Emoji.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA83B18ED10800030AF40 /* NSString+Emoji.m */; }; 16 | 010BA84718ED43AC0030AF40 /* STXFeedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA84618ED43AC0030AF40 /* STXFeedViewController.m */; }; 17 | 010BA85A18ED5D680030AF40 /* UIViewController+Indicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 010BA85918ED5D680030AF40 /* UIViewController+Indicator.m */; }; 18 | 011A76A51BC41EC300B0A046 /* Feed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 011A76A41BC41EC300B0A046 /* Feed.storyboard */; settings = {ASSET_TAGS = (); }; }; 19 | 011A76A71BC4213700B0A046 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 011A76A61BC4213700B0A046 /* LaunchScreen.storyboard */; settings = {ASSET_TAGS = (); }; }; 20 | 011A76AD1BC4283F00B0A046 /* STXFeedTableViewDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 011A76AA1BC4283F00B0A046 /* STXFeedTableViewDataSource.m */; settings = {ASSET_TAGS = (); }; }; 21 | 011A76AE1BC4283F00B0A046 /* STXFeedTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 011A76AC1BC4283F00B0A046 /* STXFeedTableViewDelegate.m */; settings = {ASSET_TAGS = (); }; }; 22 | 017D674A18F6771800DD096A /* STXLikesCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 017D674818F6771800DD096A /* STXLikesCell.m */; }; 23 | 017D674F18F6773100DD096A /* STXCaptionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 017D674D18F6773100DD096A /* STXCaptionCell.m */; }; 24 | 0196482518EAB434001185C3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196482418EAB434001185C3 /* Foundation.framework */; }; 25 | 0196482718EAB434001185C3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196482618EAB434001185C3 /* CoreGraphics.framework */; }; 26 | 0196482918EAB434001185C3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196482818EAB434001185C3 /* UIKit.framework */; }; 27 | 0196482F18EAB434001185C3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0196482D18EAB434001185C3 /* InfoPlist.strings */; }; 28 | 0196483118EAB434001185C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0196483018EAB434001185C3 /* main.m */; }; 29 | 0196483518EAB434001185C3 /* STXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0196483418EAB434001185C3 /* STXAppDelegate.m */; }; 30 | 0196483718EAB434001185C3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0196483618EAB434001185C3 /* Images.xcassets */; }; 31 | 0196483E18EAB434001185C3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196483D18EAB434001185C3 /* XCTest.framework */; }; 32 | 0196483F18EAB434001185C3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196482418EAB434001185C3 /* Foundation.framework */; }; 33 | 0196484018EAB434001185C3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0196482818EAB434001185C3 /* UIKit.framework */; }; 34 | 0196484818EAB434001185C3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0196484618EAB434001185C3 /* InfoPlist.strings */; }; 35 | 0196484A18EAB434001185C3 /* STXDynamicTableViewExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0196484918EAB434001185C3 /* STXDynamicTableViewExampleTests.m */; }; 36 | 0196485518EAB48F001185C3 /* instagram_media_popular.json in Resources */ = {isa = PBXBuildFile; fileRef = 0196485418EAB48F001185C3 /* instagram_media_popular.json */; }; 37 | 01C4B21B18F7A92100712436 /* STXComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C4B21A18F7A92100712436 /* STXComment.m */; }; 38 | 01E383A618F537150094BCC1 /* UIViewController+Sharing.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383A518F537150094BCC1 /* UIViewController+Sharing.m */; }; 39 | 01E383AA18F539B00094BCC1 /* STXButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383A918F539B00094BCC1 /* STXButton.m */; }; 40 | 01E383AD18F53DDA0094BCC1 /* UIImageView+Masking.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383AC18F53DDA0094BCC1 /* UIImageView+Masking.m */; }; 41 | 01E383B018F5491D0094BCC1 /* UIButton+STXButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383AF18F5491D0094BCC1 /* UIButton+STXButton.m */; }; 42 | 01E383B318F54B890094BCC1 /* UIImage+STXImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383B218F54B890094BCC1 /* UIImage+STXImage.m */; }; 43 | 01E383B918F55D390094BCC1 /* STXPost.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383B818F55D390094BCC1 /* STXPost.m */; }; 44 | 01E383BC18F596050094BCC1 /* STXUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383BB18F596050094BCC1 /* STXUser.m */; }; 45 | 01E383C018F638030094BCC1 /* STXCommentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383BE18F638030094BCC1 /* STXCommentCell.m */; }; 46 | 01E383C518F65C6D0094BCC1 /* STXUserActionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E383C318F65C6D0094BCC1 /* STXUserActionCell.m */; }; 47 | 01E383C618F65C6D0094BCC1 /* STXUserActionCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01E383C418F65C6D0094BCC1 /* STXUserActionCell.xib */; }; 48 | 1A1DFC0356454E938E07048F /* libPods-STXDynamicTableViewExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EAD1347E22D244BC88270CFB /* libPods-STXDynamicTableViewExample.a */; }; 49 | /* End PBXBuildFile section */ 50 | 51 | /* Begin PBXContainerItemProxy section */ 52 | 0196484118EAB434001185C3 /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 0196481918EAB434001185C3 /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 0196482018EAB434001185C3; 57 | remoteInfo = STXDynamicTableViewExample; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 010BA82218EC11D40030AF40 /* STXFeedPhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXFeedPhotoCell.h; sourceTree = ""; }; 63 | 010BA82318EC11D40030AF40 /* STXFeedPhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXFeedPhotoCell.m; sourceTree = ""; }; 64 | 010BA82418EC11D40030AF40 /* STXFeedPhotoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = STXFeedPhotoCell.xib; sourceTree = ""; }; 65 | 010BA82B18EC11D40030AF40 /* STXAttributedLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXAttributedLabel.h; sourceTree = ""; }; 66 | 010BA82C18EC11D40030AF40 /* STXAttributedLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXAttributedLabel.m; sourceTree = ""; }; 67 | 010BA82D18EC11D40030AF40 /* STXLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXLabel.h; sourceTree = ""; }; 68 | 010BA82E18EC11D40030AF40 /* STXLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXLabel.m; sourceTree = ""; }; 69 | 010BA83A18ED10800030AF40 /* NSString+Emoji.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Emoji.h"; sourceTree = ""; }; 70 | 010BA83B18ED10800030AF40 /* NSString+Emoji.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Emoji.m"; sourceTree = ""; }; 71 | 010BA84518ED43AC0030AF40 /* STXFeedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXFeedViewController.h; sourceTree = ""; }; 72 | 010BA84618ED43AC0030AF40 /* STXFeedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXFeedViewController.m; sourceTree = ""; }; 73 | 010BA84B18ED4B610030AF40 /* STXPostItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXPostItem.h; sourceTree = ""; }; 74 | 010BA84F18ED4BD80030AF40 /* STXCommentItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXCommentItem.h; sourceTree = ""; }; 75 | 010BA85318ED4C150030AF40 /* STXUserItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXUserItem.h; sourceTree = ""; }; 76 | 010BA85718ED4F420030AF40 /* STXDynamicTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STXDynamicTableView.h; sourceTree = ""; }; 77 | 010BA85818ED5D680030AF40 /* UIViewController+Indicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Indicator.h"; sourceTree = ""; }; 78 | 010BA85918ED5D680030AF40 /* UIViewController+Indicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Indicator.m"; sourceTree = ""; }; 79 | 011A76A41BC41EC300B0A046 /* Feed.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Feed.storyboard; path = ../Feed.storyboard; sourceTree = ""; }; 80 | 011A76A61BC4213700B0A046 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = STXDynamicTableViewExample/LaunchScreen.storyboard; sourceTree = SOURCE_ROOT; }; 81 | 011A76A91BC4283F00B0A046 /* STXFeedTableViewDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXFeedTableViewDataSource.h; sourceTree = ""; }; 82 | 011A76AA1BC4283F00B0A046 /* STXFeedTableViewDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXFeedTableViewDataSource.m; sourceTree = ""; }; 83 | 011A76AB1BC4283F00B0A046 /* STXFeedTableViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXFeedTableViewDelegate.h; sourceTree = ""; }; 84 | 011A76AC1BC4283F00B0A046 /* STXFeedTableViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXFeedTableViewDelegate.m; sourceTree = ""; }; 85 | 017D674718F6771800DD096A /* STXLikesCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXLikesCell.h; sourceTree = ""; }; 86 | 017D674818F6771800DD096A /* STXLikesCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXLikesCell.m; sourceTree = ""; }; 87 | 017D674C18F6773100DD096A /* STXCaptionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXCaptionCell.h; sourceTree = ""; }; 88 | 017D674D18F6773100DD096A /* STXCaptionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXCaptionCell.m; sourceTree = ""; }; 89 | 0196482118EAB434001185C3 /* STXDynamicTableViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = STXDynamicTableViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 0196482418EAB434001185C3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 91 | 0196482618EAB434001185C3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 92 | 0196482818EAB434001185C3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 93 | 0196482C18EAB434001185C3 /* STXDynamicTableViewExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "STXDynamicTableViewExample-Info.plist"; sourceTree = ""; }; 94 | 0196482E18EAB434001185C3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 95 | 0196483018EAB434001185C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 96 | 0196483218EAB434001185C3 /* STXDynamicTableViewExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STXDynamicTableViewExample-Prefix.pch"; sourceTree = ""; }; 97 | 0196483318EAB434001185C3 /* STXAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STXAppDelegate.h; sourceTree = ""; }; 98 | 0196483418EAB434001185C3 /* STXAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STXAppDelegate.m; sourceTree = ""; }; 99 | 0196483618EAB434001185C3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 100 | 0196483C18EAB434001185C3 /* STXDynamicTableViewExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = STXDynamicTableViewExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | 0196483D18EAB434001185C3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 102 | 0196484518EAB434001185C3 /* STXDynamicTableViewExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "STXDynamicTableViewExampleTests-Info.plist"; sourceTree = ""; }; 103 | 0196484718EAB434001185C3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 104 | 0196484918EAB434001185C3 /* STXDynamicTableViewExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STXDynamicTableViewExampleTests.m; sourceTree = ""; }; 105 | 0196485418EAB48F001185C3 /* instagram_media_popular.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = instagram_media_popular.json; sourceTree = ""; }; 106 | 01C4B21918F7A92100712436 /* STXComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXComment.h; sourceTree = ""; }; 107 | 01C4B21A18F7A92100712436 /* STXComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXComment.m; sourceTree = ""; }; 108 | 01E383A418F537150094BCC1 /* UIViewController+Sharing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Sharing.h"; sourceTree = ""; }; 109 | 01E383A518F537150094BCC1 /* UIViewController+Sharing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Sharing.m"; sourceTree = ""; }; 110 | 01E383A818F539B00094BCC1 /* STXButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXButton.h; sourceTree = ""; }; 111 | 01E383A918F539B00094BCC1 /* STXButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXButton.m; sourceTree = ""; }; 112 | 01E383AB18F53DDA0094BCC1 /* UIImageView+Masking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+Masking.h"; sourceTree = ""; }; 113 | 01E383AC18F53DDA0094BCC1 /* UIImageView+Masking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+Masking.m"; sourceTree = ""; }; 114 | 01E383AE18F5491D0094BCC1 /* UIButton+STXButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+STXButton.h"; sourceTree = ""; }; 115 | 01E383AF18F5491D0094BCC1 /* UIButton+STXButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+STXButton.m"; sourceTree = ""; }; 116 | 01E383B118F54B890094BCC1 /* UIImage+STXImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+STXImage.h"; sourceTree = ""; }; 117 | 01E383B218F54B890094BCC1 /* UIImage+STXImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+STXImage.m"; sourceTree = ""; }; 118 | 01E383B718F55D390094BCC1 /* STXPost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXPost.h; sourceTree = ""; }; 119 | 01E383B818F55D390094BCC1 /* STXPost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXPost.m; sourceTree = ""; }; 120 | 01E383BA18F596050094BCC1 /* STXUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXUser.h; sourceTree = ""; }; 121 | 01E383BB18F596050094BCC1 /* STXUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXUser.m; sourceTree = ""; }; 122 | 01E383BD18F638030094BCC1 /* STXCommentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXCommentCell.h; sourceTree = ""; }; 123 | 01E383BE18F638030094BCC1 /* STXCommentCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXCommentCell.m; sourceTree = ""; }; 124 | 01E383C218F65C6D0094BCC1 /* STXUserActionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STXUserActionCell.h; sourceTree = ""; }; 125 | 01E383C318F65C6D0094BCC1 /* STXUserActionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STXUserActionCell.m; sourceTree = ""; }; 126 | 01E383C418F65C6D0094BCC1 /* STXUserActionCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = STXUserActionCell.xib; sourceTree = ""; }; 127 | 2BDE37AE74C190EAE2837A7A /* Pods-STXDynamicTableViewExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-STXDynamicTableViewExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-STXDynamicTableViewExample/Pods-STXDynamicTableViewExample.release.xcconfig"; sourceTree = ""; }; 128 | 9AED87155782343E4057840A /* Pods-STXDynamicTableViewExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-STXDynamicTableViewExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-STXDynamicTableViewExample/Pods-STXDynamicTableViewExample.debug.xcconfig"; sourceTree = ""; }; 129 | EAD1347E22D244BC88270CFB /* libPods-STXDynamicTableViewExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-STXDynamicTableViewExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 130 | /* End PBXFileReference section */ 131 | 132 | /* Begin PBXFrameworksBuildPhase section */ 133 | 0196481E18EAB434001185C3 /* Frameworks */ = { 134 | isa = PBXFrameworksBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 0196482718EAB434001185C3 /* CoreGraphics.framework in Frameworks */, 138 | 0196482918EAB434001185C3 /* UIKit.framework in Frameworks */, 139 | 0196482518EAB434001185C3 /* Foundation.framework in Frameworks */, 140 | 1A1DFC0356454E938E07048F /* libPods-STXDynamicTableViewExample.a in Frameworks */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | 0196483918EAB434001185C3 /* Frameworks */ = { 145 | isa = PBXFrameworksBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 0196483E18EAB434001185C3 /* XCTest.framework in Frameworks */, 149 | 0196484018EAB434001185C3 /* UIKit.framework in Frameworks */, 150 | 0196483F18EAB434001185C3 /* Foundation.framework in Frameworks */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXFrameworksBuildPhase section */ 155 | 156 | /* Begin PBXGroup section */ 157 | 010BA7FC18EC07C90030AF40 /* STXDynamicTableView */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 011A76A81BC4283F00B0A046 /* Protocols */, 161 | 010BA83918ED10640030AF40 /* Categories */, 162 | 010BA81E18EC11D40030AF40 /* Cells */, 163 | 010BA83818EC13530030AF40 /* Items */, 164 | 01E383A718F539B00094BCC1 /* Buttons */, 165 | 010BA82A18EC11D40030AF40 /* Labels */, 166 | 010BA85718ED4F420030AF40 /* STXDynamicTableView.h */, 167 | ); 168 | path = STXDynamicTableView; 169 | sourceTree = ""; 170 | }; 171 | 010BA81E18EC11D40030AF40 /* Cells */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 010BA82218EC11D40030AF40 /* STXFeedPhotoCell.h */, 175 | 010BA82318EC11D40030AF40 /* STXFeedPhotoCell.m */, 176 | 010BA82418EC11D40030AF40 /* STXFeedPhotoCell.xib */, 177 | 017D674718F6771800DD096A /* STXLikesCell.h */, 178 | 017D674818F6771800DD096A /* STXLikesCell.m */, 179 | 017D674C18F6773100DD096A /* STXCaptionCell.h */, 180 | 017D674D18F6773100DD096A /* STXCaptionCell.m */, 181 | 01E383BD18F638030094BCC1 /* STXCommentCell.h */, 182 | 01E383BE18F638030094BCC1 /* STXCommentCell.m */, 183 | 01E383C218F65C6D0094BCC1 /* STXUserActionCell.h */, 184 | 01E383C318F65C6D0094BCC1 /* STXUserActionCell.m */, 185 | 01E383C418F65C6D0094BCC1 /* STXUserActionCell.xib */, 186 | ); 187 | path = Cells; 188 | sourceTree = ""; 189 | }; 190 | 010BA82A18EC11D40030AF40 /* Labels */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 010BA82B18EC11D40030AF40 /* STXAttributedLabel.h */, 194 | 010BA82C18EC11D40030AF40 /* STXAttributedLabel.m */, 195 | 010BA82D18EC11D40030AF40 /* STXLabel.h */, 196 | 010BA82E18EC11D40030AF40 /* STXLabel.m */, 197 | ); 198 | path = Labels; 199 | sourceTree = ""; 200 | }; 201 | 010BA83818EC13530030AF40 /* Items */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 010BA84B18ED4B610030AF40 /* STXPostItem.h */, 205 | 010BA84F18ED4BD80030AF40 /* STXCommentItem.h */, 206 | 010BA85318ED4C150030AF40 /* STXUserItem.h */, 207 | ); 208 | path = Items; 209 | sourceTree = ""; 210 | }; 211 | 010BA83918ED10640030AF40 /* Categories */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 01E383AE18F5491D0094BCC1 /* UIButton+STXButton.h */, 215 | 01E383AF18F5491D0094BCC1 /* UIButton+STXButton.m */, 216 | 01E383B118F54B890094BCC1 /* UIImage+STXImage.h */, 217 | 01E383B218F54B890094BCC1 /* UIImage+STXImage.m */, 218 | 01E383AB18F53DDA0094BCC1 /* UIImageView+Masking.h */, 219 | 01E383AC18F53DDA0094BCC1 /* UIImageView+Masking.m */, 220 | 01E383A418F537150094BCC1 /* UIViewController+Sharing.h */, 221 | 01E383A518F537150094BCC1 /* UIViewController+Sharing.m */, 222 | 010BA85818ED5D680030AF40 /* UIViewController+Indicator.h */, 223 | 010BA85918ED5D680030AF40 /* UIViewController+Indicator.m */, 224 | 010BA83A18ED10800030AF40 /* NSString+Emoji.h */, 225 | 010BA83B18ED10800030AF40 /* NSString+Emoji.m */, 226 | ); 227 | path = Categories; 228 | sourceTree = ""; 229 | }; 230 | 011A76A81BC4283F00B0A046 /* Protocols */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 011A76A91BC4283F00B0A046 /* STXFeedTableViewDataSource.h */, 234 | 011A76AA1BC4283F00B0A046 /* STXFeedTableViewDataSource.m */, 235 | 011A76AB1BC4283F00B0A046 /* STXFeedTableViewDelegate.h */, 236 | 011A76AC1BC4283F00B0A046 /* STXFeedTableViewDelegate.m */, 237 | ); 238 | path = Protocols; 239 | sourceTree = ""; 240 | }; 241 | 0196481818EAB434001185C3 = { 242 | isa = PBXGroup; 243 | children = ( 244 | 010BA7FC18EC07C90030AF40 /* STXDynamicTableView */, 245 | 0196482A18EAB434001185C3 /* STXDynamicTableViewExample */, 246 | 0196484318EAB434001185C3 /* STXDynamicTableViewExampleTests */, 247 | 0196482318EAB434001185C3 /* Frameworks */, 248 | 0196482218EAB434001185C3 /* Products */, 249 | 7D3E8B648949E9F59F51A766 /* Pods */, 250 | ); 251 | sourceTree = ""; 252 | }; 253 | 0196482218EAB434001185C3 /* Products */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | 0196482118EAB434001185C3 /* STXDynamicTableViewExample.app */, 257 | 0196483C18EAB434001185C3 /* STXDynamicTableViewExampleTests.xctest */, 258 | ); 259 | name = Products; 260 | sourceTree = ""; 261 | }; 262 | 0196482318EAB434001185C3 /* Frameworks */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | 0196482418EAB434001185C3 /* Foundation.framework */, 266 | 0196482618EAB434001185C3 /* CoreGraphics.framework */, 267 | 0196482818EAB434001185C3 /* UIKit.framework */, 268 | 0196483D18EAB434001185C3 /* XCTest.framework */, 269 | EAD1347E22D244BC88270CFB /* libPods-STXDynamicTableViewExample.a */, 270 | ); 271 | name = Frameworks; 272 | sourceTree = ""; 273 | }; 274 | 0196482A18EAB434001185C3 /* STXDynamicTableViewExample */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | 01E383B618F55D090094BCC1 /* Models */, 278 | 010BA84518ED43AC0030AF40 /* STXFeedViewController.h */, 279 | 010BA84618ED43AC0030AF40 /* STXFeedViewController.m */, 280 | 0196483318EAB434001185C3 /* STXAppDelegate.h */, 281 | 0196483418EAB434001185C3 /* STXAppDelegate.m */, 282 | 0196483618EAB434001185C3 /* Images.xcassets */, 283 | 0196485318EAB48F001185C3 /* Resources */, 284 | 0196482B18EAB434001185C3 /* Supporting Files */, 285 | ); 286 | path = STXDynamicTableViewExample; 287 | sourceTree = ""; 288 | }; 289 | 0196482B18EAB434001185C3 /* Supporting Files */ = { 290 | isa = PBXGroup; 291 | children = ( 292 | 0196482C18EAB434001185C3 /* STXDynamicTableViewExample-Info.plist */, 293 | 0196482D18EAB434001185C3 /* InfoPlist.strings */, 294 | 0196483018EAB434001185C3 /* main.m */, 295 | 0196483218EAB434001185C3 /* STXDynamicTableViewExample-Prefix.pch */, 296 | ); 297 | name = "Supporting Files"; 298 | sourceTree = ""; 299 | }; 300 | 0196484318EAB434001185C3 /* STXDynamicTableViewExampleTests */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | 0196484918EAB434001185C3 /* STXDynamicTableViewExampleTests.m */, 304 | 0196484418EAB434001185C3 /* Supporting Files */, 305 | ); 306 | path = STXDynamicTableViewExampleTests; 307 | sourceTree = ""; 308 | }; 309 | 0196484418EAB434001185C3 /* Supporting Files */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | 0196484518EAB434001185C3 /* STXDynamicTableViewExampleTests-Info.plist */, 313 | 0196484618EAB434001185C3 /* InfoPlist.strings */, 314 | ); 315 | name = "Supporting Files"; 316 | sourceTree = ""; 317 | }; 318 | 0196485318EAB48F001185C3 /* Resources */ = { 319 | isa = PBXGroup; 320 | children = ( 321 | 011A76A41BC41EC300B0A046 /* Feed.storyboard */, 322 | 011A76A61BC4213700B0A046 /* LaunchScreen.storyboard */, 323 | 0196485418EAB48F001185C3 /* instagram_media_popular.json */, 324 | ); 325 | path = Resources; 326 | sourceTree = ""; 327 | }; 328 | 01E383A718F539B00094BCC1 /* Buttons */ = { 329 | isa = PBXGroup; 330 | children = ( 331 | 01E383A818F539B00094BCC1 /* STXButton.h */, 332 | 01E383A918F539B00094BCC1 /* STXButton.m */, 333 | ); 334 | path = Buttons; 335 | sourceTree = ""; 336 | }; 337 | 01E383B618F55D090094BCC1 /* Models */ = { 338 | isa = PBXGroup; 339 | children = ( 340 | 01C4B21918F7A92100712436 /* STXComment.h */, 341 | 01C4B21A18F7A92100712436 /* STXComment.m */, 342 | 01E383B718F55D390094BCC1 /* STXPost.h */, 343 | 01E383B818F55D390094BCC1 /* STXPost.m */, 344 | 01E383BA18F596050094BCC1 /* STXUser.h */, 345 | 01E383BB18F596050094BCC1 /* STXUser.m */, 346 | ); 347 | path = Models; 348 | sourceTree = ""; 349 | }; 350 | 7D3E8B648949E9F59F51A766 /* Pods */ = { 351 | isa = PBXGroup; 352 | children = ( 353 | 9AED87155782343E4057840A /* Pods-STXDynamicTableViewExample.debug.xcconfig */, 354 | 2BDE37AE74C190EAE2837A7A /* Pods-STXDynamicTableViewExample.release.xcconfig */, 355 | ); 356 | name = Pods; 357 | sourceTree = ""; 358 | }; 359 | /* End PBXGroup section */ 360 | 361 | /* Begin PBXNativeTarget section */ 362 | 0196482018EAB434001185C3 /* STXDynamicTableViewExample */ = { 363 | isa = PBXNativeTarget; 364 | buildConfigurationList = 0196484D18EAB434001185C3 /* Build configuration list for PBXNativeTarget "STXDynamicTableViewExample" */; 365 | buildPhases = ( 366 | 1DE0784B99E84F7AA6A1219D /* Check Pods Manifest.lock */, 367 | 0196481D18EAB434001185C3 /* Sources */, 368 | 0196481E18EAB434001185C3 /* Frameworks */, 369 | 0196481F18EAB434001185C3 /* Resources */, 370 | 46FB61A0D3734DDC91CA4CEC /* Copy Pods Resources */, 371 | ); 372 | buildRules = ( 373 | ); 374 | dependencies = ( 375 | ); 376 | name = STXDynamicTableViewExample; 377 | productName = STXDynamicTableViewExample; 378 | productReference = 0196482118EAB434001185C3 /* STXDynamicTableViewExample.app */; 379 | productType = "com.apple.product-type.application"; 380 | }; 381 | 0196483B18EAB434001185C3 /* STXDynamicTableViewExampleTests */ = { 382 | isa = PBXNativeTarget; 383 | buildConfigurationList = 0196485018EAB434001185C3 /* Build configuration list for PBXNativeTarget "STXDynamicTableViewExampleTests" */; 384 | buildPhases = ( 385 | 0196483818EAB434001185C3 /* Sources */, 386 | 0196483918EAB434001185C3 /* Frameworks */, 387 | 0196483A18EAB434001185C3 /* Resources */, 388 | ); 389 | buildRules = ( 390 | ); 391 | dependencies = ( 392 | 0196484218EAB434001185C3 /* PBXTargetDependency */, 393 | ); 394 | name = STXDynamicTableViewExampleTests; 395 | productName = STXDynamicTableViewExampleTests; 396 | productReference = 0196483C18EAB434001185C3 /* STXDynamicTableViewExampleTests.xctest */; 397 | productType = "com.apple.product-type.bundle.unit-test"; 398 | }; 399 | /* End PBXNativeTarget section */ 400 | 401 | /* Begin PBXProject section */ 402 | 0196481918EAB434001185C3 /* Project object */ = { 403 | isa = PBXProject; 404 | attributes = { 405 | CLASSPREFIX = STX; 406 | LastUpgradeCheck = 0700; 407 | ORGANIZATIONNAME = "2359 Media Pte Ltd"; 408 | TargetAttributes = { 409 | 0196483B18EAB434001185C3 = { 410 | TestTargetID = 0196482018EAB434001185C3; 411 | }; 412 | }; 413 | }; 414 | buildConfigurationList = 0196481C18EAB434001185C3 /* Build configuration list for PBXProject "STXDynamicTableViewExample" */; 415 | compatibilityVersion = "Xcode 3.2"; 416 | developmentRegion = English; 417 | hasScannedForEncodings = 0; 418 | knownRegions = ( 419 | en, 420 | ); 421 | mainGroup = 0196481818EAB434001185C3; 422 | productRefGroup = 0196482218EAB434001185C3 /* Products */; 423 | projectDirPath = ""; 424 | projectRoot = ""; 425 | targets = ( 426 | 0196482018EAB434001185C3 /* STXDynamicTableViewExample */, 427 | 0196483B18EAB434001185C3 /* STXDynamicTableViewExampleTests */, 428 | ); 429 | }; 430 | /* End PBXProject section */ 431 | 432 | /* Begin PBXResourcesBuildPhase section */ 433 | 0196481F18EAB434001185C3 /* Resources */ = { 434 | isa = PBXResourcesBuildPhase; 435 | buildActionMask = 2147483647; 436 | files = ( 437 | 01E383C618F65C6D0094BCC1 /* STXUserActionCell.xib in Resources */, 438 | 0196482F18EAB434001185C3 /* InfoPlist.strings in Resources */, 439 | 011A76A51BC41EC300B0A046 /* Feed.storyboard in Resources */, 440 | 0196485518EAB48F001185C3 /* instagram_media_popular.json in Resources */, 441 | 0196483718EAB434001185C3 /* Images.xcassets in Resources */, 442 | 010BA83318EC11D40030AF40 /* STXFeedPhotoCell.xib in Resources */, 443 | 011A76A71BC4213700B0A046 /* LaunchScreen.storyboard in Resources */, 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | }; 447 | 0196483A18EAB434001185C3 /* Resources */ = { 448 | isa = PBXResourcesBuildPhase; 449 | buildActionMask = 2147483647; 450 | files = ( 451 | 0196484818EAB434001185C3 /* InfoPlist.strings in Resources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | /* End PBXResourcesBuildPhase section */ 456 | 457 | /* Begin PBXShellScriptBuildPhase section */ 458 | 1DE0784B99E84F7AA6A1219D /* Check Pods Manifest.lock */ = { 459 | isa = PBXShellScriptBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | ); 463 | inputPaths = ( 464 | ); 465 | name = "Check Pods Manifest.lock"; 466 | outputPaths = ( 467 | ); 468 | runOnlyForDeploymentPostprocessing = 0; 469 | shellPath = /bin/sh; 470 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 471 | showEnvVarsInLog = 0; 472 | }; 473 | 46FB61A0D3734DDC91CA4CEC /* Copy Pods Resources */ = { 474 | isa = PBXShellScriptBuildPhase; 475 | buildActionMask = 2147483647; 476 | files = ( 477 | ); 478 | inputPaths = ( 479 | ); 480 | name = "Copy Pods Resources"; 481 | outputPaths = ( 482 | ); 483 | runOnlyForDeploymentPostprocessing = 0; 484 | shellPath = /bin/sh; 485 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-STXDynamicTableViewExample/Pods-STXDynamicTableViewExample-resources.sh\"\n"; 486 | showEnvVarsInLog = 0; 487 | }; 488 | /* End PBXShellScriptBuildPhase section */ 489 | 490 | /* Begin PBXSourcesBuildPhase section */ 491 | 0196481D18EAB434001185C3 /* Sources */ = { 492 | isa = PBXSourcesBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | 01E383BC18F596050094BCC1 /* STXUser.m in Sources */, 496 | 010BA83C18ED10800030AF40 /* NSString+Emoji.m in Sources */, 497 | 01E383AA18F539B00094BCC1 /* STXButton.m in Sources */, 498 | 0196483518EAB434001185C3 /* STXAppDelegate.m in Sources */, 499 | 01E383C018F638030094BCC1 /* STXCommentCell.m in Sources */, 500 | 011A76AD1BC4283F00B0A046 /* STXFeedTableViewDataSource.m in Sources */, 501 | 01E383B318F54B890094BCC1 /* UIImage+STXImage.m in Sources */, 502 | 01E383C518F65C6D0094BCC1 /* STXUserActionCell.m in Sources */, 503 | 010BA83718EC11D40030AF40 /* STXLabel.m in Sources */, 504 | 0196483118EAB434001185C3 /* main.m in Sources */, 505 | 01C4B21B18F7A92100712436 /* STXComment.m in Sources */, 506 | 010BA83218EC11D40030AF40 /* STXFeedPhotoCell.m in Sources */, 507 | 01E383AD18F53DDA0094BCC1 /* UIImageView+Masking.m in Sources */, 508 | 01E383A618F537150094BCC1 /* UIViewController+Sharing.m in Sources */, 509 | 01E383B918F55D390094BCC1 /* STXPost.m in Sources */, 510 | 017D674F18F6773100DD096A /* STXCaptionCell.m in Sources */, 511 | 010BA83618EC11D40030AF40 /* STXAttributedLabel.m in Sources */, 512 | 010BA84718ED43AC0030AF40 /* STXFeedViewController.m in Sources */, 513 | 010BA85A18ED5D680030AF40 /* UIViewController+Indicator.m in Sources */, 514 | 017D674A18F6771800DD096A /* STXLikesCell.m in Sources */, 515 | 011A76AE1BC4283F00B0A046 /* STXFeedTableViewDelegate.m in Sources */, 516 | 01E383B018F5491D0094BCC1 /* UIButton+STXButton.m in Sources */, 517 | ); 518 | runOnlyForDeploymentPostprocessing = 0; 519 | }; 520 | 0196483818EAB434001185C3 /* Sources */ = { 521 | isa = PBXSourcesBuildPhase; 522 | buildActionMask = 2147483647; 523 | files = ( 524 | 010BA83D18ED10800030AF40 /* NSString+Emoji.m in Sources */, 525 | 0196484A18EAB434001185C3 /* STXDynamicTableViewExampleTests.m in Sources */, 526 | ); 527 | runOnlyForDeploymentPostprocessing = 0; 528 | }; 529 | /* End PBXSourcesBuildPhase section */ 530 | 531 | /* Begin PBXTargetDependency section */ 532 | 0196484218EAB434001185C3 /* PBXTargetDependency */ = { 533 | isa = PBXTargetDependency; 534 | target = 0196482018EAB434001185C3 /* STXDynamicTableViewExample */; 535 | targetProxy = 0196484118EAB434001185C3 /* PBXContainerItemProxy */; 536 | }; 537 | /* End PBXTargetDependency section */ 538 | 539 | /* Begin PBXVariantGroup section */ 540 | 0196482D18EAB434001185C3 /* InfoPlist.strings */ = { 541 | isa = PBXVariantGroup; 542 | children = ( 543 | 0196482E18EAB434001185C3 /* en */, 544 | ); 545 | name = InfoPlist.strings; 546 | sourceTree = ""; 547 | }; 548 | 0196484618EAB434001185C3 /* InfoPlist.strings */ = { 549 | isa = PBXVariantGroup; 550 | children = ( 551 | 0196484718EAB434001185C3 /* en */, 552 | ); 553 | name = InfoPlist.strings; 554 | sourceTree = ""; 555 | }; 556 | /* End PBXVariantGroup section */ 557 | 558 | /* Begin XCBuildConfiguration section */ 559 | 0196484B18EAB434001185C3 /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ALWAYS_SEARCH_USER_PATHS = NO; 563 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 564 | CLANG_CXX_LIBRARY = "libc++"; 565 | CLANG_ENABLE_MODULES = YES; 566 | CLANG_ENABLE_OBJC_ARC = YES; 567 | CLANG_WARN_BOOL_CONVERSION = YES; 568 | CLANG_WARN_CONSTANT_CONVERSION = YES; 569 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 570 | CLANG_WARN_EMPTY_BODY = YES; 571 | CLANG_WARN_ENUM_CONVERSION = YES; 572 | CLANG_WARN_INT_CONVERSION = YES; 573 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 574 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 575 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 576 | COPY_PHASE_STRIP = NO; 577 | ENABLE_TESTABILITY = YES; 578 | GCC_C_LANGUAGE_STANDARD = gnu99; 579 | GCC_DYNAMIC_NO_PIC = NO; 580 | GCC_OPTIMIZATION_LEVEL = 0; 581 | GCC_PREPROCESSOR_DEFINITIONS = ( 582 | "DEBUG=1", 583 | "$(inherited)", 584 | ); 585 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 586 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 587 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 588 | GCC_WARN_UNDECLARED_SELECTOR = YES; 589 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 590 | GCC_WARN_UNUSED_FUNCTION = YES; 591 | GCC_WARN_UNUSED_VARIABLE = YES; 592 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 593 | ONLY_ACTIVE_ARCH = YES; 594 | SDKROOT = iphoneos; 595 | TARGETED_DEVICE_FAMILY = "1,2"; 596 | }; 597 | name = Debug; 598 | }; 599 | 0196484C18EAB434001185C3 /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | ALWAYS_SEARCH_USER_PATHS = NO; 603 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 604 | CLANG_CXX_LIBRARY = "libc++"; 605 | CLANG_ENABLE_MODULES = YES; 606 | CLANG_ENABLE_OBJC_ARC = YES; 607 | CLANG_WARN_BOOL_CONVERSION = YES; 608 | CLANG_WARN_CONSTANT_CONVERSION = YES; 609 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 610 | CLANG_WARN_EMPTY_BODY = YES; 611 | CLANG_WARN_ENUM_CONVERSION = YES; 612 | CLANG_WARN_INT_CONVERSION = YES; 613 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 614 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 615 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 616 | COPY_PHASE_STRIP = YES; 617 | ENABLE_NS_ASSERTIONS = NO; 618 | GCC_C_LANGUAGE_STANDARD = gnu99; 619 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 620 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 621 | GCC_WARN_UNDECLARED_SELECTOR = YES; 622 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 623 | GCC_WARN_UNUSED_FUNCTION = YES; 624 | GCC_WARN_UNUSED_VARIABLE = YES; 625 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 626 | SDKROOT = iphoneos; 627 | TARGETED_DEVICE_FAMILY = "1,2"; 628 | VALIDATE_PRODUCT = YES; 629 | }; 630 | name = Release; 631 | }; 632 | 0196484E18EAB434001185C3 /* Debug */ = { 633 | isa = XCBuildConfiguration; 634 | baseConfigurationReference = 9AED87155782343E4057840A /* Pods-STXDynamicTableViewExample.debug.xcconfig */; 635 | buildSettings = { 636 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 637 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 638 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 639 | GCC_PREFIX_HEADER = "STXDynamicTableViewExample/STXDynamicTableViewExample-Prefix.pch"; 640 | INFOPLIST_FILE = "STXDynamicTableViewExample/STXDynamicTableViewExample-Info.plist"; 641 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 642 | PRODUCT_BUNDLE_IDENTIFIER = "com.2359media.${PRODUCT_NAME:rfc1034identifier}"; 643 | PRODUCT_NAME = "$(TARGET_NAME)"; 644 | WRAPPER_EXTENSION = app; 645 | }; 646 | name = Debug; 647 | }; 648 | 0196484F18EAB434001185C3 /* Release */ = { 649 | isa = XCBuildConfiguration; 650 | baseConfigurationReference = 2BDE37AE74C190EAE2837A7A /* Pods-STXDynamicTableViewExample.release.xcconfig */; 651 | buildSettings = { 652 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 653 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 654 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 655 | GCC_PREFIX_HEADER = "STXDynamicTableViewExample/STXDynamicTableViewExample-Prefix.pch"; 656 | INFOPLIST_FILE = "STXDynamicTableViewExample/STXDynamicTableViewExample-Info.plist"; 657 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 658 | PRODUCT_BUNDLE_IDENTIFIER = "com.2359media.${PRODUCT_NAME:rfc1034identifier}"; 659 | PRODUCT_NAME = "$(TARGET_NAME)"; 660 | WRAPPER_EXTENSION = app; 661 | }; 662 | name = Release; 663 | }; 664 | 0196485118EAB434001185C3 /* Debug */ = { 665 | isa = XCBuildConfiguration; 666 | baseConfigurationReference = 9AED87155782343E4057840A /* Pods-STXDynamicTableViewExample.debug.xcconfig */; 667 | buildSettings = { 668 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/STXDynamicTableViewExample.app/STXDynamicTableViewExample"; 669 | FRAMEWORK_SEARCH_PATHS = ( 670 | "$(SDKROOT)/Developer/Library/Frameworks", 671 | "$(inherited)", 672 | "$(DEVELOPER_FRAMEWORKS_DIR)", 673 | ); 674 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 675 | GCC_PREFIX_HEADER = "STXDynamicTableViewExample/STXDynamicTableViewExample-Prefix.pch"; 676 | GCC_PREPROCESSOR_DEFINITIONS = ( 677 | "DEBUG=1", 678 | "$(inherited)", 679 | ); 680 | INFOPLIST_FILE = "STXDynamicTableViewExampleTests/STXDynamicTableViewExampleTests-Info.plist"; 681 | PRODUCT_BUNDLE_IDENTIFIER = "com.2359media.${PRODUCT_NAME:rfc1034identifier}"; 682 | PRODUCT_NAME = "$(TARGET_NAME)"; 683 | TEST_HOST = "$(BUNDLE_LOADER)"; 684 | WRAPPER_EXTENSION = xctest; 685 | }; 686 | name = Debug; 687 | }; 688 | 0196485218EAB434001185C3 /* Release */ = { 689 | isa = XCBuildConfiguration; 690 | baseConfigurationReference = 2BDE37AE74C190EAE2837A7A /* Pods-STXDynamicTableViewExample.release.xcconfig */; 691 | buildSettings = { 692 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/STXDynamicTableViewExample.app/STXDynamicTableViewExample"; 693 | FRAMEWORK_SEARCH_PATHS = ( 694 | "$(SDKROOT)/Developer/Library/Frameworks", 695 | "$(inherited)", 696 | "$(DEVELOPER_FRAMEWORKS_DIR)", 697 | ); 698 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 699 | GCC_PREFIX_HEADER = "STXDynamicTableViewExample/STXDynamicTableViewExample-Prefix.pch"; 700 | INFOPLIST_FILE = "STXDynamicTableViewExampleTests/STXDynamicTableViewExampleTests-Info.plist"; 701 | PRODUCT_BUNDLE_IDENTIFIER = "com.2359media.${PRODUCT_NAME:rfc1034identifier}"; 702 | PRODUCT_NAME = "$(TARGET_NAME)"; 703 | TEST_HOST = "$(BUNDLE_LOADER)"; 704 | WRAPPER_EXTENSION = xctest; 705 | }; 706 | name = Release; 707 | }; 708 | /* End XCBuildConfiguration section */ 709 | 710 | /* Begin XCConfigurationList section */ 711 | 0196481C18EAB434001185C3 /* Build configuration list for PBXProject "STXDynamicTableViewExample" */ = { 712 | isa = XCConfigurationList; 713 | buildConfigurations = ( 714 | 0196484B18EAB434001185C3 /* Debug */, 715 | 0196484C18EAB434001185C3 /* Release */, 716 | ); 717 | defaultConfigurationIsVisible = 0; 718 | defaultConfigurationName = Release; 719 | }; 720 | 0196484D18EAB434001185C3 /* Build configuration list for PBXNativeTarget "STXDynamicTableViewExample" */ = { 721 | isa = XCConfigurationList; 722 | buildConfigurations = ( 723 | 0196484E18EAB434001185C3 /* Debug */, 724 | 0196484F18EAB434001185C3 /* Release */, 725 | ); 726 | defaultConfigurationIsVisible = 0; 727 | defaultConfigurationName = Release; 728 | }; 729 | 0196485018EAB434001185C3 /* Build configuration list for PBXNativeTarget "STXDynamicTableViewExampleTests" */ = { 730 | isa = XCConfigurationList; 731 | buildConfigurations = ( 732 | 0196485118EAB434001185C3 /* Debug */, 733 | 0196485218EAB434001185C3 /* Release */, 734 | ); 735 | defaultConfigurationIsVisible = 0; 736 | defaultConfigurationName = Release; 737 | }; 738 | /* End XCConfigurationList section */ 739 | }; 740 | rootObject = 0196481918EAB434001185C3 /* Project object */; 741 | } 742 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample.xcodeproj/xcshareddata/xcschemes/STXDynamicTableViewExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Feed.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /STXDynamicTableViewExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXComment.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXComment.h 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | @interface STXComment : NSObject 10 | 11 | /** 12 | * Initialize data model from dictionary 13 | */ 14 | 15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXComment.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXComment.m 3 | // STXDynamicTableView 4 | // 5 | // Created by Jesse Armand on 27/1/14. 6 | // Copyright (c) 2014 2359 Media. All rights reserved. 7 | // 8 | 9 | #import "STXComment.h" 10 | #import "STXUser.h" 11 | 12 | @interface STXComment () 13 | 14 | @property (copy, nonatomic) NSString *commentID; 15 | @property (copy, nonatomic) NSString *text; 16 | @property (copy, nonatomic) NSDate *postDate; 17 | @property (copy, nonatomic) NSDictionary *fromDictionary; 18 | 19 | @end 20 | 21 | @implementation STXComment 22 | 23 | #pragma mark - NSCoding 24 | 25 | - (void)encodeWithCoder:(NSCoder *)encoder 26 | { 27 | [encoder encodeObject:_commentID forKey:@"commentID"]; 28 | [encoder encodeObject:_text forKey:@"text"]; 29 | [encoder encodeObject:_postDate forKey:@"postDate"]; 30 | [encoder encodeObject:_fromDictionary forKey:@"fromDictionary"]; 31 | } 32 | 33 | - (instancetype)initWithCoder:(NSCoder *)decoder 34 | { 35 | self = [super init]; 36 | if (self) { 37 | _commentID = [decoder decodeObjectForKey:@"commentID"]; 38 | _text = [decoder decodeObjectForKey:@"text"]; 39 | _postDate = [decoder decodeObjectForKey:@"postDate"]; 40 | _fromDictionary = [decoder decodeObjectForKey:@"fromDictionary"]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | - (instancetype)copyWithZone:(NSZone *)zone 47 | { 48 | STXComment *theCopy = [[[self class] allocWithZone:zone] init]; 49 | [theCopy setCommentID:[_commentID copy]]; 50 | [theCopy setText:[_text copy]]; 51 | [theCopy setPostDate:[_postDate copy]]; 52 | [theCopy setFromDictionary:[_fromDictionary copy]]; 53 | 54 | return theCopy; 55 | } 56 | 57 | #pragma mark - Initializers 58 | 59 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 60 | { 61 | self = [super init]; 62 | if (self) { 63 | NSArray *errors; 64 | NSDictionary *mappingDictionary = @{ @"id": KZProperty(commentID), 65 | @"text": KZProperty(text), 66 | @"created_time": KZBox(Date, postDate), 67 | @"from": KZProperty(fromDictionary) }; 68 | 69 | [KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:mappingDictionary errors:&errors]; 70 | } 71 | return self; 72 | } 73 | 74 | - (NSUInteger)hash 75 | { 76 | return [_commentID hash]; 77 | } 78 | 79 | - (BOOL)isEqualToComment:(STXComment *)comment 80 | { 81 | return [comment.commentID isEqualToString:_commentID]; 82 | } 83 | 84 | - (BOOL)isEqual:(id)object 85 | { 86 | if (self == object) { 87 | return YES; 88 | } 89 | 90 | if (![object isKindOfClass:[STXComment class]]) { 91 | return NO; 92 | } 93 | 94 | return [self isEqualToComment:(STXComment *)object]; 95 | } 96 | 97 | - (NSString *)description 98 | { 99 | NSDictionary *dictionary = @{ @"commentID": self.commentID ? : @"", 100 | @"from": self.from ? [self.from username] : @"", 101 | @"text": self.text ? : @"" }; 102 | return [NSString stringWithFormat:@"<%@: %p> %@", NSStringFromClass([self class]), self, dictionary]; 103 | } 104 | 105 | #pragma mark - STXCommentItem 106 | 107 | - (id)from 108 | { 109 | STXUser *user = [[STXUser alloc] initWithDictionary:self.fromDictionary]; 110 | return user; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXPost.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXPost.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 9/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | #import "STXPostItem.h" 12 | 13 | @interface STXPost : NSObject 14 | 15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXPost.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXPost.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 9/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXPost.h" 10 | #import "STXUser.h" 11 | #import "STXComment.h" 12 | 13 | @interface STXPost () 14 | 15 | @property (copy, nonatomic) NSString *postID; 16 | @property (copy, nonatomic) NSDate *postDate; 17 | 18 | @property (copy, nonatomic) NSURL *imageURL; 19 | @property (copy, nonatomic) NSURL *link; 20 | 21 | @property (copy, nonatomic) NSDictionary *caption; 22 | @property (copy, nonatomic) NSDictionary *userDictionary; 23 | 24 | @property (copy, nonatomic) NSDictionary *likes; 25 | @property (copy, nonatomic) NSArray *comments; 26 | @property (copy, nonatomic) NSDictionary *commentsDictionary; 27 | 28 | @property (nonatomic) BOOL liked; 29 | 30 | @end 31 | 32 | @implementation STXPost 33 | 34 | #pragma mark - NSCoding 35 | 36 | - (void)encodeWithCoder:(NSCoder *)encoder 37 | { 38 | [encoder encodeObject:_postID forKey:@"postID"]; 39 | [encoder encodeObject:_postDate forKey:@"postDate"]; 40 | [encoder encodeObject:_imageURL forKey:@"imageURL"]; 41 | [encoder encodeObject:_link forKey:@"link"]; 42 | [encoder encodeObject:_caption forKey:@"caption"]; 43 | [encoder encodeObject:_userDictionary forKey:@"userDictionary"]; 44 | [encoder encodeObject:_likes forKey:@"likes"]; 45 | [encoder encodeObject:_comments forKey:@"comments"]; 46 | [encoder encodeObject:_commentsDictionary forKey:@"commentsDictionary"]; 47 | [encoder encodeBool:_liked forKey:@"liked"]; 48 | } 49 | 50 | - (instancetype)initWithCoder:(NSCoder *)decoder 51 | { 52 | self = [super init]; 53 | if (self) { 54 | _postID = [decoder decodeObjectForKey:@"postID"]; 55 | _postDate = [decoder decodeObjectForKey:@"postDate"]; 56 | _imageURL = [decoder decodeObjectForKey:@"imageURL"]; 57 | _link = [decoder decodeObjectForKey:@"link"]; 58 | _caption = [decoder decodeObjectForKey:@"caption"]; 59 | _userDictionary = [decoder decodeObjectForKey:@"userDictionary"]; 60 | _likes = [decoder decodeObjectForKey:@"likes"]; 61 | _comments = [decoder decodeObjectForKey:@"comments"]; 62 | _commentsDictionary = [decoder decodeObjectForKey:@"commentsDictionary"]; 63 | _liked = [decoder decodeBoolForKey:@"liked"]; 64 | } 65 | return self; 66 | } 67 | 68 | - (instancetype)copyWithZone:(NSZone *)zone 69 | { 70 | STXPost *theCopy = [[STXPost allocWithZone:zone] init]; // use designated initializer 71 | 72 | [theCopy setPostID:[_postID copy]]; 73 | [theCopy setPostDate:[_postDate copy]]; 74 | [theCopy setImageURL:[_imageURL copy]]; 75 | [theCopy setLink:[_link copy]]; 76 | [theCopy setCaption:[_caption copy]]; 77 | [theCopy setUserDictionary:[_userDictionary copy]]; 78 | [theCopy setLikes:[_likes copy]]; 79 | [theCopy setComments:[_comments copy]]; 80 | [theCopy setCommentsDictionary:[_commentsDictionary copy]]; 81 | [theCopy setLiked:_liked]; 82 | 83 | return theCopy; 84 | } 85 | 86 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 87 | { 88 | self = [super init]; 89 | if (self) { 90 | NSArray *errors; 91 | NSDictionary *mappingDictionary = @{ @"id": KZProperty(postID), 92 | @"link": KZBox(URL, link), 93 | @"caption": KZProperty(caption), 94 | @"user": KZProperty(userDictionary), 95 | @"user_has_liked": KZProperty(liked), 96 | @"images": @{ @"standard_resolution": @{ @"url": KZBox(URL, imageURL) } }, 97 | @"likes": KZProperty(likes), 98 | @"comments": KZProperty(commentsDictionary) }; 99 | 100 | [KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:mappingDictionary errors:&errors]; 101 | } 102 | 103 | return self; 104 | } 105 | 106 | 107 | #pragma mark - NSObject 108 | 109 | - (NSUInteger)hash 110 | { 111 | return [_postID hash]; 112 | } 113 | 114 | - (BOOL)isEqualToPost:(STXPost *)post 115 | { 116 | return [post.postID isEqualToString:_postID]; 117 | } 118 | 119 | - (BOOL)isEqual:(id)object 120 | { 121 | if (self == object) { 122 | return YES; 123 | } 124 | 125 | if (![object isKindOfClass:[STXPost class]]) { 126 | return NO; 127 | } 128 | 129 | return [self isEqualToPost:(STXPost *)object]; 130 | } 131 | 132 | - (NSString *)description 133 | { 134 | NSDictionary *dictionary = @{ @"postID": self.postID ? : @"", 135 | @"postDate": self.postDate ? : @"", 136 | @"sharedURL": self.sharedURL ? : @"" }; 137 | return [NSString stringWithFormat:@"<%@: %p> %@", NSStringFromClass([self class]), self, dictionary]; 138 | } 139 | 140 | #pragma mark - STXPostItem 141 | 142 | - (NSDate *)postDate 143 | { 144 | NSTimeInterval createdTime = [[self.caption valueForKey:@"created_time"] doubleValue]; 145 | return [NSDate dateWithTimeIntervalSince1970:createdTime]; 146 | } 147 | 148 | - (NSString *)captionText 149 | { 150 | return [self.caption valueForKey:@"text"]; 151 | } 152 | 153 | - (NSURL *)sharedURL 154 | { 155 | return self.link; 156 | } 157 | 158 | - (NSURL *)photoURL 159 | { 160 | return self.imageURL; 161 | } 162 | 163 | - (NSArray *)comments 164 | { 165 | if (_comments == nil) { 166 | 167 | NSMutableArray *mutableComments = [NSMutableArray array]; 168 | 169 | NSArray *commentsArray = [self.commentsDictionary valueForKey:@"data"]; 170 | for (NSDictionary *commentDictionary in commentsArray) { 171 | STXComment *comment = [[STXComment alloc] initWithDictionary:commentDictionary]; 172 | [mutableComments addObject:comment]; 173 | } 174 | 175 | _comments = [mutableComments copy]; 176 | } 177 | 178 | return [_comments copy]; 179 | } 180 | 181 | - (NSInteger)totalLikes 182 | { 183 | NSNumber *count = [self.likes valueForKey:@"count"]; 184 | return [count integerValue]; 185 | } 186 | 187 | - (NSInteger)totalComments 188 | { 189 | NSNumber *count = [self.commentsDictionary valueForKey:@"count"]; 190 | return [count integerValue]; 191 | } 192 | 193 | - (id)user 194 | { 195 | STXUser *user = [[STXUser alloc] initWithDictionary:self.userDictionary]; 196 | return user; 197 | } 198 | 199 | @end 200 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXUser.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 9/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | 11 | #import "STXUserItem.h" 12 | 13 | @interface STXUser : NSObject 14 | 15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/Models/STXUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXUser.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 9/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXUser.h" 10 | 11 | @interface STXUser () 12 | 13 | @property (copy, nonatomic) NSString *userID; 14 | @property (copy, nonatomic) NSString *username; 15 | @property (copy, nonatomic) NSString *fullname; 16 | @property (copy, nonatomic) NSURL *profilePictureURL; 17 | 18 | @end 19 | 20 | @implementation STXUser 21 | 22 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 23 | { 24 | self = [super init]; 25 | if (self) { 26 | NSArray *errors; 27 | NSDictionary *mappingDictionary = @{ @"id": KZProperty(userID), 28 | @"username": KZProperty(username), 29 | @"full_name": KZProperty(fullname), 30 | @"profile_picture": KZBox(URL, profilePictureURL) }; 31 | 32 | [KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:mappingDictionary errors:&errors]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (NSUInteger)hash 39 | { 40 | return [_userID hash]; 41 | } 42 | 43 | - (BOOL)isEqualToUser:(STXUser *)user 44 | { 45 | return [user.userID isEqualToString:_userID]; 46 | } 47 | 48 | - (BOOL)isEqual:(id)object 49 | { 50 | if (self == object) { 51 | return YES; 52 | } 53 | 54 | if (![object isKindOfClass:[STXUser class]]) { 55 | return NO; 56 | } 57 | 58 | return [self isEqualToUser:(STXUser *)object]; 59 | } 60 | 61 | - (NSString *)description 62 | { 63 | NSDictionary *dictionary = @{ @"userID": self.userID ? : @"", 64 | @"username": self.username ? : @"", 65 | @"fullname": self.fullname ? : @"", 66 | @"profilePictureURL": self.profilePictureURL ? : @"" }; 67 | return [NSString stringWithFormat:@"<%@: %p> %@", NSStringFromClass([self class]), self, dictionary]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXAppDelegate.h 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 1/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface STXAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXAppDelegate.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 1/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import "STXAppDelegate.h" 10 | 11 | #import "STXFeedViewController.h" 12 | 13 | @implementation STXAppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | return YES; 18 | } 19 | 20 | - (void)applicationWillResignActive:(UIApplication *)application 21 | { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 24 | } 25 | 26 | - (void)applicationDidEnterBackground:(UIApplication *)application 27 | { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | - (void)applicationWillEnterForeground:(UIApplication *)application 33 | { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application 38 | { 39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 40 | } 41 | 42 | - (void)applicationWillTerminate:(UIApplication *)application 43 | { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXDynamicTableViewExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Feed 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXDynamicTableViewExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_7_0 10 | #warning "This project uses features only available in iOS SDK 7.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | 25 | #import "STXDynamicTableView.h" 26 | #endif 27 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXFeedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedViewController.h 3 | // 4 | // Created by Jesse Armand on 20/1/14. 5 | // Copyright (c) 2014 2359 Media. All rights reserved. 6 | // 7 | 8 | @interface STXFeedViewController : UIViewController 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/STXFeedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXFeedViewController.m 3 | // 4 | // Created by Jesse Armand on 20/1/14. 5 | // Copyright (c) 2014 2359 Media. All rights reserved. 6 | // 7 | 8 | #import "STXFeedViewController.h" 9 | 10 | #import "STXPost.h" 11 | 12 | #define PHOTO_CELL_ROW 0 13 | 14 | @interface STXFeedViewController () 15 | 16 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 17 | @property (strong, nonatomic) UIActivityIndicatorView *activityIndicatorView; 18 | 19 | @property (strong, nonatomic) STXFeedTableViewDataSource *tableViewDataSource; 20 | @property (strong, nonatomic) STXFeedTableViewDelegate *tableViewDelegate; 21 | 22 | @end 23 | 24 | @implementation STXFeedViewController 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | 30 | self.title = NSLocalizedString(@"Feed", nil); 31 | 32 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 33 | 34 | STXFeedTableViewDataSource *dataSource = [[STXFeedTableViewDataSource alloc] initWithController:self tableView:self.tableView]; 35 | self.tableView.dataSource = dataSource; 36 | self.tableViewDataSource = dataSource; 37 | 38 | STXFeedTableViewDelegate *delegate = [[STXFeedTableViewDelegate alloc] initWithController:self]; 39 | self.tableView.delegate = delegate; 40 | self.tableViewDelegate = delegate; 41 | 42 | self.activityIndicatorView = [self activityIndicatorViewOnView:self.view]; 43 | 44 | [self loadFeed]; 45 | } 46 | 47 | - (void)dealloc 48 | { 49 | // To prevent crash when popping this from navigation controller 50 | self.tableView.delegate = nil; 51 | self.tableView.dataSource = nil; 52 | } 53 | 54 | - (void)viewDidAppear:(BOOL)animated 55 | { 56 | [super viewDidAppear:animated]; 57 | 58 | // This will be notified when the Dynamic Type user setting changes (from the system Settings app) 59 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentSizeCategoryChanged:) name:UIContentSizeCategoryDidChangeNotification object:nil]; 60 | 61 | if ([self.tableViewDataSource.posts count] == 0) { 62 | [self.activityIndicatorView startAnimating]; 63 | } 64 | } 65 | 66 | - (void)viewDidDisappear:(BOOL)animated 67 | { 68 | [super viewDidDisappear:animated]; 69 | 70 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil]; 71 | } 72 | 73 | - (void)didReceiveMemoryWarning 74 | { 75 | [super didReceiveMemoryWarning]; 76 | } 77 | 78 | - (void)contentSizeCategoryChanged:(NSNotification *)notification 79 | { 80 | [self.tableView reloadData]; 81 | } 82 | 83 | #pragma mark - Feed 84 | 85 | - (void)loadFeed 86 | { 87 | NSString *feedPath = [[NSBundle mainBundle] pathForResource:@"instagram_media_popular" ofType:@"json"]; 88 | 89 | NSError *error; 90 | NSData *jsonData = [NSData dataWithContentsOfFile:feedPath options:NSDataReadingMappedIfSafe error:&error]; 91 | if (jsonData) { 92 | NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&error]; 93 | if (error) { 94 | UALog(@"%@", error); 95 | } 96 | 97 | NSDictionary *instagramPopularMediaDictionary = jsonObject; 98 | if (instagramPopularMediaDictionary) { 99 | NSArray *mediaDataArray = [instagramPopularMediaDictionary valueForKey:@"data"]; 100 | 101 | NSMutableArray *posts = [NSMutableArray array]; 102 | for (NSDictionary *mediaDictionary in mediaDataArray) { 103 | STXPost *post = [[STXPost alloc] initWithDictionary:mediaDictionary]; 104 | [posts addObject:post]; 105 | } 106 | 107 | self.tableViewDataSource.posts = [posts copy]; 108 | 109 | [self.tableView reloadData]; 110 | 111 | } else { 112 | if (error) { 113 | UALog(@"%@", error); 114 | } 115 | } 116 | } else { 117 | if (error) { 118 | UALog(@"%@", error); 119 | } 120 | } 121 | 122 | } 123 | 124 | #pragma mark - User Action Cell 125 | 126 | - (void)userDidLike:(STXUserActionCell *)userActionCell 127 | { 128 | 129 | } 130 | 131 | - (void)userDidUnlike:(STXUserActionCell *)userActionCell 132 | { 133 | 134 | } 135 | 136 | - (void)userWillComment:(STXUserActionCell *)userActionCell 137 | { 138 | 139 | } 140 | 141 | - (void)userWillShare:(STXUserActionCell *)userActionCell 142 | { 143 | id postItem = userActionCell.postItem; 144 | 145 | NSIndexPath *photoCellIndexPath = [NSIndexPath indexPathForRow:PHOTO_CELL_ROW inSection:userActionCell.indexPath.section]; 146 | STXFeedPhotoCell *photoCell = (STXFeedPhotoCell *)[self.tableView cellForRowAtIndexPath:photoCellIndexPath]; 147 | UIImage *photoImage = photoCell.photoImage; 148 | 149 | [self shareImage:photoImage text:postItem.captionText url:postItem.sharedURL]; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /STXDynamicTableViewExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // STXDynamicTableViewExample 4 | // 5 | // Created by Jesse Armand on 1/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | #import "STXAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([STXAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /STXDynamicTableViewExampleTests/STXDynamicTableViewExampleTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /STXDynamicTableViewExampleTests/STXDynamicTableViewExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // STXDynamicTableViewExampleTests.m 3 | // STXDynamicTableViewExampleTests 4 | // 5 | // Created by Jesse Armand on 1/4/14. 6 | // Copyright (c) 2014 2359 Media Pte Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface STXDynamicTableViewExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation STXDynamicTableViewExampleTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /STXDynamicTableViewExampleTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle install 4 | pod install 5 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # The script exits immediately if any statement or command returns non-true value 4 | set -e 5 | 6 | xcodebuild -workspace "$PROJECT_NAME" -scheme "$SCHEME_NAME" -configuration "$CONFIGURATION_NAME" -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c && exit ${PIPESTATUS[0]} 7 | 8 | --------------------------------------------------------------------------------