mirror of
https://github.com/samsonjs/samhuri.net-ios.git
synced 2026-04-27 15:07:44 +00:00
implement basic editing, preview, and improve article list
- new dark look that matches samhuri.net - improved cell layout for article list - fix yap caching - update service layer to match new API - implement authentication - fix bugs all over - add a preview
This commit is contained in:
parent
905c60d434
commit
bd704e4241
23 changed files with 456 additions and 146 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
xcuserdata
|
xcuserdata
|
||||||
|
auth.json
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
1BCFCC637C63C780248D685E /* PostCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCFCB5C36F301B2B93F8069 /* PostCell.m */; };
|
||||||
30089596C2F733D451A454E8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1613DC56A86AFA7E50460A37 /* libPods.a */; };
|
30089596C2F733D451A454E8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1613DC56A86AFA7E50460A37 /* libPods.a */; };
|
||||||
|
7B4070531AE46BC9000C2E43 /* auth.json in Resources */ = {isa = PBXBuildFile; fileRef = 7B4070521AE46BC9000C2E43 /* auth.json */; };
|
||||||
7B5C4BDF19F2606900667D48 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BDE19F2606900667D48 /* main.m */; };
|
7B5C4BDF19F2606900667D48 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BDE19F2606900667D48 /* main.m */; };
|
||||||
7B5C4BE219F2606900667D48 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE119F2606900667D48 /* AppDelegate.m */; };
|
7B5C4BE219F2606900667D48 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE119F2606900667D48 /* AppDelegate.m */; };
|
||||||
7B5C4BE519F2606900667D48 /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE419F2606900667D48 /* MasterViewController.m */; };
|
7B5C4BE519F2606900667D48 /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE419F2606900667D48 /* MasterViewController.m */; };
|
||||||
|
|
@ -22,6 +24,7 @@
|
||||||
7B9E64421A22F3840072FF42 /* BlogService.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E64411A22F3840072FF42 /* BlogService.m */; };
|
7B9E64421A22F3840072FF42 /* BlogService.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E64411A22F3840072FF42 /* BlogService.m */; };
|
||||||
7B9E644C1A230B940072FF42 /* JSONHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */; };
|
7B9E644C1A230B940072FF42 /* JSONHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */; };
|
||||||
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644E1A23129B0072FF42 /* BlogStatus.m */; };
|
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644E1A23129B0072FF42 /* BlogStatus.m */; };
|
||||||
|
7BE3A0351AE461E700E45CCB /* PreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3A0341AE461E700E45CCB /* PreviewViewController.m */; };
|
||||||
7BF029331A27117200E42EDE /* ModelStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029321A27117200E42EDE /* ModelStore.m */; };
|
7BF029331A27117200E42EDE /* ModelStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029321A27117200E42EDE /* ModelStore.m */; };
|
||||||
7BF029381A280CB200E42EDE /* BlogController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029371A280CB200E42EDE /* BlogController.m */; };
|
7BF029381A280CB200E42EDE /* BlogController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029371A280CB200E42EDE /* BlogController.m */; };
|
||||||
B8B8958B2AA40812EFE04FEF /* libPods-BlogTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36BC848680D4F831E4DE23 /* libPods-BlogTests.a */; };
|
B8B8958B2AA40812EFE04FEF /* libPods-BlogTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36BC848680D4F831E4DE23 /* libPods-BlogTests.a */; };
|
||||||
|
|
@ -39,6 +42,9 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1613DC56A86AFA7E50460A37 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
1613DC56A86AFA7E50460A37 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
1BCFCB5C36F301B2B93F8069 /* PostCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostCell.m; sourceTree = "<group>"; };
|
||||||
|
1BCFCCF30594E0E2DCC32116 /* PostCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostCell.h; sourceTree = "<group>"; };
|
||||||
|
7B4070521AE46BC9000C2E43 /* auth.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = auth.json; sourceTree = "<group>"; };
|
||||||
7B5C4BD919F2606900667D48 /* Blog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blog.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
7B5C4BD919F2606900667D48 /* Blog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blog.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
7B5C4BDD19F2606900667D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
7B5C4BDD19F2606900667D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
7B5C4BDE19F2606900667D48 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
7B5C4BDE19F2606900667D48 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
|
|
@ -66,6 +72,8 @@
|
||||||
7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONHTTPClient.m; sourceTree = "<group>"; };
|
7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONHTTPClient.m; sourceTree = "<group>"; };
|
||||||
7B9E644D1A23129B0072FF42 /* BlogStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogStatus.h; sourceTree = "<group>"; };
|
7B9E644D1A23129B0072FF42 /* BlogStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogStatus.h; sourceTree = "<group>"; };
|
||||||
7B9E644E1A23129B0072FF42 /* BlogStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlogStatus.m; sourceTree = "<group>"; };
|
7B9E644E1A23129B0072FF42 /* BlogStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlogStatus.m; sourceTree = "<group>"; };
|
||||||
|
7BE3A0331AE461E700E45CCB /* PreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreviewViewController.h; sourceTree = "<group>"; };
|
||||||
|
7BE3A0341AE461E700E45CCB /* PreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreviewViewController.m; sourceTree = "<group>"; };
|
||||||
7BF029311A27117200E42EDE /* ModelStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModelStore.h; sourceTree = "<group>"; };
|
7BF029311A27117200E42EDE /* ModelStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModelStore.h; sourceTree = "<group>"; };
|
||||||
7BF029321A27117200E42EDE /* ModelStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModelStore.m; sourceTree = "<group>"; };
|
7BF029321A27117200E42EDE /* ModelStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModelStore.m; sourceTree = "<group>"; };
|
||||||
7BF029361A280CB200E42EDE /* BlogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogController.h; sourceTree = "<group>"; };
|
7BF029361A280CB200E42EDE /* BlogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogController.h; sourceTree = "<group>"; };
|
||||||
|
|
@ -140,10 +148,14 @@
|
||||||
7B5C4BE419F2606900667D48 /* MasterViewController.m */,
|
7B5C4BE419F2606900667D48 /* MasterViewController.m */,
|
||||||
7B5C4BE619F2606900667D48 /* DetailViewController.h */,
|
7B5C4BE619F2606900667D48 /* DetailViewController.h */,
|
||||||
7B5C4BE719F2606900667D48 /* DetailViewController.m */,
|
7B5C4BE719F2606900667D48 /* DetailViewController.m */,
|
||||||
|
7BE3A0331AE461E700E45CCB /* PreviewViewController.h */,
|
||||||
|
7BE3A0341AE461E700E45CCB /* PreviewViewController.m */,
|
||||||
7B5C4BE919F2606900667D48 /* Main.storyboard */,
|
7B5C4BE919F2606900667D48 /* Main.storyboard */,
|
||||||
7B5C4BEC19F2606900667D48 /* Images.xcassets */,
|
7B5C4BEC19F2606900667D48 /* Images.xcassets */,
|
||||||
7B5C4BEE19F2606900667D48 /* LaunchScreen.xib */,
|
7B5C4BEE19F2606900667D48 /* LaunchScreen.xib */,
|
||||||
7B5C4BDC19F2606900667D48 /* Supporting Files */,
|
7B5C4BDC19F2606900667D48 /* Supporting Files */,
|
||||||
|
1BCFCB5C36F301B2B93F8069 /* PostCell.m */,
|
||||||
|
1BCFCCF30594E0E2DCC32116 /* PostCell.h */,
|
||||||
);
|
);
|
||||||
path = Blog;
|
path = Blog;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -151,6 +163,7 @@
|
||||||
7B5C4BDC19F2606900667D48 /* Supporting Files */ = {
|
7B5C4BDC19F2606900667D48 /* Supporting Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7B4070521AE46BC9000C2E43 /* auth.json */,
|
||||||
7B5C4BDD19F2606900667D48 /* Info.plist */,
|
7B5C4BDD19F2606900667D48 /* Info.plist */,
|
||||||
7B5C4BDE19F2606900667D48 /* main.m */,
|
7B5C4BDE19F2606900667D48 /* main.m */,
|
||||||
);
|
);
|
||||||
|
|
@ -273,6 +286,7 @@
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
7B5C4BD819F2606900667D48 = {
|
7B5C4BD819F2606900667D48 = {
|
||||||
CreatedOnToolsVersion = 6.0.1;
|
CreatedOnToolsVersion = 6.0.1;
|
||||||
|
DevelopmentTeam = B2W6993X5Z;
|
||||||
};
|
};
|
||||||
7B5C4BF419F2606900667D48 = {
|
7B5C4BF419F2606900667D48 = {
|
||||||
CreatedOnToolsVersion = 6.0.1;
|
CreatedOnToolsVersion = 6.0.1;
|
||||||
|
|
@ -307,6 +321,7 @@
|
||||||
7B5C4BEB19F2606900667D48 /* Main.storyboard in Resources */,
|
7B5C4BEB19F2606900667D48 /* Main.storyboard in Resources */,
|
||||||
7B5C4BF019F2606900667D48 /* LaunchScreen.xib in Resources */,
|
7B5C4BF019F2606900667D48 /* LaunchScreen.xib in Resources */,
|
||||||
7B5C4BED19F2606900667D48 /* Images.xcassets in Resources */,
|
7B5C4BED19F2606900667D48 /* Images.xcassets in Resources */,
|
||||||
|
7B4070531AE46BC9000C2E43 /* auth.json in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -398,7 +413,9 @@
|
||||||
7BF029331A27117200E42EDE /* ModelStore.m in Sources */,
|
7BF029331A27117200E42EDE /* ModelStore.m in Sources */,
|
||||||
7BF029381A280CB200E42EDE /* BlogController.m in Sources */,
|
7BF029381A280CB200E42EDE /* BlogController.m in Sources */,
|
||||||
7B5C4BE819F2606900667D48 /* DetailViewController.m in Sources */,
|
7B5C4BE819F2606900667D48 /* DetailViewController.m in Sources */,
|
||||||
|
7BE3A0351AE461E700E45CCB /* PreviewViewController.m in Sources */,
|
||||||
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */,
|
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */,
|
||||||
|
1BCFCC637C63C780248D685E /* PostCell.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,28 +7,78 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
#import "MasterViewController.h"
|
||||||
#import "DetailViewController.h"
|
#import "DetailViewController.h"
|
||||||
#import "BlogService.h"
|
#import "BlogService.h"
|
||||||
|
#import "YapDatabase.h"
|
||||||
|
#import "ModelStore.h"
|
||||||
|
#import "JSONHTTPClient.h"
|
||||||
|
#import "BlogController.h"
|
||||||
|
|
||||||
@interface AppDelegate () <UISplitViewControllerDelegate>
|
@interface AppDelegate () <UISplitViewControllerDelegate>
|
||||||
|
|
||||||
@property (nonatomic, readonly, strong) BlogService *blogService;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
_blogService = [BlogService new];
|
|
||||||
|
|
||||||
// Override point for customization after application launch.
|
|
||||||
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
||||||
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
|
UINavigationController *navigationController = splitViewController.viewControllers.lastObject;
|
||||||
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
|
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
|
||||||
splitViewController.delegate = self;
|
splitViewController.delegate = self;
|
||||||
|
[self setupBlogController];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (MasterViewController *)masterViewController {
|
||||||
|
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
||||||
|
UINavigationController *navigationController = splitViewController.viewControllers.firstObject;
|
||||||
|
MasterViewController *masterViewController = (MasterViewController *)navigationController.viewControllers.firstObject;
|
||||||
|
return masterViewController;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupBlogController {
|
||||||
|
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
|
||||||
|
NSString *dbPath = [cachesPath stringByAppendingPathComponent:@"blog.sqlite"];
|
||||||
|
ModelStore *store = [self newModelStoreWithPath:dbPath];
|
||||||
|
BlogController *blogController = [self newBlogControllerWithModelStore:store rootURL:@"http://ocean.samhuri.net:6706/"];
|
||||||
|
|
||||||
|
[self masterViewController].blogController = blogController;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ModelStore *)newModelStoreWithPath:(NSString *)dbPath {
|
||||||
|
YapDatabase *database = [[YapDatabase alloc] initWithPath:dbPath];
|
||||||
|
YapDatabaseConnection *connection = [database newConnection];
|
||||||
|
ModelStore *store = [[ModelStore alloc] initWithConnection:connection];
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BlogController *)newBlogControllerWithModelStore:(ModelStore *)store rootURL:(NSString *)rootURL {
|
||||||
|
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
||||||
|
JSONHTTPClient *client = [[JSONHTTPClient alloc] initWithSession:session];
|
||||||
|
client.defaultHeaders = [self defaultBlogHeaders];
|
||||||
|
BlogService *service = [[BlogService alloc] initWithRootURL:rootURL client:client];
|
||||||
|
BlogController *blogController = [[BlogController alloc] initWithService:service store:store];
|
||||||
|
return blogController;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)defaultBlogHeaders {
|
||||||
|
NSString *authPath = [[NSBundle mainBundle] pathForResource:@"auth.json" ofType:nil];
|
||||||
|
if (authPath.length) {
|
||||||
|
NSData *data = [NSData dataWithContentsOfFile:authPath];
|
||||||
|
NSError *error = nil;
|
||||||
|
NSDictionary *auth = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||||
|
if (auth) {
|
||||||
|
return @{@"Auth" : [NSString stringWithFormat:@"%@|%@", auth[@"username"], auth[@"password"]]};
|
||||||
|
}
|
||||||
|
NSLog(@"auth.json: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
||||||
|
NSLog(@"[ERROR] Failed to parse auth.json: %@ %@", error.localizedDescription, error.userInfo);
|
||||||
|
}
|
||||||
|
NSLog(@"[WARNING] No auth.json found. Blog will be read-only.");
|
||||||
|
return nil;
|
||||||
|
};
|
||||||
|
|
||||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||||
// 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.
|
// 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.
|
||||||
// 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.
|
// 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.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7531" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7520"/>
|
||||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
|
|
@ -14,17 +14,18 @@
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="blög" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="blög" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<nil key="simulatedStatusBarMetrics"/>
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
<point key="canvasLocation" x="548" y="455"/>
|
<point key="canvasLocation" x="548" y="455"/>
|
||||||
</view>
|
</view>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7531" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7520"/>
|
||||||
|
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Master-->
|
<!--Master-->
|
||||||
<scene sceneID="pY4-Hu-kfo">
|
<scene sceneID="pY4-Hu-kfo">
|
||||||
<objects>
|
<objects>
|
||||||
<navigationController storyboardIdentifier="Master Nav Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" id="RMx-3f-FxP" sceneMemberID="viewController">
|
<navigationController storyboardIdentifier="Master Nav Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" id="RMx-3f-FxP" sceneMemberID="viewController">
|
||||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="Pmd-2v-anx">
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
|
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" barStyle="black" translucent="NO" id="Pmd-2v-anx">
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<color key="tintColor" red="0.79534143518518507" green="0.0" blue="0.013255690586419705" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="7bK-jq-Zjz" kind="relationship" relationship="rootViewController" id="tsl-Nk-0bq"/>
|
<segue destination="7bK-jq-Zjz" kind="relationship" relationship="rootViewController" id="tsl-Nk-0bq"/>
|
||||||
|
|
@ -28,39 +32,118 @@
|
||||||
<viewControllerLayoutGuide type="bottom" id="GAO-Cl-Wes"/>
|
<viewControllerLayoutGuide type="bottom" id="GAO-Cl-Wes"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Detail view content goes here" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="0XM-y9-sOw">
|
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YUD-Xe-6Cc">
|
||||||
<rect key="frame" x="20" y="292" width="560" height="17"/>
|
<rect key="frame" x="0.0" y="492" width="600" height="44"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<items>
|
||||||
<fontDescription key="fontDescription" type="system" size="system"/>
|
<barButtonItem title="Preview" id="uGx-Jy-rOz">
|
||||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
<connections>
|
||||||
<nil key="highlightedColor"/>
|
<segue destination="ixd-IL-hNy" kind="show" identifier="showPreview" id="eVY-Ks-1b3"/>
|
||||||
</label>
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</items>
|
||||||
|
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</toolbar>
|
||||||
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wrG-1y-ZY3">
|
||||||
|
<rect key="frame" x="0.0" y="64" width="600" height="492"/>
|
||||||
|
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="JEX-9P-axG" id="vKD-HY-lGQ"/>
|
||||||
|
</connections>
|
||||||
|
</textView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="0XM-y9-sOw" firstAttribute="leading" secondItem="svH-Pt-448" secondAttribute="leading" constant="20" symbolic="YES" id="Tsc-yG-G1q"/>
|
<constraint firstAttribute="centerY" secondItem="wrG-1y-ZY3" secondAttribute="centerY" id="8IO-mb-1kq"/>
|
||||||
<constraint firstItem="0XM-y9-sOw" firstAttribute="centerY" secondItem="svH-Pt-448" secondAttribute="centerY" id="jWN-iV-94e"/>
|
<constraint firstAttribute="width" secondItem="wrG-1y-ZY3" secondAttribute="width" id="8hl-Ab-ACB"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="0XM-y9-sOw" secondAttribute="trailing" constant="20" symbolic="YES" id="tHV-ZD-HQj"/>
|
<constraint firstItem="YUD-Xe-6Cc" firstAttribute="bottom" secondItem="GAO-Cl-Wes" secondAttribute="top" id="DLQ-Z3-h8m"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="wrG-1y-ZY3" secondAttribute="centerX" id="FEY-jO-Mjl"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="YUD-Xe-6Cc" secondAttribute="centerX" id="JTh-pp-5hr"/>
|
||||||
|
<constraint firstAttribute="height" secondItem="wrG-1y-ZY3" secondAttribute="height" id="ORx-N8-ZLB"/>
|
||||||
|
<constraint firstItem="SYR-Wa-9uf" firstAttribute="top" secondItem="wrG-1y-ZY3" secondAttribute="top" id="VlL-zs-EVE"/>
|
||||||
|
<constraint firstAttribute="height" secondItem="YUD-Xe-6Cc" secondAttribute="height" id="dwI-2s-ERv"/>
|
||||||
|
<constraint firstAttribute="height" secondItem="wrG-1y-ZY3" secondAttribute="height" id="ecu-u5-SXU"/>
|
||||||
|
<constraint firstItem="YUD-Xe-6Cc" firstAttribute="top" secondItem="wrG-1y-ZY3" secondAttribute="bottom" id="iTL-zi-eKI"/>
|
||||||
|
<constraint firstAttribute="width" secondItem="YUD-Xe-6Cc" secondAttribute="width" id="n19-6i-zpg"/>
|
||||||
|
<constraint firstItem="wrG-1y-ZY3" firstAttribute="top" secondItem="SYR-Wa-9uf" secondAttribute="bottom" id="tUU-ig-gJV"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
<variation key="default">
|
||||||
|
<mask key="constraints">
|
||||||
|
<exclude reference="VlL-zs-EVE"/>
|
||||||
|
<exclude reference="8IO-mb-1kq"/>
|
||||||
|
<exclude reference="ORx-N8-ZLB"/>
|
||||||
|
<exclude reference="ecu-u5-SXU"/>
|
||||||
|
<exclude reference="dwI-2s-ERv"/>
|
||||||
|
</mask>
|
||||||
|
</variation>
|
||||||
</view>
|
</view>
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<navigationItem key="navigationItem" title="Detail" id="mOI-FS-AaM"/>
|
<navigationItem key="navigationItem" title="Article Title" id="mOI-FS-AaM">
|
||||||
|
<barButtonItem key="backBarButtonItem" title=" " id="g7z-xe-9m6"/>
|
||||||
|
</navigationItem>
|
||||||
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="detailDescriptionLabel" destination="0XM-y9-sOw" id="deQ-Na-JPF"/>
|
<outlet property="textView" destination="wrG-1y-ZY3" id="lvo-lm-t7Z"/>
|
||||||
|
<outlet property="toolbar" destination="YUD-Xe-6Cc" id="SIv-cT-WDD"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="709" y="129"/>
|
<point key="canvasLocation" x="709" y="129"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Preview-->
|
||||||
|
<scene sceneID="MEA-t1-FD6">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="Preview View Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="ixd-IL-hNy" customClass="PreviewViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="VoQ-Bk-8rs"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="qQh-ht-rVd"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="d1U-C6-XQQ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<webView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0n2-Ma-NtP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="ixd-IL-hNy" id="UhD-wi-jtK"/>
|
||||||
|
</connections>
|
||||||
|
</webView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="0n2-Ma-NtP" secondAttribute="centerX" id="Iad-IJ-UKT"/>
|
||||||
|
<constraint firstAttribute="centerY" secondItem="0n2-Ma-NtP" secondAttribute="centerY" id="cwx-9Q-nmc"/>
|
||||||
|
<constraint firstItem="0n2-Ma-NtP" firstAttribute="height" secondItem="d1U-C6-XQQ" secondAttribute="height" id="eZP-iJ-reC"/>
|
||||||
|
<constraint firstItem="0n2-Ma-NtP" firstAttribute="width" secondItem="d1U-C6-XQQ" secondAttribute="width" id="myh-gl-cb6"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" title="Preview" id="qGX-iM-tle">
|
||||||
|
<barButtonItem key="backBarButtonItem" title=" " id="Cyw-0C-8qX"/>
|
||||||
|
</navigationItem>
|
||||||
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="webView" destination="0n2-Ma-NtP" id="4yz-yf-YCI"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="wK2-TI-hhX" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1478" y="129"/>
|
||||||
|
</scene>
|
||||||
<!--Split View Controller-->
|
<!--Split View Controller-->
|
||||||
<scene sceneID="Nki-YV-4Qg">
|
<scene sceneID="Nki-YV-4Qg">
|
||||||
<objects>
|
<objects>
|
||||||
<splitViewController storyboardIdentifier="Split View Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="H1p-Uh-vWS" sceneMemberID="viewController">
|
<splitViewController storyboardIdentifier="Split View Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="H1p-Uh-vWS" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="RMx-3f-FxP" kind="relationship" relationship="masterViewController" id="BlO-5A-QYV"/>
|
<segue destination="RMx-3f-FxP" kind="relationship" relationship="masterViewController" id="BlO-5A-QYV"/>
|
||||||
<segue destination="vC3-pB-5Vb" kind="relationship" relationship="detailViewController" id="Tll-UG-LXB"/>
|
<segue destination="vC3-pB-5Vb" kind="relationship" relationship="detailViewController" id="Tll-UG-LXB"/>
|
||||||
|
|
@ -75,34 +158,49 @@
|
||||||
<objects>
|
<objects>
|
||||||
<tableViewController storyboardIdentifier="Master View Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterViewController" sceneMemberID="viewController">
|
<tableViewController storyboardIdentifier="Master View Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterViewController" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="r7i-6Z-zg0">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="r7i-6Z-zg0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="Arm-wq-HPj" detailTextLabel="3Mi-b7-7nb" style="IBUITableViewCellStyleValue1" id="WCw-Qf-5nD">
|
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" id="WCw-Qf-5nD" customClass="PostCell">
|
||||||
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
|
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WCw-Qf-5nD" id="37f-cq-3Eg">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WCw-Qf-5nD" id="37f-cq-3Eg">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Diagram of An Ember Application and More Long Text, Hooray!" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" id="Arm-wq-HPj">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2015-04-19" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vd6-ZS-EnM">
|
||||||
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
|
<rect key="frame" x="494" y="19" width="65" height="15"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" horizontalCompressionResistancePriority="751" text="Aug 31, 2013" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" id="3Mi-b7-7nb">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="I Can't Wait to See What Trey Parker & Matt Stone Do With This" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sTp-VX-x9k">
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<rect key="frame" x="8" y="14" width="478" height="21"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="vd6-ZS-EnM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="sTp-VX-x9k" secondAttribute="trailing" constant="8" id="CVb-7u-yaW"/>
|
||||||
|
<constraint firstItem="vd6-ZS-EnM" firstAttribute="trailing" secondItem="37f-cq-3Eg" secondAttribute="trailingMargin" id="ErG-lz-QR2"/>
|
||||||
|
<constraint firstAttribute="centerY" secondItem="sTp-VX-x9k" secondAttribute="centerY" id="L2O-P8-B0I"/>
|
||||||
|
<constraint firstItem="sTp-VX-x9k" firstAttribute="bottom" secondItem="37f-cq-3Eg" secondAttribute="bottomMargin" id="O7g-0A-fpG"/>
|
||||||
|
<constraint firstItem="vd6-ZS-EnM" firstAttribute="baseline" secondItem="sTp-VX-x9k" secondAttribute="baseline" id="WBb-wO-bu6"/>
|
||||||
|
<constraint firstItem="sTp-VX-x9k" firstAttribute="leading" secondItem="37f-cq-3Eg" secondAttribute="leadingMargin" id="iU2-pk-NdH"/>
|
||||||
|
</constraints>
|
||||||
|
<variation key="default">
|
||||||
|
<mask key="constraints">
|
||||||
|
<exclude reference="L2O-P8-B0I"/>
|
||||||
|
</mask>
|
||||||
|
</variation>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="dateLabel" destination="vd6-ZS-EnM" id="mER-WA-QZz"/>
|
||||||
|
<outlet property="titleLabel" destination="sTp-VX-x9k" id="qTw-yO-gEL"/>
|
||||||
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showDetail" id="6S0-TO-JiA"/>
|
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showDetail" id="6S0-TO-JiA"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
|
|
@ -113,12 +211,14 @@
|
||||||
<outlet property="delegate" destination="7bK-jq-Zjz" id="RA6-mI-bju"/>
|
<outlet property="delegate" destination="7bK-jq-Zjz" id="RA6-mI-bju"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<navigationItem key="navigationItem" title="Master" id="Zdf-7t-Un8">
|
<navigationItem key="navigationItem" title="samhuri.net" id="Zdf-7t-Un8">
|
||||||
|
<barButtonItem key="backBarButtonItem" title=" " id="UCo-qs-2hI"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="leftBarButtonItem" destination="8HS-W8-a6l" id="CwH-zD-2Qm"/>
|
<outlet property="leftBarButtonItem" destination="8HS-W8-a6l" id="CwH-zD-2Qm"/>
|
||||||
<outlet property="rightBarButtonItem" destination="u2a-vi-nHQ" id="zsl-st-de9"/>
|
<outlet property="rightBarButtonItem" destination="u2a-vi-nHQ" id="zsl-st-de9"/>
|
||||||
</connections>
|
</connections>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="addButton" destination="u2a-vi-nHQ" id="BNL-ge-ZGw"/>
|
<outlet property="addButton" destination="u2a-vi-nHQ" id="BNL-ge-ZGw"/>
|
||||||
<outlet property="publishButton" destination="8HS-W8-a6l" id="amK-fb-yQq"/>
|
<outlet property="publishButton" destination="8HS-W8-a6l" id="amK-fb-yQq"/>
|
||||||
|
|
@ -142,8 +242,11 @@
|
||||||
<scene sceneID="r7l-gg-dq7">
|
<scene sceneID="r7l-gg-dq7">
|
||||||
<objects>
|
<objects>
|
||||||
<navigationController storyboardIdentifier="Detail Nav Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="vC3-pB-5Vb" sceneMemberID="viewController">
|
<navigationController storyboardIdentifier="Detail Nav Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="vC3-pB-5Vb" sceneMemberID="viewController">
|
||||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="DjV-YW-jjY">
|
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||||
|
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" barStyle="black" translucent="NO" id="DjV-YW-jjY">
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="JEX-9P-axG" kind="relationship" relationship="rootViewController" id="GKi-kA-LjT"/>
|
<segue destination="JEX-9P-axG" kind="relationship" relationship="rootViewController" id="GKi-kA-LjT"/>
|
||||||
|
|
@ -155,6 +258,6 @@
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="6S0-TO-JiA"/>
|
<segue reference="Tll-UG-LXB"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@
|
||||||
|
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
|
|
||||||
NSString *BlogStatusChangedNotification;
|
extern NSString *BlogStatusChangedNotification;
|
||||||
NSString *BlogDraftsChangedNotification;
|
extern NSString *BlogDraftsChangedNotification;
|
||||||
NSString *BlogDraftAddedNotification;
|
extern NSString *BlogDraftAddedNotification;
|
||||||
NSString *BlogDraftRemovedNotification;
|
extern NSString *BlogDraftRemovedNotification;
|
||||||
NSString *BlogPublishedPostsChangedNotification;
|
extern NSString *BlogPublishedPostsChangedNotification;
|
||||||
NSString *BlogPublishedPostAddedNotification;
|
extern NSString *BlogPublishedPostAddedNotification;
|
||||||
NSString *BlogPublishedPostRemovedNotification;
|
extern NSString *BlogPublishedPostRemovedNotification;
|
||||||
NSString *BlogPostChangedNotification;
|
extern NSString *BlogPostChangedNotification;
|
||||||
NSString *BlogPostDeletedNotification;
|
extern NSString *BlogPostDeletedNotification;
|
||||||
|
|
||||||
@class PMKPromise;
|
@class PMKPromise;
|
||||||
@class ModelStore;
|
@class ModelStore;
|
||||||
|
|
@ -26,7 +26,7 @@ NSString *BlogPostDeletedNotification;
|
||||||
|
|
||||||
- (instancetype)initWithService:(BlogService *)service store:(ModelStore *)store;
|
- (instancetype)initWithService:(BlogService *)service store:(ModelStore *)store;
|
||||||
|
|
||||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path;
|
- (NSMutableURLRequest *)previewRequestWithPath:(NSString *)path;
|
||||||
|
|
||||||
- (PMKPromise *)requestBlogStatus;
|
- (PMKPromise *)requestBlogStatus;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,11 @@ NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path {
|
- (NSMutableURLRequest *)previewRequestWithPath:(NSString *)path;
|
||||||
return [_service previewURLForPostWithPath:path];
|
{
|
||||||
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[_service urlFor:path]];
|
||||||
|
[request addValue:@"text/html" forHTTPHeaderField:@"Accept"];
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PMKPromise *)requestBlogStatus {
|
- (PMKPromise *)requestBlogStatus {
|
||||||
|
|
@ -55,9 +58,11 @@ NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
||||||
- (PMKPromise *)requestDrafts {
|
- (PMKPromise *)requestDrafts {
|
||||||
NSArray *posts = [_store drafts];
|
NSArray *posts = [_store drafts];
|
||||||
if (posts) {
|
if (posts) {
|
||||||
|
NSLog(@"returning %@ cached drafts", @(posts.count));
|
||||||
return [PMKPromise promiseWithValue:posts];
|
return [PMKPromise promiseWithValue:posts];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
NSLog(@"requesting drafts from server");
|
||||||
return [_service requestDrafts].then(^(NSArray *posts) {
|
return [_service requestDrafts].then(^(NSArray *posts) {
|
||||||
[_store saveDrafts:posts];
|
[_store saveDrafts:posts];
|
||||||
return posts;
|
return posts;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ typedef NS_ENUM(NSUInteger, BlogServiceErrorCode) {
|
||||||
|
|
||||||
- (instancetype)initWithRootURL:(NSString *)rootURL client:(JSONHTTPClient *)client;
|
- (instancetype)initWithRootURL:(NSString *)rootURL client:(JSONHTTPClient *)client;
|
||||||
|
|
||||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path;
|
- (NSURL *)urlFor:(NSString *)path, ...;
|
||||||
|
|
||||||
- (PMKPromise *)requestBlogStatus;
|
- (PMKPromise *)requestBlogStatus;
|
||||||
- (PMKPromise *)requestPublishEnvironment:(NSString *)environment;
|
- (PMKPromise *)requestPublishEnvironment:(NSString *)environment;
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,6 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path {
|
|
||||||
return [self urlFor:@"%@/preview", path];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSURL *)urlFor:(NSString *)path, ... {
|
- (NSURL *)urlFor:(NSString *)path, ... {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, path);
|
va_start(args, path);
|
||||||
|
|
@ -82,7 +78,7 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PMKPromise *)requestDrafts {
|
- (PMKPromise *)requestDrafts {
|
||||||
return [self.client get:[self urlFor:@"/drafts"] headers:nil].then([self decodePostsBlock]);
|
return [self.client get:[self urlFor:@"/posts/drafts"] headers:nil].then([self decodePostsBlock]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PMKPromise *)requestPublishedPosts {
|
- (PMKPromise *)requestPublishedPosts {
|
||||||
|
|
@ -97,9 +93,9 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
||||||
NSDictionary *fields = @{@"id": draftID,
|
NSDictionary *fields = @{@"id": draftID,
|
||||||
@"title": title,
|
@"title": title,
|
||||||
@"body": body,
|
@"body": body,
|
||||||
@"link": link,
|
@"link": link ?: [NSNull null],
|
||||||
};
|
};
|
||||||
return [self.client postJSON:[self urlFor:@"/drafts"] headers:nil fields:fields].then([self decodePostBlock]);
|
return [self.client postJSON:[self urlFor:@"/posts/drafts"] headers:nil fields:fields].then([self decodePostBlock]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PMKPromise *)requestPublishDraftWithPath:(NSString *)path {
|
- (PMKPromise *)requestPublishDraftWithPath:(NSString *)path {
|
||||||
|
|
@ -113,7 +109,7 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
||||||
- (PMKPromise *)requestUpdatePostWithPath:(NSString *)path title:(NSString *)title body:(NSString *)body link:(NSString *)link {
|
- (PMKPromise *)requestUpdatePostWithPath:(NSString *)path title:(NSString *)title body:(NSString *)body link:(NSString *)link {
|
||||||
NSDictionary *fields = @{@"title": title,
|
NSDictionary *fields = @{@"title": title,
|
||||||
@"body": body,
|
@"body": body,
|
||||||
@"link": link,
|
@"link": link ?: [NSNull null],
|
||||||
};
|
};
|
||||||
return [self.client putJSON:[self urlFor:path] headers:nil fields:fields];
|
return [self.client putJSON:[self urlFor:path] headers:nil fields:fields];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
||||||
|
@class BlogController;
|
||||||
@class Post;
|
@class Post;
|
||||||
|
|
||||||
@interface DetailViewController : UIViewController
|
@interface DetailViewController : UIViewController
|
||||||
|
|
||||||
|
@property (strong, nonatomic) BlogController *blogController;
|
||||||
@property (strong, nonatomic) Post *post;
|
@property (strong, nonatomic) Post *post;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,16 @@
|
||||||
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import <PromiseKit/Promise.h>
|
||||||
#import "DetailViewController.h"
|
#import "DetailViewController.h"
|
||||||
|
#import "BlogController.h"
|
||||||
#import "Post.h"
|
#import "Post.h"
|
||||||
|
#import "PreviewViewController.h"
|
||||||
|
|
||||||
@interface DetailViewController ()
|
@interface DetailViewController () <UITextViewDelegate>
|
||||||
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
|
@property (nonatomic, weak) IBOutlet UITextView *textView;
|
||||||
|
@property (nonatomic, weak) IBOutlet UIToolbar *toolbar;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
@ -22,30 +26,60 @@
|
||||||
- (void)setPost:(id)newPost {
|
- (void)setPost:(id)newPost {
|
||||||
if (_post != newPost) {
|
if (_post != newPost) {
|
||||||
_post = newPost;
|
_post = newPost;
|
||||||
|
|
||||||
// Update the view.
|
|
||||||
[self configureView];
|
[self configureView];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)configureView {
|
- (void)configureView {
|
||||||
// Update the user interface for the detail item.
|
|
||||||
if (self.post) {
|
if (self.post) {
|
||||||
// FIXME: date, link (edit, open), status (draft, published), delete, preview, publish
|
// FIXME: date, status (draft, published)
|
||||||
self.navigationItem.title = self.post.title ?: @"Untitled";
|
self.navigationItem.title = self.post.title ?: @"Untitled";
|
||||||
self.detailDescriptionLabel.text = self.post.body;
|
self.textView.text = self.post.body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
// Do any additional setup after loading the view, typically from a nib.
|
|
||||||
[self configureView];
|
[self configureView];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didReceiveMemoryWarning {
|
- (void)viewWillAppear:(BOOL)animated;
|
||||||
[super didReceiveMemoryWarning];
|
{
|
||||||
// Dispose of any resources that can be recreated.
|
[super viewWillAppear:animated];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(savePostBody) name:UIApplicationWillResignActiveNotification object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
|
[super viewWillDisappear:animated];
|
||||||
|
[self savePostBody];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PMKPromise *)savePostBody {
|
||||||
|
if (!self.post || !self.textView) { return [PMKPromise promiseWithValue:nil]; }
|
||||||
|
|
||||||
|
Post *newPost = [self.post copyWithBody:self.textView.text];
|
||||||
|
if (![self.post isEqual:newPost])
|
||||||
|
{
|
||||||
|
self.post = newPost;
|
||||||
|
return [self.blogController requestUpdatePostWithPath:self.post.path title:self.post.title body:self.post.body link:self.post.url.absoluteString]
|
||||||
|
.then(^(Post *post) {
|
||||||
|
NSLog(@"saved post at path %@", self.post.path);
|
||||||
|
}).catch(^(NSError *error) {
|
||||||
|
NSLog(@"Error saving post at path %@: %@ %@", self.post.path, error.localizedDescription, error.userInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [PMKPromise promiseWithValue:self.post];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||||
|
[super prepareForSegue:segue sender:sender];
|
||||||
|
if ([segue.identifier isEqualToString:@"showPreview"]) {
|
||||||
|
PreviewViewController *previewViewController = segue.destinationViewController;
|
||||||
|
previewViewController.promise = [self savePostBody];
|
||||||
|
previewViewController.initialRequest = [self.blogController previewRequestWithPath:self.post.path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@
|
||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UIStatusBarStyle</key>
|
||||||
|
<string>UIStatusBarStyleLightContent</string>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
<key>UIStatusBarTintParameters</key>
|
<key>UIStatusBarTintParameters</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UINavigationBar</key>
|
<key>UINavigationBar</key>
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ NSString * const JSONHTTPClientErrorDomain = @"JSONHTTPClientErrorDomain";
|
||||||
[request setValue:headers[key] forHTTPHeaderField:key];
|
[request setValue:headers[key] forHTTPHeaderField:key];
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
[request setValue:[NSString stringWithFormat:@"%lu", [data length]] forKey:@"Content-Length"];
|
[request setValue:[NSString stringWithFormat:@"%lu", [data length]] forHTTPHeaderField:@"Content-Length"];
|
||||||
[request setHTTPBody:data];
|
[request setHTTPBody:data];
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
||||||
|
@class BlogController;
|
||||||
|
|
||||||
@interface MasterViewController : UITableViewController
|
@interface MasterViewController : UITableViewController
|
||||||
|
|
||||||
|
@property (strong, nonatomic) BlogController *blogController;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,12 @@
|
||||||
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import <PromiseKit/Promise.h>
|
||||||
#import "MasterViewController.h"
|
#import "MasterViewController.h"
|
||||||
#import "DetailViewController.h"
|
#import "DetailViewController.h"
|
||||||
#import "Post.h"
|
#import "Post.h"
|
||||||
#import "BlogController.h"
|
#import "BlogController.h"
|
||||||
#import "ModelStore.h"
|
#import "PostCell.h"
|
||||||
#import "BlogService.h"
|
|
||||||
#import "YapDatabaseConnection.h"
|
|
||||||
#import "YapDatabase.h"
|
|
||||||
#import "JSONHTTPClient.h"
|
|
||||||
|
|
||||||
@interface MasterViewController ()
|
@interface MasterViewController ()
|
||||||
|
|
||||||
|
|
@ -22,7 +19,6 @@
|
||||||
@property (strong, nonatomic) DetailViewController *detailViewController;
|
@property (strong, nonatomic) DetailViewController *detailViewController;
|
||||||
@property (strong, nonatomic) IBOutlet UIBarButtonItem *publishButton;
|
@property (strong, nonatomic) IBOutlet UIBarButtonItem *publishButton;
|
||||||
@property (strong, nonatomic) IBOutlet UIBarButtonItem *addButton;
|
@property (strong, nonatomic) IBOutlet UIBarButtonItem *addButton;
|
||||||
@property (strong, nonatomic) BlogController *blogController;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
@ -34,16 +30,6 @@
|
||||||
self.clearsSelectionOnViewWillAppear = NO;
|
self.clearsSelectionOnViewWillAppear = NO;
|
||||||
self.preferredContentSize = CGSizeMake(320.0, 600.0);
|
self.preferredContentSize = CGSizeMake(320.0, 600.0);
|
||||||
}
|
}
|
||||||
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
|
|
||||||
NSString *path = [cachesPath stringByAppendingPathComponent:@"blog.sqlite"];
|
|
||||||
YapDatabase *database = [[YapDatabase alloc] initWithPath:path];
|
|
||||||
YapDatabaseConnection *connection = [database newConnection];
|
|
||||||
ModelStore *store = [[ModelStore alloc] initWithConnection:connection];
|
|
||||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
||||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
|
||||||
JSONHTTPClient *client = [[JSONHTTPClient alloc] initWithSession:session];
|
|
||||||
BlogService *service = [[BlogService alloc] initWithRootURL:@"http://ocean.samhuri.net:6706/" client:client];
|
|
||||||
self.blogController = [[BlogController alloc] initWithService:service store:store];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
|
|
@ -65,10 +51,10 @@
|
||||||
// TODO: show a spinner
|
// TODO: show a spinner
|
||||||
[self.blogController requestDrafts].then(^(NSArray *drafts) {
|
[self.blogController requestDrafts].then(^(NSArray *drafts) {
|
||||||
return [self.blogController requestPublishedPosts].then(^(NSArray *posts) {
|
return [self.blogController requestPublishedPosts].then(^(NSArray *posts) {
|
||||||
NSLog(@"drafts = %@", drafts);
|
|
||||||
NSLog(@"posts = %@", posts);
|
|
||||||
self.posts = [drafts mutableCopy];
|
self.posts = [drafts mutableCopy];
|
||||||
[self.posts addObjectsFromArray:posts];
|
for (Post *post in [posts reverseObjectEnumerator]) {
|
||||||
|
[self.posts addObject:post];
|
||||||
|
}
|
||||||
[self.tableView reloadData];
|
[self.tableView reloadData];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -93,10 +79,11 @@
|
||||||
#pragma mark - Segues
|
#pragma mark - Segues
|
||||||
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||||
if ([[segue identifier] isEqualToString:@"showDetail"]) {
|
if ([segue.identifier isEqualToString:@"showDetail"]) {
|
||||||
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
|
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
|
||||||
Post *post = self.posts[indexPath.row];
|
Post *post = self.posts[indexPath.row];
|
||||||
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
|
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
|
||||||
|
controller.blogController = self.blogController;
|
||||||
[controller setPost:post];
|
[controller setPost:post];
|
||||||
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
|
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
|
||||||
controller.navigationItem.leftItemsSupplementBackButton = YES;
|
controller.navigationItem.leftItemsSupplementBackButton = YES;
|
||||||
|
|
@ -114,12 +101,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
|
PostCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
|
||||||
|
|
||||||
Post *post = self.posts[indexPath.row];
|
Post *post = self.posts[indexPath.row];
|
||||||
// FIXME: unique title
|
// FIXME: unique title
|
||||||
cell.textLabel.text = post.title ?: @"Untitled";
|
NSString *title = post.title ?: @"Untitled";
|
||||||
cell.detailTextLabel.text = post.draft ? @"Draft" : post.formattedDate;
|
NSString *date = post.draft ? @"Draft" : post.formattedDate;
|
||||||
|
[cell configureWithTitle:title date:date];
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,12 @@
|
||||||
NSArray *postPaths = [transaction objectForKey:@"drafts" inCollection:@"PostCollection"];
|
NSArray *postPaths = [transaction objectForKey:@"drafts" inCollection:@"PostCollection"];
|
||||||
if (postPaths) {
|
if (postPaths) {
|
||||||
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
||||||
|
if (object) {
|
||||||
|
if (!posts) {
|
||||||
|
posts = [NSMutableArray new];
|
||||||
|
}
|
||||||
[posts addObject:object];
|
[posts addObject:object];
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
@ -49,7 +54,12 @@
|
||||||
NSArray *postPaths = [transaction objectForKey:@"published" inCollection:@"PostCollection"];
|
NSArray *postPaths = [transaction objectForKey:@"published" inCollection:@"PostCollection"];
|
||||||
if (postPaths) {
|
if (postPaths) {
|
||||||
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
||||||
|
if (object) {
|
||||||
|
if (!posts) {
|
||||||
|
posts = [NSMutableArray new];
|
||||||
|
}
|
||||||
[posts addObject:object];
|
[posts addObject:object];
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
@ -90,7 +100,7 @@
|
||||||
NSMutableArray *postIDs = [NSMutableArray array];
|
NSMutableArray *postIDs = [NSMutableArray array];
|
||||||
for (Post *post in posts) {
|
for (Post *post in posts) {
|
||||||
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
||||||
[postIDs addObject:post.objectID];
|
[postIDs addObject:post.path];
|
||||||
}
|
}
|
||||||
[transaction setObject:postIDs forKey:@"drafts" inCollection:@"PostCollection"];
|
[transaction setObject:postIDs forKey:@"drafts" inCollection:@"PostCollection"];
|
||||||
} completionBlock:^{
|
} completionBlock:^{
|
||||||
|
|
@ -105,7 +115,7 @@
|
||||||
NSMutableArray *postIDs = [NSMutableArray array];
|
NSMutableArray *postIDs = [NSMutableArray array];
|
||||||
for (Post *post in posts) {
|
for (Post *post in posts) {
|
||||||
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
||||||
[postIDs addObject:post.objectID];
|
[postIDs addObject:post.path];
|
||||||
}
|
}
|
||||||
[transaction setObject:postIDs forKey:@"published" inCollection:@"PostCollection"];
|
[transaction setObject:postIDs forKey:@"published" inCollection:@"PostCollection"];
|
||||||
} completionBlock:^{
|
} completionBlock:^{
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
@property (nonatomic, readonly) NSInteger mm_day;
|
@property (nonatomic, readonly) NSInteger mm_day;
|
||||||
|
|
||||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
||||||
|
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
|
||||||
- (NSString *)mm_relativeToNow;
|
- (NSString *)mm_relativeToNow;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -18,112 +18,108 @@
|
||||||
@implementation NSDate (Marshmallows)
|
@implementation NSDate (Marshmallows)
|
||||||
|
|
||||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
|
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
|
||||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
return [self mm_dateWithYear:year month:month day:day hour:0 minute:0 second:0];
|
||||||
NSDateComponents *components = [NSDateComponents new];
|
}
|
||||||
[components setYear:year];
|
|
||||||
[components setMonth:month];
|
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
|
||||||
[components setDay:day];
|
return [[NSCalendar currentCalendar] dateWithEra:1 year:year month:month day:day hour:hour minute:minute second:second nanosecond:0];
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)mm_relativeToNow {
|
- (NSString *)mm_relativeToNow {
|
||||||
double diff = [[NSDate date] timeIntervalSinceDate:self];
|
double diff = [[NSDate date] timeIntervalSinceDate:self];
|
||||||
NSString *result = nil;
|
|
||||||
|
|
||||||
// future
|
// future
|
||||||
if (diff < -2 * YEAR) {
|
if (diff < -2 * YEAR) {
|
||||||
result = [NSString stringWithFormat:@"in %d years", abs(diff / YEAR)];
|
return [NSString stringWithFormat:@"in %d years", abs((int)(diff / YEAR))];
|
||||||
}
|
}
|
||||||
else if (diff < -YEAR) {
|
else if (diff < -YEAR) {
|
||||||
result = @"next year";
|
return @"next year";
|
||||||
}
|
}
|
||||||
else if (diff < -8 * WEEK) {
|
else if (diff < -8 * WEEK) {
|
||||||
result = [NSString stringWithFormat:@"in %d months", abs(diff / MONTH)];
|
return [NSString stringWithFormat:@"in %d months", abs((int)(diff / MONTH))];
|
||||||
}
|
}
|
||||||
else if (diff < -4 * WEEK) {
|
else if (diff < -4 * WEEK) {
|
||||||
result = @"next month";
|
return @"next month";
|
||||||
}
|
}
|
||||||
else if (diff < -2 * WEEK) {
|
else if (diff < -2 * WEEK) {
|
||||||
result = [NSString stringWithFormat:@"in %d weeks", abs(diff / WEEK)];
|
return [NSString stringWithFormat:@"in %d weeks", abs((int)(diff / WEEK))];
|
||||||
}
|
}
|
||||||
else if (diff < -WEEK) {
|
else if (diff < -WEEK) {
|
||||||
result = @"next week";
|
return @"next week";
|
||||||
}
|
}
|
||||||
else if (diff < -2 * DAY) {
|
else if (diff < -2 * DAY) {
|
||||||
result = [NSString stringWithFormat:@"in %d days", abs(diff / DAY)];
|
return [NSString stringWithFormat:@"in %d days", abs((int)(diff / DAY))];
|
||||||
}
|
}
|
||||||
else if (diff < -DAY) {
|
else if (diff < -DAY) {
|
||||||
result = @"tomorrow";
|
return @"tomorrow";
|
||||||
}
|
}
|
||||||
else if (diff < -2 * HOUR) {
|
else if (diff < -2 * HOUR) {
|
||||||
result = [NSString stringWithFormat:@"in %d hours", abs(diff / HOUR)];
|
return [NSString stringWithFormat:@"in %d hours", abs((int)(diff / HOUR))];
|
||||||
}
|
}
|
||||||
else if (diff < -HOUR) {
|
else if (diff < -HOUR) {
|
||||||
result = @"in an hour";
|
return @"in an hour";
|
||||||
}
|
}
|
||||||
else if (diff < -2 * MINUTE) {
|
else if (diff < -2 * MINUTE) {
|
||||||
result = [NSString stringWithFormat:@"in %d minutes", abs(diff / MINUTE)];
|
return [NSString stringWithFormat:@"in %d minutes", abs((int)(diff / MINUTE))];
|
||||||
}
|
}
|
||||||
else if (diff < -MINUTE) {
|
else if (diff < -MINUTE) {
|
||||||
result = @"in a minute";
|
return @"in a minute";
|
||||||
}
|
}
|
||||||
|
|
||||||
// present
|
// present
|
||||||
else if (diff < MINUTE) {
|
else if (diff < MINUTE) {
|
||||||
result = @"right now";
|
return @"right now";
|
||||||
}
|
}
|
||||||
|
|
||||||
// past
|
// past
|
||||||
else if (diff < 2 * MINUTE) {
|
else if (diff < 2 * MINUTE) {
|
||||||
result = @"a minute ago";
|
return @"a minute ago";
|
||||||
}
|
}
|
||||||
else if (diff < HOUR) {
|
else if (diff < HOUR) {
|
||||||
result = [NSString stringWithFormat:@"%d minutes ago", (int)(diff / MINUTE)];
|
return [NSString stringWithFormat:@"%d minutes ago", (int)(diff / MINUTE)];
|
||||||
}
|
}
|
||||||
else if (diff < 2 * HOUR) {
|
else if (diff < 2 * HOUR) {
|
||||||
result = @"an hour ago";
|
return @"an hour ago";
|
||||||
}
|
}
|
||||||
else if (diff < DAY) {
|
else if (diff < DAY) {
|
||||||
result = [NSString stringWithFormat:@"%d hours ago", (int)(diff / HOUR)];
|
return [NSString stringWithFormat:@"%d hours ago", (int)(diff / HOUR)];
|
||||||
}
|
}
|
||||||
else if (diff < 2 * DAY) {
|
else if (diff < 2 * DAY) {
|
||||||
result = @"yesterday";
|
return @"yesterday";
|
||||||
}
|
}
|
||||||
else if (diff < WEEK) {
|
else if (diff < WEEK) {
|
||||||
result = [NSString stringWithFormat:@"%d days ago", (int)(diff / DAY)];
|
return [NSString stringWithFormat:@"%d days ago", (int)(diff / DAY)];
|
||||||
}
|
}
|
||||||
else if (diff < 2 * WEEK) {
|
else if (diff < 2 * WEEK) {
|
||||||
result = @"last week";
|
return @"last week";
|
||||||
}
|
}
|
||||||
else if (diff < 4 * WEEK) {
|
else if (diff < 4 * WEEK) {
|
||||||
result = [NSString stringWithFormat:@"%d weeks ago", (int)(diff / WEEK)];
|
return [NSString stringWithFormat:@"%d weeks ago", (int)(diff / WEEK)];
|
||||||
}
|
}
|
||||||
else if (diff < 8 * WEEK) {
|
else if (diff < 8 * WEEK) {
|
||||||
result = @"last month";
|
return @"last month";
|
||||||
}
|
}
|
||||||
else if (diff < YEAR) {
|
else if (diff < YEAR) {
|
||||||
result = [NSString stringWithFormat:@"%d months ago", (int)(diff / MONTH)];
|
return [NSString stringWithFormat:@"%d months ago", (int)(diff / MONTH)];
|
||||||
}
|
}
|
||||||
else if (diff < 2 * YEAR) {
|
else if (diff < 2 * YEAR) {
|
||||||
result = @"last year";
|
return @"last year";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = [NSString stringWithFormat:@"%d years ago", (int)(diff / YEAR)];
|
return [NSString stringWithFormat:@"%d years ago", (int)(diff / YEAR)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)mm_year {
|
- (NSInteger)mm_year {
|
||||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:self].year;
|
return [[NSCalendar currentCalendar] component:NSCalendarUnitYear fromDate:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)mm_month {
|
- (NSInteger)mm_month {
|
||||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitMonth fromDate:self].month;
|
return [[NSCalendar currentCalendar] component:NSCalendarUnitMonth fromDate:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)mm_day {
|
- (NSInteger)mm_day {
|
||||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitDay fromDate:self].day;
|
return [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
14
Blog/Post.m
14
Blog/Post.m
|
|
@ -81,7 +81,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqualToPost:(Post *)other {
|
- (BOOL)isEqualToPost:(Post *)other {
|
||||||
return [self.objectID isEqual:other.objectID];
|
return [self.objectID isEqualToString:other.objectID]
|
||||||
|
&& [self.path isEqualToString:other.path]
|
||||||
|
&& [self.title isEqualToString:other.title]
|
||||||
|
&& [self.body isEqualToString:other.body]
|
||||||
|
&& self.draft == other.draft
|
||||||
|
&& ((!self.url && !other.url) || [self.url isEqual:other.url]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)object {
|
- (BOOL)isEqual:(id)object {
|
||||||
|
|
@ -139,11 +144,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)path {
|
- (NSString *)path {
|
||||||
if (!_path && self.slug) {
|
if (!_path) {
|
||||||
if (self.draft) {
|
if (self.draft) {
|
||||||
_path = [NSString stringWithFormat:@"/drafts/%@", self.slug];
|
_path = [NSString stringWithFormat:@"/posts/drafts/%@", self.objectID];
|
||||||
}
|
}
|
||||||
else if (self.date) {
|
else {
|
||||||
|
NSAssert(self.slug && self.date, @"slug and date are required");
|
||||||
NSString *paddedMonth = [self paddedMonthForDate:self.date];
|
NSString *paddedMonth = [self paddedMonthForDate:self.date];
|
||||||
_path = [NSString stringWithFormat:@"/posts/%ld/%@/%@", (long)self.time.mm_year, paddedMonth, self.slug];
|
_path = [NSString stringWithFormat:@"/posts/%ld/%@/%@", (long)self.time.mm_year, paddedMonth, self.slug];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
Blog/PostCell.h
Normal file
11
Blog/PostCell.h
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 15-04-19.
|
||||||
|
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
@import UIKit;
|
||||||
|
|
||||||
|
@interface PostCell : UITableViewCell
|
||||||
|
|
||||||
|
- (void)configureWithTitle:(NSString *)title date:(NSString *)date;
|
||||||
|
|
||||||
|
@end
|
||||||
21
Blog/PostCell.m
Normal file
21
Blog/PostCell.m
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 15-04-19.
|
||||||
|
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
#import "PostCell.h"
|
||||||
|
|
||||||
|
@interface PostCell ()
|
||||||
|
|
||||||
|
@property (nonatomic, weak) IBOutlet UILabel *titleLabel;
|
||||||
|
@property (nonatomic, weak) IBOutlet UILabel *dateLabel;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation PostCell
|
||||||
|
|
||||||
|
- (void)configureWithTitle:(NSString *)title date:(NSString *)date {
|
||||||
|
self.titleLabel.text = title;
|
||||||
|
self.dateLabel.text = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
17
Blog/PreviewViewController.h
Normal file
17
Blog/PreviewViewController.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// PreviewViewController.h
|
||||||
|
// Blog
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2015-04-19.
|
||||||
|
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <PromiseKit/Promise.h>
|
||||||
|
|
||||||
|
@interface PreviewViewController : UIViewController
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSURLRequest *initialRequest;
|
||||||
|
@property (nonatomic, strong) PMKPromise *promise;
|
||||||
|
|
||||||
|
@end
|
||||||
43
Blog/PreviewViewController.m
Normal file
43
Blog/PreviewViewController.m
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// PreviewViewController.m
|
||||||
|
// Blog
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2015-04-19.
|
||||||
|
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "PreviewViewController.h"
|
||||||
|
|
||||||
|
@interface PreviewViewController () <UIWebViewDelegate>
|
||||||
|
|
||||||
|
@property (weak, nonatomic) IBOutlet UIWebView *webView;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation PreviewViewController
|
||||||
|
|
||||||
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
if (self.initialRequest) {
|
||||||
|
if (self.promise) {
|
||||||
|
self.promise.then(^{
|
||||||
|
[self.webView loadRequest:self.initialRequest];
|
||||||
|
}).finally(^{
|
||||||
|
self.promise = nil;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[self.webView loadRequest:self.initialRequest];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setInitialRequest:(NSURLRequest *)initialRequest {
|
||||||
|
_initialRequest = initialRequest;
|
||||||
|
[self.webView loadHTMLString:@"<!doctype html><html><head><title></title></head><body></body></html>" baseURL:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - UIWebViewDelegate methods
|
||||||
|
|
||||||
|
@end
|
||||||
Loading…
Reference in a new issue