├── Images
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── Resources
├── designs.sketch
├── logo-white.png
├── logo-white@2x.png
├── logo-white@3x.png
├── logo-gray-small.png
├── logo-gray-small@2x.png
└── logo-gray-small@3x.png
├── ZhihuDaily
├── Assets.xcassets
│ ├── Contents.json
│ ├── logo-white.imageset
│ │ ├── logo-white.png
│ │ ├── logo-white@2x.png
│ │ ├── logo-white@3x.png
│ │ └── Contents.json
│ ├── logo-gray-small.imageset
│ │ ├── logo-gray-small.png
│ │ ├── logo-gray-small@2x.png
│ │ ├── logo-gray-small@3x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Story.m
├── Timeline.m
├── StoryBody.m
├── BaseViewController.h
├── NavigationController.h
├── StartupViewController.h
├── BaseTableViewController.h
├── BaseTableViewCell.h
├── NSLock+Helpers.h
├── OperationQueue.h
├── ZhihuDaily.xcdatamodeld
│ ├── .xccurrentversion
│ └── ZhihuDaily.xcdatamodel
│ │ └── contents
├── UIImage+Helpers.h
├── ParsingOperation.h
├── CacheManagmentViewController.h
├── BaseViewController.m
├── SettingsViewController.h
├── NSString+MD5.h
├── ReaderViewController.h
├── TimelineCell.h
├── TopStoryPageController.h
├── StoryBodyParsingOperation.h
├── main.m
├── TimelineParsingOperation.h
├── NSLock+Helpers.m
├── TimelineSectionHeaderView.h
├── TimelineTableViewController.h
├── TopStoryCell.h
├── HUDManager.h
├── URLDownloadingOperation.h
├── StartupImageParsingOperation.h
├── Story.h
├── StoryBody.h
├── Timeline.h
├── TimelineDataSource.h
├── BlockOperation.h
├── ThemeManager.h
├── CacheManager.h
├── ImageDownloadingOperation.h
├── TopStoryViewController.h
├── UIImage+Helpers.m
├── TimelineViewController.h
├── NSString+MD5.m
├── OperationQueue.m
├── TimelineController.h
├── BlockOperation.m
├── BaseOperation.h
├── AppDelegate.h
├── ParsingOperation.m
├── StoryBodyParsingOperation.m
├── SettingsViewController.m
├── StartupImageParsingOperation.m
├── TimelineCell.m
├── BaseTableViewController.m
├── TimelineSectionHeaderView.m
├── URLDownloadingOperation.m
├── ThemeManager.m
├── Info.plist
├── BaseTableViewCell.m
├── TopStoryViewController.m
├── TopStoryCell.m
├── NavigationController.m
├── TopStoryPageController.m
├── BottomToastView.xib
├── TimelineParsingOperation.m
├── ImageDownloadingOperation.m
├── TimelineDataSource.m
├── CacheManagmentViewController.m
├── CacheManager.m
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── TopStoryView.xib
├── TimelineController.m
├── CentralProgressView.xib
├── ReaderViewController.m
├── HUDManager.m
├── TimelineTableViewController.m
├── AppDelegate.m
├── StartupViewController.m
├── TimelineCell.xib
├── TimelineSectionHeaderView.xib
├── StartupViewController.xib
├── BaseOperation.m
├── TimelineViewController.m
└── Settings.storyboard
├── ZhihuDaily.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── LICENSE
├── .gitignore
└── README.md
/Images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Images/1.png
--------------------------------------------------------------------------------
/Images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Images/2.png
--------------------------------------------------------------------------------
/Images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Images/3.png
--------------------------------------------------------------------------------
/Images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Images/4.png
--------------------------------------------------------------------------------
/Resources/designs.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/designs.sketch
--------------------------------------------------------------------------------
/Resources/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-white.png
--------------------------------------------------------------------------------
/Resources/logo-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-white@2x.png
--------------------------------------------------------------------------------
/Resources/logo-white@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-white@3x.png
--------------------------------------------------------------------------------
/Resources/logo-gray-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-gray-small.png
--------------------------------------------------------------------------------
/Resources/logo-gray-small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-gray-small@2x.png
--------------------------------------------------------------------------------
/Resources/logo-gray-small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/Resources/logo-gray-small@3x.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white@2x.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-white.imageset/logo-white@3x.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small@2x.png
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unixzii/ZhihuDaily-iOS/HEAD/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/logo-gray-small@3x.png
--------------------------------------------------------------------------------
/ZhihuDaily.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ZhihuDaily/Story.m:
--------------------------------------------------------------------------------
1 | //
2 | // Story.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "Story.h"
10 |
11 | @implementation Story
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/Timeline.m:
--------------------------------------------------------------------------------
1 | //
2 | // Timeline.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "Timeline.h"
10 |
11 | @implementation Timeline
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/StoryBody.m:
--------------------------------------------------------------------------------
1 | //
2 | // StoryBody.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "StoryBody.h"
10 |
11 | @implementation StoryBody
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface BaseViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/NavigationController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NavigationController : UINavigationController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/StartupViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // StartupViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface StartupViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseTableViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // BaseTableViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface BaseTableViewController : UITableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseTableViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // BaseTableViewCell.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface BaseTableViewCell : UITableViewCell
12 |
13 | - (void)applyTheme;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/NSLock+Helpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSLock+Helpers.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSLock (Helpers)
12 |
13 | - (id)withCriticalZone:(id(^)())block;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/OperationQueue.h:
--------------------------------------------------------------------------------
1 | //
2 | // OperationQueue.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface OperationQueue : NSOperationQueue
12 |
13 | + (instancetype)globalQueue;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/ZhihuDaily.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | ZhihuDaily.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ZhihuDaily/UIImage+Helpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Helpers.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIImage (Helpers)
12 |
13 | + (instancetype)imageFilledWithColor:(UIColor *)color;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/ParsingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // ParsingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseOperation.h"
10 |
11 | @interface ParsingOperation : BaseOperation
12 |
13 | @property (copy, nonatomic) NSData *data;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/ZhihuDaily.xcdatamodeld/ZhihuDaily.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ZhihuDaily/CacheManagmentViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // CacheManagmentViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseTableViewController.h"
10 |
11 | @interface CacheManagmentViewController : BaseTableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseViewController.h"
10 |
11 | @interface BaseViewController ()
12 |
13 | @end
14 |
15 | @implementation BaseViewController
16 |
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ZhihuDaily/SettingsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "BaseTableViewController.h"
11 |
12 | @interface SettingsViewController : BaseTableViewController
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/ZhihuDaily/NSString+MD5.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+MD5.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSString (MD5)
12 |
13 | @property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *stringDigestedViaMD5;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ZhihuDaily/ReaderViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Story.h"
11 |
12 | @interface ReaderViewController : UIViewController
13 |
14 | @property (weak, nonatomic) Story *story;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineCell.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "BaseTableViewCell.h"
11 | #import "Story.h"
12 |
13 | @interface TimelineCell : BaseTableViewCell
14 |
15 | - (void)loadWithStory:(Story *)story;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryPageController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryPageController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Timeline.h"
11 |
12 | @interface TopStoryPageController : UIPageViewController
13 |
14 | - (void)setTimeline:(Timeline *)timeline;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/StoryBodyParsingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // StoryBodyParsingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "ParsingOperation.h"
10 | #import "Story.h"
11 |
12 | @interface StoryBodyParsingOperation : ParsingOperation
13 |
14 | @property (weak, nonatomic) Story *story;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineParsingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineParsingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "ParsingOperation.h"
10 | #import "Timeline.h"
11 |
12 | @interface TimelineParsingOperation : ParsingOperation
13 |
14 | @property (readonly, strong, nonatomic) Timeline *timeline;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/NSLock+Helpers.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSLock+Helpers.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "NSLock+Helpers.h"
10 |
11 | @implementation NSLock (Helpers)
12 |
13 | - (id)withCriticalZone:(id(^)())block {
14 | [self lock];
15 | id obj = block();
16 | [self unlock];
17 |
18 | return obj;
19 | }
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineSectionHeaderView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineSectionHeaderView.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/23.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TimelineSectionHeaderView : UIView
12 |
13 | @property (weak, nonatomic) IBOutlet UILabel *textLabel;
14 | @property (weak, nonatomic) IBOutlet UIView *topLineView;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineTableViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineTableViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "BaseTableViewController.h"
11 |
12 | @interface TimelineTableViewController : BaseTableViewController
13 |
14 | @property (strong, nonatomic) id dataSource;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryCell.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TopStoryPageController.h"
11 |
12 | @interface TopStoryCell : UITableViewCell
13 |
14 | @property (strong, nonatomic) TopStoryPageController *pageController;
15 | @property (strong, nonatomic) UIPageControl *pageControl;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/ZhihuDaily/HUDManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // HUDManager.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HUDManager : NSObject
12 |
13 | + (instancetype)defaultManager;
14 |
15 | - (void)showToastWithString:(NSString *)string;
16 | - (void)hideToast;
17 |
18 | - (void)showProgressWithString:(NSString *)string;
19 | - (void)hideProgress;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/ZhihuDaily/URLDownloadingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // URLDownloadingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseOperation.h"
10 |
11 | @interface URLDownloadingOperation : BaseOperation
12 |
13 | @property (copy, nonatomic) NSURL *URL;
14 | @property (readonly, copy, nonatomic) NSData *data;
15 |
16 | + (instancetype)operationWithURL:(NSURL *)URL;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ZhihuDaily/StartupImageParsingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // StartupImageParsingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ParsingOperation.h"
11 |
12 | @interface StartupImageParsingOperation : ParsingOperation
13 |
14 | @property (readonly, strong, nonatomic) UIImage *image;
15 | @property (readonly, copy, nonatomic) NSString *text;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/ZhihuDaily/Story.h:
--------------------------------------------------------------------------------
1 | //
2 | // Story.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "StoryBody.h"
11 |
12 | @interface Story : NSObject
13 |
14 | @property (copy, nonatomic) NSString *title;
15 | @property (copy, nonatomic) NSURL *imageURL;
16 | @property (assign, nonatomic) NSUInteger storyId;
17 | @property (strong, nonatomic) StoryBody *body;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ZhihuDaily/StoryBody.h:
--------------------------------------------------------------------------------
1 | //
2 | // StoryBody.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface StoryBody : NSObject
12 |
13 | @property (copy, nonatomic) NSString *htmlString;
14 | @property (strong, nonatomic) NSString *cssString;
15 | @property (strong, nonatomic) NSString *imageSource;
16 | @property (copy, nonatomic) NSURL *shareURL;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ZhihuDaily/Timeline.h:
--------------------------------------------------------------------------------
1 | //
2 | // Timeline.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Story.h"
11 |
12 | @interface Timeline : NSObject
13 |
14 | @property (copy, nonatomic) NSDate *date;
15 | @property (copy, nonatomic) NSString *dateString;
16 | @property (copy, nonatomic) NSArray *stories;
17 | @property (copy, nonatomic) NSArray *topStories;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineDataSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineDataSource.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Timeline.h"
11 |
12 | @interface TimelineDataSource : NSObject
13 |
14 | @property (copy, nonatomic) NSString *cellIdentifier;
15 | @property (copy, nonatomic) NSArray *timelines;
16 |
17 | - (Story *)storyAtIndexPath:(NSIndexPath *)indexPath;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-white.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo-white.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "logo-white@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "logo-white@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/ZhihuDaily/BlockOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // BlockOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseOperation.h"
10 |
11 | @interface BlockOperation : BaseOperation
12 |
13 | @property (assign, nonatomic) dispatch_queue_t dispatchQueue;
14 | @property (copy, nonatomic) void(^block)();
15 |
16 | + (instancetype)mainQueueOperationWithBlock:(void(^)())block;
17 | + (instancetype)globalQueueOperationWithBlock:(void(^)())block;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/logo-gray-small.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo-gray-small.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "logo-gray-small@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "logo-gray-small@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/ZhihuDaily/ThemeManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeManager.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | static NSString * const kThemeManagerDidChangeThemeNotification = @"kThemeManagerDidChangeThemeNotification";
12 |
13 | @interface ThemeManager : NSObject
14 |
15 | @property (readonly, copy, nonatomic) NSString *currentThemeName;
16 |
17 | + (instancetype)defaultManager;
18 |
19 | - (void)setThemeName:(NSString *)themeName;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/ZhihuDaily/CacheManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // CacheManager.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CacheManager : NSObject
12 |
13 | + (instancetype)defaultManager;
14 |
15 | - (void)putObject:(NSObject *)object forKey:(NSString *)key;
16 | - (void)putObject:(NSObject *)object forKey:(NSString *)key toBothDiskAndMemory:(BOOL)flag;
17 |
18 | - (id)objectForKey:(NSString *)key;
19 |
20 | - (void)clearMemoryCaches;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/ZhihuDaily/ImageDownloadingOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDownloadingOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/23.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "BaseOperation.h"
11 |
12 | @interface ImageDownloadingOperation : BaseOperation
13 |
14 | @property (copy, nonatomic) NSURL *URL;
15 | @property (weak, nonatomic) UIImageView *targetImageView;
16 | @property (assign, nonatomic) BOOL cacheDisable;
17 |
18 | + (instancetype)operationWithURL:(NSURL *)URL forImageView:(UIImageView *)imageView;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Story.h"
11 |
12 | static NSString * const kTopStoryViewDidTapNotification = @"kTopStoryViewDidTapNotification";
13 | static NSString * const kTopStoryUserInfoKey = @"kTopStoryUserInfoKey";
14 |
15 | @interface TopStoryViewController : UIViewController
16 |
17 | @property (assign) NSUInteger index;
18 |
19 | - (void)loadWithStory:(Story *)story;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/ZhihuDaily/UIImage+Helpers.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Helpers.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "UIImage+Helpers.h"
10 |
11 | @implementation UIImage (Helpers)
12 |
13 | + (instancetype)imageFilledWithColor:(UIColor *)color {
14 | UIImage *result;
15 |
16 | UIGraphicsBeginImageContext(CGSizeMake(1, 1));
17 | [color setFill];
18 | CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, 1, 1));
19 | result = UIGraphicsGetImageFromCurrentImageContext();
20 | UIGraphicsEndImageContext();
21 |
22 | return result;
23 | }
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineViewController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "BaseViewController.h"
11 |
12 | static NSString * const kNeedsUpdateRefreshStateNotification = @"kNeedsUpdateRefreshStateNotification";
13 | static NSString * const kTimelineNeedsReserveNotification = @"kTimelineNeedsReserveNotification";
14 | static NSString * const kStoryShouldShowNotification = @"kStoryShouldShowNotification";
15 | static NSString * const kStoryUserInfoKey = @"kTopStoryUserInfoKey";
16 |
17 | @interface TimelineViewController : BaseViewController
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ZhihuDaily/NSString+MD5.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+MD5.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "NSString+MD5.h"
10 | #import
11 |
12 | @implementation NSString (MD5)
13 |
14 | - (NSString *)stringDigestedViaMD5 {
15 | const char * cStr = self.UTF8String;
16 | unsigned char result[CC_MD5_DIGEST_LENGTH];
17 |
18 | CC_MD5(cStr, (CC_LONG) strlen(cStr), result);
19 |
20 | NSMutableString *resultString = [[NSMutableString alloc] init];
21 |
22 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
23 | [resultString appendFormat:@"%02x", result[i]];
24 | }
25 |
26 | return [resultString copy];
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/ZhihuDaily/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/ZhihuDaily/OperationQueue.m:
--------------------------------------------------------------------------------
1 | //
2 | // OperationQueue.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "OperationQueue.h"
10 | #import "BaseOperation.h"
11 |
12 | @implementation OperationQueue
13 |
14 | + (instancetype)globalQueue {
15 | static OperationQueue *GlobalQueue = nil;
16 | static dispatch_once_t onceToken;
17 |
18 | dispatch_once(&onceToken, ^{
19 | GlobalQueue = [[OperationQueue alloc] init];
20 | });
21 |
22 | return GlobalQueue;
23 | }
24 |
25 | - (void)addOperation:(NSOperation *)op {
26 | if ([op isKindOfClass:[BaseOperation class]]) {
27 | [((BaseOperation *) op) willEnqueue:self];
28 | }
29 |
30 | [super addOperation:op];
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineController.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Timeline.h"
11 |
12 | @class TimelineController;
13 |
14 |
15 | @protocol TimelineControllerDelegate
16 |
17 | @optional
18 |
19 | - (void)timelineControllerDidFinishLoading:(TimelineController *)timelineController;
20 | - (void)timelineControllerDidFailedLoading:(TimelineController *)timelineController;
21 |
22 | @end
23 |
24 |
25 | @interface TimelineController : NSObject
26 |
27 | @property (readonly, copy, nonatomic) NSArray *timelines;
28 | @property (weak, nonatomic) id delegate;
29 |
30 | @property (readonly, assign, nonatomic) BOOL busy;
31 |
32 | - (void)reload;
33 | - (void)reserve;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/ZhihuDaily/BlockOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // BlockOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BlockOperation.h"
10 |
11 | @implementation BlockOperation
12 |
13 | + (instancetype)mainQueueOperationWithBlock:(void(^)())block {
14 | BlockOperation *op = [[BlockOperation alloc] init];
15 | op.dispatchQueue = dispatch_get_main_queue();
16 | op.block = block;
17 |
18 | return op;
19 | }
20 |
21 | + (instancetype)globalQueueOperationWithBlock:(void(^)())block {
22 | BlockOperation *op = [[BlockOperation alloc] init];
23 | op.dispatchQueue = dispatch_get_global_queue(0, 0);
24 | op.block = block;
25 |
26 | return op;
27 | }
28 |
29 | - (void)executeMain {
30 | dispatch_async(self.dispatchQueue, ^{
31 | self.block();
32 | [self finish];
33 | });
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // BaseOperation.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSInteger, OperationState) {
12 | OperationStateInitialized,
13 | OperationStatePending,
14 | OperationStateEvaluating,
15 | OperationStateReady,
16 | OperationStateExecuting,
17 | OperationStateFinished
18 | };
19 |
20 | @interface BaseOperation : NSOperation
21 |
22 | @property (readonly, assign, atomic) OperationState state;
23 | @property (readonly, strong, nonatomic) NSArray *errors;
24 | @property (readonly, assign, nonatomic) BOOL hasErrors;
25 | @property (readonly, weak, nonatomic) NSOperationQueue *parentQueue;
26 |
27 | - (void)willEnqueue:(NSOperationQueue *)queue;
28 |
29 | - (void)executeMain;
30 |
31 | - (void)finish;
32 | - (void)finishWithError:(NSError *)error;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/ZhihuDaily/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (strong, nonatomic) UIWindow *window;
15 | @property (weak, nonatomic) UIViewController *mainViewController;
16 |
17 | @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
18 | @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
19 | @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
20 | @property (readonly, assign, nonatomic) BOOL supportsCoreData;
21 |
22 | + (instancetype)sharedDelegate;
23 |
24 | - (void)saveContext;
25 | @property (NS_NONATOMIC_IOSONLY, readonly, copy) NSURL *applicationDocumentsDirectory;
26 | @property (NS_NONATOMIC_IOSONLY, readonly, copy) NSURL *applicationCachesDirectory;
27 |
28 | @end
29 |
30 |
--------------------------------------------------------------------------------
/ZhihuDaily/ParsingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // ParsingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "ParsingOperation.h"
10 | #import "URLDownloadingOperation.h"
11 |
12 | @implementation ParsingOperation
13 |
14 | - (NSData *)data {
15 | __block NSData *inputData = self->_data;
16 |
17 | if (!inputData) {
18 | [self.dependencies enumerateObjectsUsingBlock:^(NSOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
19 | if ([obj isKindOfClass:[URLDownloadingOperation class]]) {
20 | inputData = ((URLDownloadingOperation *) obj).data;
21 |
22 | if (inputData) {
23 | *stop = YES;
24 | }
25 | }
26 | }];
27 | }
28 |
29 | if (!inputData) {
30 | [self finishWithError:[NSError errorWithDomain:@"ErrorDomainInvalidParameter" code:1 userInfo:nil]];
31 | return nil;
32 | }
33 |
34 | return inputData;
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Cyandev
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 |
--------------------------------------------------------------------------------
/ZhihuDaily/StoryBodyParsingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // StoryBodyParsingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "StoryBodyParsingOperation.h"
10 | #import "StoryBody.h"
11 |
12 | @implementation StoryBodyParsingOperation
13 |
14 | - (void)executeMain {
15 | NSError *error;
16 |
17 | NSObject *obj = [NSJSONSerialization JSONObjectWithData:self.data options:kNilOptions error:&error];
18 |
19 | if (error) {
20 | [self finishWithError:error];
21 | return;
22 | }
23 |
24 | if (![obj isKindOfClass:[NSDictionary class]]) {
25 | [self finishWithError:[NSError errorWithDomain:@"ErrorDomainUnknown" code:1 userInfo:nil]];
26 | return;
27 | }
28 |
29 | self.story.body = [[StoryBody alloc] init];
30 | self.story.body.htmlString = ((NSDictionary *) obj)[@"body"];
31 | self.story.body.cssString = [((NSDictionary *) obj)[@"css"] firstObject];
32 | self.story.body.imageSource = ((NSDictionary *) obj)[@"image_source"];
33 | self.story.body.shareURL = [NSURL URLWithString:((NSDictionary *) obj)[@"share_url"]];
34 |
35 | [self finish];
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/ZhihuDaily/SettingsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "SettingsViewController.h"
10 | #import "ThemeManager.h"
11 |
12 | @interface SettingsViewController ()
13 |
14 | @property (weak, nonatomic) IBOutlet UISwitch *nightModeSwitch;
15 | @property (strong, nonatomic) IBOutlet UISwitch *noImageModeSwitch;
16 |
17 | @end
18 |
19 | @implementation SettingsViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 |
24 | self.nightModeSwitch.on = [[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"];
25 | self.noImageModeSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"NoImageMode"];
26 | }
27 |
28 | - (IBAction)nightModeSwitchDidChange:(id)sender {
29 | // 延时应用主题以避免开关动画卡顿。
30 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
31 | [[ThemeManager defaultManager] setThemeName:self.nightModeSwitch.on ? @"Dark" : @"Default"];
32 | });
33 | }
34 |
35 | - (IBAction)noImageModeSwitchDidChange:(id)sender {
36 | [[NSUserDefaults standardUserDefaults] setBool:self.noImageModeSwitch.on forKey:@"NoImageMode"];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/screenshots
54 |
--------------------------------------------------------------------------------
/ZhihuDaily/StartupImageParsingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // StartupImageParsingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "StartupImageParsingOperation.h"
10 | #import "URLDownloadingOperation.h"
11 |
12 | @interface StartupImageParsingOperation ()
13 |
14 | @property (readwrite, strong, nonatomic) UIImage *image;
15 | @property (readwrite, copy, nonatomic) NSString *text;
16 |
17 | @end
18 |
19 | @implementation StartupImageParsingOperation
20 |
21 | - (void)executeMain {
22 | NSError *error;
23 |
24 | NSObject *obj = [NSJSONSerialization JSONObjectWithData:self.data options:kNilOptions error:&error];
25 |
26 | if (error) {
27 | [self finishWithError:error];
28 | return;
29 | }
30 |
31 | if (![obj isKindOfClass:[NSDictionary class]]) {
32 | [self finishWithError:[NSError errorWithDomain:@"ErrorDomainUnknown" code:1 userInfo:nil]];
33 | return;
34 | }
35 |
36 | NSString *imgURLString = ((NSDictionary *) obj)[@"img"];
37 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgURLString]];
38 |
39 | if (imgData) {
40 | self.image = [UIImage imageWithData:imgData];
41 | }
42 |
43 | self.text = ((NSDictionary *) obj)[@"text"];
44 |
45 | [self finish];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineCell.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineCell.h"
10 | #import "OperationQueue.h"
11 | #import "ImageDownloadingOperation.h"
12 |
13 | @interface TimelineCell ()
14 |
15 | @property (strong, nonatomic) ImageDownloadingOperation *currentDownloadingOperation;
16 |
17 | @end
18 |
19 | @implementation TimelineCell
20 |
21 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
22 | if (highlighted) {
23 | self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1];
24 | } else {
25 | [self applyTheme];
26 | [super setHighlighted:highlighted animated:animated];
27 | }
28 | }
29 |
30 | - (UILabel *)textLabel {
31 | return [self viewWithTag:1];
32 | }
33 |
34 | - (UIImageView *)imageView {
35 | return [self viewWithTag:2];
36 | }
37 |
38 | - (void)loadWithStory:(Story *)story {
39 | self.textLabel.text = story.title;
40 | self.imageView.image = nil;
41 |
42 | if (self.currentDownloadingOperation) {
43 | [self.currentDownloadingOperation cancel];
44 | }
45 |
46 | self.currentDownloadingOperation = [ImageDownloadingOperation operationWithURL:story.imageURL forImageView:self.imageView];
47 |
48 | [[OperationQueue globalQueue] addOperation:self.currentDownloadingOperation];
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseTableViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // BaseTableViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseTableViewController.h"
10 | #import "ThemeManager.h"
11 |
12 | @interface BaseTableViewController ()
13 |
14 | @end
15 |
16 | @implementation BaseTableViewController
17 |
18 | - (void)viewDidLoad {
19 | [super viewDidLoad];
20 |
21 | [self applyTheme];
22 |
23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applyTheme) name:kThemeManagerDidChangeThemeNotification object:nil];
24 | }
25 |
26 | - (void)dealloc {
27 | [[NSNotificationCenter defaultCenter] removeObserver:self];
28 | }
29 |
30 | - (void)applyTheme {
31 | if ([[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"]) {
32 | self.tableView.backgroundColor = [UIColor colorWithRed:0.15 green:0.15 blue:0.2 alpha:1];
33 | self.tableView.separatorColor = [UIColor colorWithRed:0.11 green:0.11 blue:0.16 alpha:1];
34 | self.tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
35 | } else {
36 | self.tableView.backgroundColor = [UIColor colorWithRed:0.94 green:0.94 blue:0.96 alpha:1];
37 | self.tableView.separatorColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.91 alpha:1];
38 | self.tableView.indicatorStyle = UIScrollViewIndicatorStyleDefault;
39 | }
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineSectionHeaderView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineSectionHeaderView.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/23.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineSectionHeaderView.h"
10 | #import "ThemeManager.h"
11 |
12 | @interface TimelineSectionHeaderView ()
13 |
14 | @property (weak, nonatomic) IBOutlet UIVisualEffectView *visualEffectView;
15 |
16 | @end
17 |
18 | @implementation TimelineSectionHeaderView
19 |
20 | - (void)awakeFromNib {
21 | [super awakeFromNib];
22 |
23 | [self applyTheme];
24 |
25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applyTheme) name:kThemeManagerDidChangeThemeNotification object:nil];
26 | }
27 |
28 | - (void)dealloc {
29 | [[NSNotificationCenter defaultCenter] removeObserver:self];
30 | }
31 |
32 | - (void)applyTheme {
33 | if ([[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"]) {
34 | self.visualEffectView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
35 | //self.backgroundColor = [UIColor colorWithRed:0.12 green:0.12 blue:0.16 alpha:1];
36 | self.textLabel.textColor = [UIColor colorWithRed:0.62 green:0.65 blue:0.72 alpha:1];
37 | } else {
38 | self.visualEffectView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight];
39 | //self.backgroundColor = [UIColor whiteColor];
40 | self.textLabel.textColor = [UIColor lightGrayColor];
41 | }
42 | }
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZhihuDaily-iOS
2 | A minimal Zhihu daily app for iOS without any 3rd-party frameworks.
3 |
4 | ## Features
5 | * Splash screen updating
6 | * Image disk cache and cache cleaner
7 | * UI Night mode
8 | * Built-in full-featured WebView
9 |
10 | ## Screenshots
11 | #### Splash screen:
12 | 
13 | #### Timeline:
14 | 
15 | #### Night mode:
16 | 
17 | #### Reader:
18 | 
19 |
20 | ## Project Structure
21 | * Extensions (系统类扩展)
22 | * Services (应用依赖的全局服务、例如缓存、主题管理等)
23 | * Operations (一些 `NSOperation` 子类,实现了一些粒度较小的异步操作)
24 | * Models (数据模型)
25 | * Data Sources (数据源,用于将数据模型映射到视图,减少 VC 负荷)
26 | * Views (一些视图类)
27 | * View Controllers (视图控制器)
28 | * Supporting Files (应用依赖的支持文件)
29 |
30 | ## Build
31 | 本项目无任何依赖库。
32 | ```
33 | git clone https://github.com/unixzii/ZhihuDaily-iOS.git
34 | cd ./ZhihuDaily-iOS
35 | open ./ZhihuDaily.xcodeproj
36 | ```
37 | 在 Xcode 中直接编译调试即可。
38 |
39 | ## Author
40 | **Cyandev**, an UC student majored in Software Engineering.
41 | * Weibo: [@unixzii](http://weibo.com/2834711045/profile?topnav=1&wvr=6&is_all=1)
42 | * Email: , (alternative)
43 |
44 | ## License
45 | ZhihuDaily-iOS is available under the MIT license. See the LICENSE file for more info.
46 |
--------------------------------------------------------------------------------
/ZhihuDaily/URLDownloadingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // URLDownloadingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "URLDownloadingOperation.h"
10 |
11 | @interface URLDownloadingOperation ()
12 |
13 | @property (readwrite, copy, nonatomic) NSData *data;
14 | @property (strong, nonatomic) NSURLSessionDataTask *task;
15 |
16 | @end
17 |
18 | @implementation URLDownloadingOperation
19 |
20 | + (instancetype)operationWithURL:(NSURL *)URL {
21 | URLDownloadingOperation *op = [[URLDownloadingOperation alloc] init];
22 | op.URL = URL;
23 |
24 | return op;
25 | }
26 |
27 | - (void)executeMain {
28 | __weak typeof(URLDownloadingOperation) *weakSelf = self;
29 |
30 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
31 | config.timeoutIntervalForRequest = 3.0;
32 | NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
33 |
34 | self.task = [session dataTaskWithURL:self.URL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
35 | if (error) {
36 | [weakSelf finishWithError:error];
37 | } else {
38 | weakSelf.data = data;
39 | [weakSelf finish];
40 | }
41 | }];
42 |
43 | [self.task resume];
44 | }
45 |
46 | - (void)cancel {
47 | [self.task cancel];
48 |
49 | [super cancel];
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/ZhihuDaily/ThemeManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeManager.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ThemeManager.h"
11 |
12 | @interface ThemeManager ()
13 |
14 | @property (readwrite, copy, nonatomic) NSString *currentThemeName;
15 |
16 | @end
17 |
18 | @implementation ThemeManager
19 |
20 | + (instancetype)defaultManager {
21 | static ThemeManager *DefaultManager = nil;
22 | static dispatch_once_t onceToken;
23 |
24 | dispatch_once(&onceToken, ^{
25 | DefaultManager = [[ThemeManager alloc] init];
26 | });
27 |
28 | return DefaultManager;
29 | }
30 |
31 | - (instancetype)init
32 | {
33 | self = [super init];
34 | if (self) {
35 | self.currentThemeName = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppTheme"];
36 | if (!self.currentThemeName) {
37 | self.currentThemeName = @"Default";
38 | }
39 | }
40 | return self;
41 | }
42 |
43 | - (void)setThemeName:(NSString *)themeName {
44 | self.currentThemeName = themeName;
45 |
46 | [[NSUserDefaults standardUserDefaults] setObject:themeName forKey:@"AppTheme"];
47 |
48 | [[NSNotificationCenter defaultCenter] postNotificationName:kThemeManagerDidChangeThemeNotification object:nil];
49 | [UIView transitionWithView:[UIApplication sharedApplication].keyWindow duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
50 |
51 | } completion:nil];
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/ZhihuDaily/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | zh_CN
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | LaunchScreen
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarHidden
34 |
35 | NSAppTransportSecurity
36 |
37 | NSAllowsArbitraryLoads
38 |
39 |
40 | LSApplicationCategoryType
41 | public.app-category.news
42 | CFBundleDisplayName
43 | 知乎日报
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseTableViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // BaseTableViewCell.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseTableViewCell.h"
10 | #import "ThemeManager.h"
11 |
12 | @implementation BaseTableViewCell
13 |
14 | - (void)awakeFromNib {
15 | [super awakeFromNib];
16 |
17 | [self applyTheme];
18 |
19 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applyTheme) name:kThemeManagerDidChangeThemeNotification object:nil];
20 | }
21 |
22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
23 | [super setSelected:selected animated:animated];
24 |
25 | // Configure the view for the selected state
26 | }
27 |
28 | - (void)dealloc {
29 | [[NSNotificationCenter defaultCenter] removeObserver:self];
30 | }
31 |
32 | - (void)applyTheme {
33 | if ([[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"]) {
34 | self.backgroundColor = [UIColor colorWithRed:0.12 green:0.12 blue:0.16 alpha:1];
35 | self.textLabel.textColor = [UIColor colorWithRed:0.62 green:0.65 blue:0.72 alpha:1];
36 | self.detailTextLabel.textColor = [UIColor colorWithRed:0.32 green:0.33 blue:0.4 alpha:1];
37 |
38 | UIView *selected = [[UIView alloc] init];
39 | selected.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.15];
40 | self.selectedBackgroundView = selected;
41 | } else {
42 | self.backgroundColor = [UIColor whiteColor];
43 | self.textLabel.textColor = [UIColor colorWithRed:0.37 green:0.37 blue:0.37 alpha:1];
44 | self.detailTextLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1];
45 | self.selectedBackgroundView = nil;
46 | }
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TopStoryViewController.h"
10 | #import "TimelineViewController.h"
11 | #import "OperationQueue.h"
12 | #import "ImageDownloadingOperation.h"
13 |
14 | @interface TopStoryViewController ()
15 |
16 | @property (strong, nonatomic) ImageDownloadingOperation *currentDownloadingOperation;
17 |
18 | @property (weak, nonatomic) Story *story;
19 |
20 | @property (weak, nonatomic) IBOutlet UIImageView *imageView;
21 | @property (weak, nonatomic) IBOutlet UILabel *label;
22 |
23 | @end
24 |
25 | @implementation TopStoryViewController
26 |
27 | - (void)viewDidLoad {
28 | [super viewDidLoad];
29 |
30 | UITapGestureRecognizer *aRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
31 | [self.view addGestureRecognizer:aRecognizer];
32 | }
33 |
34 | - (void)loadWithStory:(Story *)story {
35 | self.story = story;
36 |
37 | self.label.text = story.title;
38 | self.imageView.image = nil;
39 |
40 | if (self.currentDownloadingOperation) {
41 | [self.currentDownloadingOperation cancel];
42 | }
43 |
44 | self.currentDownloadingOperation = [ImageDownloadingOperation operationWithURL:story.imageURL forImageView:self.imageView];
45 |
46 | [[OperationQueue globalQueue] addOperation:self.currentDownloadingOperation];
47 | }
48 |
49 | - (void)handleTap:(id)sender {
50 | NSNotification *notification = [NSNotification notificationWithName:kStoryShouldShowNotification object:nil userInfo:@{kStoryUserInfoKey: self.story}];
51 | [[NSNotificationCenter defaultCenter] postNotification:notification];
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryCell.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TopStoryCell.h"
10 | #import "TopStoryViewController.h"
11 |
12 | @interface TopStoryCell ()
13 |
14 | @end
15 |
16 | @implementation TopStoryCell
17 |
18 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
19 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
20 | if (self) {
21 | self.pageController = [[TopStoryPageController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
22 | self.pageController.delegate = self;
23 | [self addSubview:self.pageController.view];
24 |
25 | self.pageControl = [[UIPageControl alloc] init];
26 | self.pageControl.userInteractionEnabled = false;
27 | [self addSubview:self.pageControl];
28 | }
29 | return self;
30 | }
31 |
32 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
33 | [super setSelected:selected animated:animated];
34 |
35 | // Configure the view for the selected state
36 | }
37 |
38 | - (void)layoutSubviews {
39 | [super layoutSubviews];
40 |
41 | self.pageController.view.frame = self.bounds;
42 | [self.pageController.view setNeedsLayout];
43 |
44 | self.pageControl.frame = CGRectMake(0, self.frame.size.height - 20, self.frame.size.width, 20);
45 | }
46 |
47 | - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
48 | self.pageControl.currentPage = ((TopStoryViewController *) (pageViewController.viewControllers).firstObject).index;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/ZhihuDaily/NavigationController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "NavigationController.h"
10 | #import "ThemeManager.h"
11 | #import "UIImage+Helpers.h"
12 |
13 | @interface NavigationController ()
14 |
15 | @end
16 |
17 | @implementation NavigationController
18 |
19 | - (void)viewDidLoad {
20 | [super viewDidLoad];
21 |
22 | [self applyTheme];
23 |
24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applyTheme) name:kThemeManagerDidChangeThemeNotification object:nil];
25 | }
26 |
27 | - (UIStatusBarStyle)preferredStatusBarStyle {
28 | return [[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"] ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
29 | }
30 |
31 | - (void)dealloc {
32 | [[NSNotificationCenter defaultCenter] removeObserver:self];
33 | }
34 |
35 | - (void)applyTheme {
36 | if ([[ThemeManager defaultManager].currentThemeName isEqualToString:@"Dark"]) {
37 | [self.navigationBar setBackgroundImage:[UIImage imageFilledWithColor:[UIColor colorWithRed:0.12 green:0.12 blue:0.16 alpha:1]] forBarMetrics:UIBarMetricsDefault];
38 | self.navigationBar.shadowImage = [UIImage imageFilledWithColor:[UIColor colorWithRed:0.11 green:0.11 blue:0.16 alpha:1]];
39 | self.navigationBar.tintColor = [UIColor colorWithRed:0.38 green:0.38 blue:0.44 alpha:1];
40 | self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor colorWithRed:0.74 green:0.77 blue:0.82 alpha:1]};
41 | } else {
42 | [self.navigationBar setBackgroundImage:[UIImage imageFilledWithColor:[UIColor whiteColor]] forBarMetrics:UIBarMetricsDefault];
43 | self.navigationBar.shadowImage = [UIImage imageFilledWithColor:[UIColor colorWithRed:0.9 green:0.9 blue:0.91 alpha:1]];
44 | self.navigationBar.tintColor = [UIColor colorWithRed:0.73 green:0.73 blue:0.84 alpha:1];
45 | self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor colorWithRed:0.23 green:0.25 blue:0.27 alpha:1]};
46 | }
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/ZhihuDaily/TopStoryPageController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TopStoryPageController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TopStoryPageController.h"
10 | #import "TopStoryViewController.h"
11 |
12 | @interface TopStoryPageController ()
13 |
14 | @property (weak, nonatomic) Timeline *timeline;
15 |
16 | @end
17 |
18 | @implementation TopStoryPageController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 |
23 | self.view.backgroundColor = [UIColor darkGrayColor];
24 |
25 | self.dataSource = self;
26 | }
27 |
28 | - (void)setTimeline:(Timeline *)timeline {
29 | self->_timeline = timeline;
30 |
31 | if (timeline.topStories.count > 0) {
32 | [self setViewControllers:@[[self makeViewControllerForStoryAtIndex:0]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
33 | } else {
34 | [self setViewControllers:@[] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
35 | }
36 | }
37 |
38 | - (UIViewController *)makeViewControllerForStoryAtIndex:(NSUInteger)idx {
39 | TopStoryViewController *vc = [[TopStoryViewController alloc] init];
40 | vc.view.frame = self.view.bounds;
41 | vc.index = idx;
42 | [vc loadWithStory:(self.timeline.topStories)[vc.index]];
43 |
44 | return vc;
45 | }
46 |
47 | - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
48 | if (((TopStoryViewController *) viewController).index + 1 < self.timeline.topStories.count) {
49 | return [self makeViewControllerForStoryAtIndex:((TopStoryViewController *) viewController).index + 1];
50 | } else {
51 | return nil;
52 | }
53 | }
54 |
55 | - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
56 | if (((TopStoryViewController *) viewController).index >= 1) {
57 | return [self makeViewControllerForStoryAtIndex:((TopStoryViewController *) viewController).index - 1];
58 | } else {
59 | return nil;
60 | }
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/ZhihuDaily/BottomToastView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineParsingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineParsingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineParsingOperation.h"
10 |
11 | @interface TimelineParsingOperation ()
12 |
13 | @property (readwrite, strong, nonatomic) Timeline *timeline;
14 |
15 | @end
16 |
17 | @implementation TimelineParsingOperation
18 |
19 | - (void)executeMain {
20 | NSError *error;
21 |
22 | NSObject *obj = [NSJSONSerialization JSONObjectWithData:self.data options:kNilOptions error:&error];
23 |
24 | if (error) {
25 | [self finishWithError:error];
26 | return;
27 | }
28 |
29 | if (![obj isKindOfClass:[NSDictionary class]]) {
30 | [self finishWithError:[NSError errorWithDomain:@"ErrorDomainUnknown" code:1 userInfo:nil]];
31 | return;
32 | }
33 |
34 | self.timeline = [[Timeline alloc] init];
35 |
36 | NSString *dateString = ((NSDictionary *) obj)[@"date"];
37 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
38 | formatter.dateFormat = @"yyyyMMdd";
39 |
40 | self.timeline.dateString = dateString;
41 | self.timeline.date = [formatter dateFromString:dateString];
42 |
43 | NSArray *stories = ((NSDictionary *) obj)[@"stories"];
44 | NSArray *topStories = ((NSDictionary *) obj)[@"top_stories"];
45 |
46 | [self inflateStories:stories forTop:NO];
47 | [self inflateStories:topStories forTop:YES];
48 |
49 | [self finish];
50 | }
51 |
52 | - (void)inflateStories:(NSArray *)stories forTop:(BOOL)top {
53 | NSMutableArray *storyModels = [[NSMutableArray alloc] init];
54 |
55 | [stories enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
56 | Story *story = [[Story alloc] init];
57 | story.title = obj[@"title"];
58 | story.storyId = [obj[@"id"] unsignedIntegerValue];
59 |
60 | if (top) {
61 | story.imageURL = [NSURL URLWithString:obj[@"image"]];
62 | } else {
63 | story.imageURL = [NSURL URLWithString:[obj[@"images"] firstObject]];
64 | }
65 |
66 | [storyModels addObject:story];
67 | }];
68 |
69 | if (top) {
70 | self.timeline.topStories = storyModels;
71 | } else {
72 | self.timeline.stories = storyModels;
73 | }
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/ZhihuDaily/ImageDownloadingOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDownloadingOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/23.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "ImageDownloadingOperation.h"
10 | #import "CacheManager.h"
11 |
12 | @interface ImageDownloadingOperation ()
13 |
14 | @property (strong, nonatomic) NSURLSessionDataTask *task;
15 |
16 | @end
17 |
18 | @implementation ImageDownloadingOperation
19 |
20 | + (instancetype)operationWithURL:(NSURL *)URL forImageView:(UIImageView *)imageView {
21 | ImageDownloadingOperation *op = [[ImageDownloadingOperation alloc] init];
22 | op.URL = URL;
23 | op.targetImageView = imageView;
24 |
25 | return op;
26 | }
27 |
28 | - (void)executeMain {
29 | UIImage *cachedImage = [[CacheManager defaultManager] objectForKey:self.URL.absoluteString];
30 | if (cachedImage) {
31 | dispatch_async(dispatch_get_main_queue(), ^{
32 | self.targetImageView.image = cachedImage;
33 | });
34 | [self finish];
35 | return;
36 | }
37 |
38 | __weak typeof(ImageDownloadingOperation) *weakSelf = self;
39 |
40 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
41 | config.timeoutIntervalForRequest = 3.0;
42 | NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
43 |
44 | self.task = [session dataTaskWithURL:self.URL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
45 | if (error) {
46 | [weakSelf finishWithError:error];
47 | } else {
48 | UIImage *image = [UIImage imageWithData:data];
49 | if (image) {
50 | if (!weakSelf.cacheDisable) {
51 | [[CacheManager defaultManager] putObject:image forKey:weakSelf.URL.absoluteString toBothDiskAndMemory:YES];
52 | }
53 | dispatch_async(dispatch_get_main_queue(), ^{
54 | weakSelf.targetImageView.image = image;
55 | });
56 | [weakSelf finish];
57 | } else {
58 | [weakSelf finishWithError:[NSError errorWithDomain:@"ErrorDomainUnknown" code:1 userInfo:nil]];
59 | }
60 |
61 | }
62 | }];
63 |
64 | [self.task resume];
65 | }
66 |
67 | - (void)cancel {
68 | [self.task cancel];
69 |
70 | [super cancel];
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineDataSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineDataSource.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineDataSource.h"
10 | #import "TimelineCell.h"
11 | #import "TopStoryCell.h"
12 |
13 | @interface TimelineDataSource ()
14 |
15 | @property (strong, nonatomic) NSDateFormatter *formatter;
16 |
17 | @end
18 |
19 | @implementation TimelineDataSource
20 |
21 | - (instancetype)init
22 | {
23 | self = [super init];
24 | if (self) {
25 | self.formatter = [[NSDateFormatter alloc] init];
26 | self.formatter.dateFormat = @"yyyy年MM月dd日";
27 | }
28 | return self;
29 | }
30 |
31 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
32 | if (!self.timelines) {
33 | return 0;
34 | }
35 |
36 | return self.timelines.count + 1;
37 | }
38 |
39 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
40 | if (!self.timelines) {
41 | return 0;
42 | }
43 |
44 | if (section == 0) {
45 | return 1;
46 | }
47 |
48 | return (self.timelines)[section - 1].stories.count;
49 | }
50 |
51 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
52 | if (indexPath.section == 0) {
53 | static NSString *TopStoryCellIdentifier = @"TopStoryCell";
54 |
55 | TopStoryCell *cell = [tableView dequeueReusableCellWithIdentifier:TopStoryCellIdentifier];
56 |
57 | if (!cell) {
58 | cell = [[TopStoryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TopStoryCellIdentifier];
59 | }
60 |
61 | [cell.pageController setTimeline:(self.timelines).firstObject];
62 | cell.pageControl.numberOfPages = (self.timelines).firstObject.topStories.count;
63 | cell.pageControl.currentPage = 0;
64 |
65 | return cell;
66 | }
67 |
68 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];
69 |
70 | [((TimelineCell *) cell) loadWithStory:[self storyAtIndexPath:indexPath]];
71 |
72 | return cell;
73 | }
74 |
75 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
76 | if (!self.timelines || section == 0) {
77 | return nil;
78 | }
79 |
80 | return [self.formatter stringFromDate:(self.timelines)[section - 1].date];
81 | }
82 |
83 | - (Story *)storyAtIndexPath:(NSIndexPath *)indexPath {
84 | return ((self.timelines)[indexPath.section - 1].stories)[indexPath.row];
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/ZhihuDaily/CacheManagmentViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // CacheManagmentViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "CacheManagmentViewController.h"
10 | #import "AppDelegate.h"
11 | #import "HUDManager.h"
12 |
13 | @interface CacheManagmentViewController ()
14 |
15 | @property (weak, nonatomic) IBOutlet UILabel *cacheSizeLabel;
16 | @property (weak, nonatomic) IBOutlet UITableViewCell *clearCacheCell;
17 |
18 | @end
19 |
20 | @implementation CacheManagmentViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 | }
25 |
26 | - (void)viewDidAppear:(BOOL)animated {
27 | [super viewDidAppear:animated];
28 |
29 | [self calculateCacheSize];
30 | }
31 |
32 | - (void)calculateCacheSize {
33 | dispatch_async(dispatch_get_global_queue(0, 0), ^{
34 | NSFileManager *fileManager = [NSFileManager defaultManager];
35 |
36 | NSString *cachePath = [[[AppDelegate sharedDelegate] applicationCachesDirectory].absoluteString substringFromIndex:5];
37 |
38 | __block NSUInteger totalSize = 0;
39 |
40 | [[fileManager subpathsOfDirectoryAtPath:cachePath error:nil] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
41 | totalSize += [[fileManager attributesOfItemAtPath:[cachePath stringByAppendingPathComponent:obj] error:nil] fileSize];
42 | }];
43 |
44 | NSByteCountFormatter *formatter = [[NSByteCountFormatter alloc] init];
45 | NSString *result = [formatter stringFromByteCount:totalSize];
46 |
47 | dispatch_async(dispatch_get_main_queue(), ^{
48 | self.cacheSizeLabel.text = result;
49 | });
50 | });
51 | }
52 |
53 | - (void)clearCache {
54 | dispatch_async(dispatch_get_main_queue(), ^{
55 | [[HUDManager defaultManager] showProgressWithString:@"正在清理"];
56 | });
57 |
58 | NSString *cachePath = [[[AppDelegate sharedDelegate] applicationCachesDirectory].absoluteString substringFromIndex:5];
59 |
60 | [[NSURLCache sharedURLCache] removeAllCachedResponses];
61 | [[NSFileManager defaultManager] removeItemAtPath:cachePath error:nil];
62 |
63 | // 等待一秒,让用户感觉清理了许多垃圾......
64 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
65 | [[HUDManager defaultManager] hideProgress];
66 | [[HUDManager defaultManager] showToastWithString:@"缓存已经全部清空。"];
67 | [self.navigationController popViewControllerAnimated:YES];
68 | });
69 | }
70 |
71 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
72 | if ([tableView cellForRowAtIndexPath:indexPath] == self.clearCacheCell) {
73 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
74 | dispatch_async(dispatch_get_global_queue(0, 0), ^{
75 | [self clearCache];
76 | });
77 | }
78 | }
79 |
80 | @end
81 |
--------------------------------------------------------------------------------
/ZhihuDaily/CacheManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // CacheManager.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "CacheManager.h"
10 | #import "AppDelegate.h"
11 | #import "NSLock+Helpers.h"
12 | #import "NSString+MD5.h"
13 |
14 | @interface CacheManager ()
15 |
16 | @property (strong, nonatomic) NSLock *cacheLock;
17 | @property (strong, nonatomic) NSMutableDictionary *memoryCache;
18 |
19 | @end
20 |
21 | @implementation CacheManager
22 |
23 | + (instancetype)defaultManager {
24 | static CacheManager *DefaultManager = nil;
25 | static dispatch_once_t onceToken;
26 |
27 | dispatch_once(&onceToken, ^{
28 | DefaultManager = [[CacheManager alloc] init];
29 | });
30 |
31 | return DefaultManager;
32 | }
33 |
34 | - (instancetype)init
35 | {
36 | self = [super init];
37 | if (self) {
38 | self.cacheLock = [[NSLock alloc] init];
39 | }
40 | return self;
41 | }
42 |
43 | - (NSString *)filePathForKey:(NSString *)key {
44 | NSString *basePath = [[[AppDelegate sharedDelegate] applicationCachesDirectory].absoluteString substringFromIndex:5];
45 | NSString *finalPath = [[basePath stringByAppendingPathComponent:[key stringDigestedViaMD5]] stringByAppendingString:@".bcache"];
46 |
47 | return finalPath;
48 | }
49 |
50 | - (void)putObject:(NSObject *)object forKey:(NSString *)key {
51 | [self putObject:object forKey:key toBothDiskAndMemory:NO];
52 | }
53 |
54 | - (void)putObject:(NSObject *)object forKey:(NSString *)key toBothDiskAndMemory:(BOOL)flag {
55 | [self.cacheLock withCriticalZone:^id{
56 | (self.memoryCache)[key] = object;
57 | return nil;
58 | }];
59 |
60 | if (flag) {
61 | NSMutableData *data = [[NSMutableData alloc] init];
62 | NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
63 | [archiver encodeObject:object forKey:@"CachedData"];
64 | [archiver encodeObject:[NSDate dateWithTimeIntervalSinceNow:0] forKey:@"LastModifiedDate"];
65 | [archiver finishEncoding];
66 |
67 | [data writeToFile:[self filePathForKey:key] options:kNilOptions error:nil];
68 | }
69 | }
70 |
71 | - (id)objectForKey:(NSString *)key {
72 | NSObject *obj = [self.cacheLock withCriticalZone:^id{
73 | return (self.memoryCache)[key];
74 | }];
75 |
76 | if (!obj) {
77 | NSString *filePath = [self filePathForKey:key];
78 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
79 | NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
80 | NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
81 | obj = [unarchiver decodeObjectForKey:@"CachedData"];
82 | [unarchiver finishDecoding];
83 | }
84 | }
85 |
86 | return obj;
87 | }
88 |
89 | - (void)clearMemoryCaches {
90 | [self.cacheLock withCriticalZone:^id{
91 | [self.memoryCache removeAllObjects];
92 | return nil;
93 | }];
94 | }
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/ZhihuDaily/Base.lproj/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 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineController.h"
10 | #import "OperationQueue.h"
11 | #import "URLDownloadingOperation.h"
12 | #import "TimelineParsingOperation.h"
13 | #import "BlockOperation.h"
14 |
15 | @interface TimelineController ()
16 |
17 | @property (readwrite, strong, nonatomic) NSMutableArray *internalTimelines;
18 | @property (readwrite, assign, nonatomic) BOOL busy;
19 |
20 | @end
21 |
22 | @implementation TimelineController
23 |
24 | - (NSArray *)timelines {
25 | return [self.internalTimelines copy];
26 | }
27 |
28 | - (void)reload {
29 | if (self.busy)
30 | return;
31 |
32 | if (!self.internalTimelines) {
33 | self.internalTimelines = [[NSMutableArray alloc] init];
34 | }
35 |
36 | self.busy = YES;
37 |
38 | URLDownloadingOperation *downloadOp = [URLDownloadingOperation operationWithURL:[NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"]];
39 | TimelineParsingOperation *parsingOp = [[TimelineParsingOperation alloc] init];
40 | BlockOperation *notifyOp = [BlockOperation mainQueueOperationWithBlock:^{
41 | if (parsingOp.timeline) {
42 | if (![(self.internalTimelines).firstObject.date isEqualToDate:parsingOp.timeline.date]) {
43 | [self.internalTimelines removeAllObjects];
44 | [self.internalTimelines addObject:parsingOp.timeline];
45 | } else {
46 | [self.internalTimelines setObject:parsingOp.timeline atIndexedSubscript:0];
47 | }
48 | [self.delegate timelineControllerDidFinishLoading:self];
49 | } else {
50 | [self.delegate timelineControllerDidFailedLoading:self];
51 | }
52 |
53 | self.busy = NO;
54 | }];
55 |
56 | [parsingOp addDependency:downloadOp];
57 | [notifyOp addDependency:parsingOp];
58 |
59 | [[OperationQueue globalQueue] addOperation:downloadOp];
60 | [[OperationQueue globalQueue] addOperation:parsingOp];
61 | [[OperationQueue globalQueue] addOperation:notifyOp];
62 | }
63 |
64 | - (void)reserve {
65 | if (self.busy)
66 | return;
67 |
68 | self.busy = YES;
69 |
70 | URLDownloadingOperation *downloadOp = [URLDownloadingOperation operationWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://news-at.zhihu.com/api/4/news/before/%@", (self.timelines).lastObject.dateString]]];
71 | TimelineParsingOperation *parsingOp = [[TimelineParsingOperation alloc] init];
72 | BlockOperation *notifyOp = [BlockOperation mainQueueOperationWithBlock:^{
73 | if (parsingOp.timeline) {
74 | [self.internalTimelines addObject:parsingOp.timeline];
75 | [self.delegate timelineControllerDidFinishLoading:self];
76 | } else {
77 | [self.delegate timelineControllerDidFailedLoading:self];
78 | }
79 |
80 | self.busy = NO;
81 | }];
82 |
83 | [parsingOp addDependency:downloadOp];
84 | [notifyOp addDependency:parsingOp];
85 |
86 | [[OperationQueue globalQueue] addOperation:downloadOp];
87 | [[OperationQueue globalQueue] addOperation:parsingOp];
88 | [[OperationQueue globalQueue] addOperation:notifyOp];
89 | }
90 |
91 | @end
92 |
--------------------------------------------------------------------------------
/ZhihuDaily/CentralProgressView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/ZhihuDaily/ReaderViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "ReaderViewController.h"
10 | #import "OperationQueue.h"
11 | #import "URLDownloadingOperation.h"
12 | #import "StoryBodyParsingOperation.h"
13 | #import "BlockOperation.h"
14 | #import "CacheManager.h"
15 | #import "HUDManager.h"
16 |
17 | @interface ReaderViewController ()
18 |
19 | @property (strong, nonatomic) UIWebView *webView;
20 | @property (strong, nonatomic) UIImageView *imageView;
21 |
22 | @end
23 |
24 | @implementation ReaderViewController
25 |
26 | - (void)viewDidLoad {
27 | [super viewDidLoad];
28 |
29 | self.navigationItem.title = @"文章正文";
30 |
31 | self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
32 | self.webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
33 | self.webView.scrollView.scrollIndicatorInsets = self.webView.scrollView.contentInset;
34 | self.webView.delegate = self;
35 | [self.view addSubview:self.webView];
36 |
37 | UIImageView *grayLogoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo-gray-small"]];
38 | grayLogoImageView.frame = CGRectMake((self.view.frame.size.width - 65) / 2.0, 20, 65, 65);
39 | [self.webView insertSubview:grayLogoImageView atIndex:0];
40 |
41 | self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 200)];
42 | self.imageView.clipsToBounds = YES;
43 | self.imageView.contentMode = UIViewContentModeScaleAspectFill;
44 | [self.webView.scrollView addSubview:self.imageView];
45 |
46 | dispatch_async(dispatch_get_main_queue(), ^{
47 | [self loadStoryBody];
48 | [[HUDManager defaultManager] showProgressWithString:@"正在载入"];
49 | });
50 | }
51 |
52 | - (void)loadStoryBody {
53 | URLDownloadingOperation *downloadOp = [URLDownloadingOperation operationWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://news-at.zhihu.com/api/4/news/%lu", (unsigned long) self.story.storyId]]];
54 | StoryBodyParsingOperation *parsingOp = [[StoryBodyParsingOperation alloc] init];
55 | BlockOperation *updatingOp = [BlockOperation mainQueueOperationWithBlock:^{
56 | if (parsingOp.story.body) {
57 | NSString *compoundHTML = [NSString stringWithFormat:@"%@", self.story.body.cssString, self.story.body.htmlString];
58 | [self.webView loadHTMLString:compoundHTML baseURL:nil];
59 | self.imageView.image = [[CacheManager defaultManager] objectForKey:self.story.imageURL.absoluteString];
60 | } else {
61 | [[HUDManager defaultManager] showToastWithString:@"无法加载文章,请检查网络设置。"];
62 | }
63 | [[HUDManager defaultManager] hideProgress];
64 | }];
65 |
66 | parsingOp.story = self.story;
67 |
68 | [parsingOp addDependency:downloadOp];
69 | [updatingOp addDependency:parsingOp];
70 |
71 | [[OperationQueue globalQueue] addOperation:downloadOp];
72 | [[OperationQueue globalQueue] addOperation:parsingOp];
73 | [[OperationQueue globalQueue] addOperation:updatingOp];
74 | }
75 |
76 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
77 | if (navigationType == UIWebViewNavigationTypeLinkClicked) {
78 | [[UIApplication sharedApplication] openURL:request.URL];
79 | return NO;
80 | }
81 |
82 | return YES;
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/ZhihuDaily/HUDManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // HUDManager.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "HUDManager.h"
10 |
11 | @interface HUDManager ()
12 |
13 | @property (strong, nonatomic) UIView *centralProgressView;
14 | @property (strong, nonatomic) UIView *bottomToastView;
15 |
16 | @property (strong, nonatomic) NSTimer *timer;
17 |
18 | @end
19 |
20 | @implementation HUDManager
21 |
22 | + (instancetype)defaultManager {
23 | static HUDManager *DefaultManager = nil;
24 | static dispatch_once_t onceToken;
25 |
26 | dispatch_once(&onceToken, ^{
27 | DefaultManager = [[HUDManager alloc] init];
28 | });
29 |
30 | return DefaultManager;
31 | }
32 |
33 | - (instancetype)init
34 | {
35 | self = [super init];
36 | if (self) {
37 | self.centralProgressView = [[UINib nibWithNibName:@"CentralProgressView" bundle:nil] instantiateWithOwner:self options:nil].firstObject;
38 | self.bottomToastView = [[UINib nibWithNibName:@"BottomToastView" bundle:nil] instantiateWithOwner:self options:nil].firstObject;
39 | self.timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(hideToast) userInfo:nil repeats:YES];
40 | }
41 | return self;
42 | }
43 |
44 | - (void)showToastWithString:(NSString *)string {
45 | [self.timer invalidate];
46 |
47 | UIWindow *window = [UIApplication sharedApplication].keyWindow;
48 |
49 | self.bottomToastView.frame = CGRectMake(0, window.frame.size.height - 50, window.frame.size.width, 50);
50 | self.bottomToastView.transform = CGAffineTransformMakeTranslation(0, 50);
51 | ((UILabel *) [self.bottomToastView viewWithTag:1]).text = string;
52 |
53 | [window addSubview:self.bottomToastView];
54 |
55 | [UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
56 | self.bottomToastView.transform = CGAffineTransformIdentity;
57 | } completion:nil];
58 |
59 | self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(hideToast) userInfo:nil repeats:NO];
60 | }
61 |
62 | - (void)hideToast {
63 | [UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
64 | self.bottomToastView.transform = CGAffineTransformMakeTranslation(0, 50);
65 | } completion:^(BOOL finished) {
66 | [self.bottomToastView removeFromSuperview];
67 | self.bottomToastView.transform = CGAffineTransformIdentity;
68 | }];
69 | [self.timer invalidate];
70 | }
71 |
72 | - (void)showProgressWithString:(NSString *)string {
73 | UIWindow *window = [UIApplication sharedApplication].keyWindow;
74 |
75 | self.centralProgressView.frame = CGRectMake((CGRectGetWidth(window.frame) - 160) / 2.0, (CGRectGetHeight(window.frame) - 100) / 2.0, 160, 100);
76 | self.centralProgressView.alpha = 0;
77 | ((UILabel *) [self.centralProgressView viewWithTag:1]).text = string;
78 |
79 | [window addSubview:self.centralProgressView];
80 | [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
81 |
82 | [UIView animateWithDuration:0.2 animations:^{
83 | self.centralProgressView.alpha = 1;
84 | }];
85 | }
86 |
87 | - (void)hideProgress {
88 | [UIView animateWithDuration:0.4 animations:^{
89 | self.centralProgressView.alpha = 0;
90 | } completion:^(BOOL finished) {
91 | [self.centralProgressView removeFromSuperview];
92 | [[UIApplication sharedApplication] endIgnoringInteractionEvents];
93 | }];
94 | }
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineTableViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineTableViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/22.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineTableViewController.h"
10 | #import "TimelineViewController.h"
11 | #import "TimelineDataSource.h"
12 | #import "TimelineSectionHeaderView.h"
13 | #import "ReaderViewController.h"
14 |
15 | @interface TimelineTableViewController ()
16 |
17 | @end
18 |
19 | @implementation TimelineTableViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 |
24 | self.refreshControl = [[UIRefreshControl alloc] init];
25 | }
26 |
27 | - (void)viewWillAppear:(BOOL)animated {
28 | [super viewWillAppear:animated];
29 |
30 | [self.tableView deselectRowAtIndexPath:(self.tableView).indexPathForSelectedRow animated:animated];
31 | }
32 |
33 | #pragma mark - Table view data source
34 |
35 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
36 | return [self.dataSource numberOfSectionsInTableView:tableView];
37 | }
38 |
39 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
40 | return [self.dataSource tableView:tableView numberOfRowsInSection:section];
41 | }
42 |
43 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
44 | return [self.dataSource tableView:tableView cellForRowAtIndexPath:indexPath];
45 | }
46 |
47 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
48 | return [self.dataSource tableView:tableView titleForHeaderInSection:section];
49 | }
50 |
51 | #pragma mark - Table view delegate
52 |
53 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
54 | if (section == 0) {
55 | return 0;
56 | }
57 |
58 | TimelineSectionHeaderView *headerView = [[UINib nibWithNibName:@"TimelineSectionHeaderView" bundle:nil] instantiateWithOwner:self options:nil].firstObject;
59 | headerView.textLabel.text = [self tableView:tableView titleForHeaderInSection:section];
60 | headerView.topLineView.hidden = section <= 1;
61 |
62 | return headerView;
63 | }
64 |
65 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
66 | if (section == 0) {
67 | return 0;
68 | }
69 |
70 | return 40;
71 | }
72 |
73 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
74 | if ([self.dataSource isKindOfClass:[TimelineDataSource class]]) {
75 | if (indexPath.section == 0) {
76 | return 200;
77 | }
78 | }
79 |
80 | return 90;
81 | }
82 |
83 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
84 | Story *story = [((TimelineDataSource *) self.dataSource) storyAtIndexPath:indexPath];
85 |
86 | NSNotification *notification = [NSNotification notificationWithName:kStoryShouldShowNotification object:nil userInfo:@{kStoryUserInfoKey: story}];
87 | [[NSNotificationCenter defaultCenter] postNotification:notification];
88 | }
89 |
90 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
91 | CGFloat offsetY = scrollView.contentOffset.y;
92 |
93 | if (offsetY > scrollView.contentSize.height - scrollView.frame.size.height) {
94 | [[NSNotificationCenter defaultCenter] postNotificationName:kTimelineNeedsReserveNotification object:nil];
95 | }
96 | }
97 |
98 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
99 | [[NSNotificationCenter defaultCenter] postNotificationName:kNeedsUpdateRefreshStateNotification object:nil];
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/ZhihuDaily/Base.lproj/TopStoryView.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 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ZhihuDaily/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "CacheManager.h"
11 | #import "StartupViewController.h"
12 |
13 | @interface AppDelegate ()
14 |
15 | @end
16 |
17 | @implementation AppDelegate
18 |
19 | + (instancetype)sharedDelegate {
20 | return [UIApplication sharedApplication].delegate;
21 | }
22 |
23 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
24 |
25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
26 | self.window.rootViewController = [[StartupViewController alloc] init];
27 | [self.window makeKeyAndVisible];
28 |
29 | [self initializeManagedObjectContext];
30 |
31 | return YES;
32 | }
33 |
34 | - (void)applicationWillTerminate:(UIApplication *)application {
35 | [self saveContext];
36 | }
37 |
38 | - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
39 | [[CacheManager defaultManager] clearMemoryCaches];
40 | }
41 |
42 | #pragma mark - Core Data stack
43 |
44 | @synthesize managedObjectContext = _managedObjectContext;
45 | @synthesize managedObjectModel = _managedObjectModel;
46 | @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
47 | @synthesize supportsCoreData = _supportsCoreData;
48 |
49 | - (NSManagedObjectModel *)managedObjectModel {
50 | if (_managedObjectModel == nil) {
51 | NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ZhihuDaily" withExtension:@"momd"];
52 | _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
53 | }
54 | return _managedObjectModel;
55 | }
56 |
57 | - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
58 | if (_persistentStoreCoordinator != nil) {
59 | return _persistentStoreCoordinator;
60 | }
61 |
62 | // Create the coordinator and store
63 |
64 | _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
65 | NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ZhihuDaily.sqlite"];
66 | NSError *error = nil;
67 | if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
68 | NSLog(@"Unresolved error %@, %@", error, error.userInfo);
69 | _supportsCoreData = NO;
70 | } else {
71 | _supportsCoreData = YES;
72 | }
73 |
74 | return _persistentStoreCoordinator;
75 | }
76 |
77 | - (void)initializeManagedObjectContext {
78 | NSPersistentStoreCoordinator *coordinator = self.persistentStoreCoordinator;
79 | if (!coordinator) {
80 | return;
81 | }
82 | _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
83 | _managedObjectContext.persistentStoreCoordinator = coordinator;
84 | }
85 |
86 | #pragma mark - Core Data Saving support
87 |
88 | - (void)saveContext {
89 | NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
90 | if (managedObjectContext != nil) {
91 | NSError *error = nil;
92 | if (managedObjectContext.hasChanges && ![managedObjectContext save:&error]) {
93 | NSLog(@"Unresolved error %@, %@", error, error.userInfo);
94 | _supportsCoreData = NO;
95 | }
96 | }
97 | }
98 |
99 | #pragma mark - Utility
100 |
101 | - (NSURL *)applicationDocumentsDirectory {
102 | return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;
103 | }
104 |
105 | - (NSURL *)applicationCachesDirectory {
106 | return [[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask].lastObject;
107 | }
108 |
109 | @end
110 |
--------------------------------------------------------------------------------
/ZhihuDaily/StartupViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // StartupViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "StartupViewController.h"
11 | #import "OperationQueue.h"
12 | #import "BlockOperation.h"
13 | #import "URLDownloadingOperation.h"
14 | #import "StartupImageParsingOperation.h"
15 | #import "TimelineViewController.h"
16 | #import "NavigationController.h"
17 | #import "CacheManager.h"
18 |
19 | NSString * const kStartupImageCacheKey = @"com.cyandev.ZhihuDaily/startup_image";
20 |
21 | @interface StartupViewController ()
22 |
23 | @property (assign, nonatomic) BOOL mainViewControllerShown;
24 |
25 | @property (weak, nonatomic) IBOutlet UIView *scrimView;
26 | @property (weak, nonatomic) IBOutlet UIImageView *imageView;
27 | @property (weak, nonatomic) IBOutlet UILabel *textLabel;
28 | @property (assign, nonatomic) NSTimeInterval startTime;
29 |
30 | @end
31 |
32 | @implementation StartupViewController
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 |
37 | [self makeScrimView];
38 |
39 | self.imageView.image = [[CacheManager defaultManager] objectForKey:kStartupImageCacheKey];
40 | }
41 |
42 | - (void)viewDidAppear:(BOOL)animated {
43 | [super viewDidAppear:animated];
44 |
45 | self.startTime = [NSDate dateWithTimeIntervalSinceNow:0].timeIntervalSince1970;
46 |
47 | [self beginLoadStartupImage];
48 |
49 | [UIView animateWithDuration:4 animations:^{
50 | self.imageView.transform = CGAffineTransformMakeScale(1.1, 1.1);
51 | }];
52 | }
53 |
54 | - (BOOL)prefersStatusBarHidden {
55 | return true;
56 | }
57 |
58 | - (void)makeScrimView {
59 | CAGradientLayer *gradient = [CAGradientLayer layer];
60 | gradient.frame = self.scrimView.bounds;
61 | gradient.colors = @[(id) [UIColor colorWithWhite:0 alpha:0.5].CGColor, (id) [UIColor clearColor].CGColor];
62 | gradient.startPoint = CGPointMake(0, 1.5);
63 | gradient.endPoint = CGPointMake(0, 0);
64 |
65 | [self.scrimView.layer addSublayer:gradient];
66 | }
67 |
68 | - (void)beginLoadStartupImage {
69 | void(^delayedBlock)() = ^{
70 | if (!self.mainViewControllerShown) {
71 | [self showMainViewController];
72 | }
73 | };
74 |
75 | URLDownloadingOperation *downloadOp = [URLDownloadingOperation operationWithURL:[NSURL URLWithString:@"http://news-at.zhihu.com/api/4/start-image/720*1184"]];
76 | StartupImageParsingOperation *parsingOp = [[StartupImageParsingOperation alloc] init];
77 | BlockOperation *updatingOp = [BlockOperation mainQueueOperationWithBlock:^{
78 | UIImage *image = parsingOp.image;
79 |
80 | if (!image) {
81 | self.textLabel.text = @"首屏图片拉取失败";
82 | return;
83 | }
84 |
85 | [[CacheManager defaultManager] putObject:image forKey:kStartupImageCacheKey toBothDiskAndMemory:YES];
86 |
87 | [UIView transitionWithView:self.imageView duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
88 | self.imageView.image = image;
89 | } completion:nil];
90 | self.textLabel.text = parsingOp.text;
91 | }];
92 |
93 | [parsingOp addDependency:downloadOp];
94 | [updatingOp addDependency:parsingOp];
95 |
96 | [[OperationQueue globalQueue] addOperation:downloadOp];
97 | [[OperationQueue globalQueue] addOperation:parsingOp];
98 | [[OperationQueue globalQueue] addOperation:updatingOp];
99 |
100 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), delayedBlock);
101 | }
102 |
103 | - (void)showMainViewController {
104 | self.mainViewControllerShown = YES;
105 |
106 | TimelineViewController *vc = [[TimelineViewController alloc] init];
107 | vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
108 | [AppDelegate sharedDelegate].mainViewController = vc;
109 |
110 | NavigationController *nc = [[NavigationController alloc] initWithRootViewController:vc];
111 | [self presentViewController:nc animated:YES completion:nil];
112 | }
113 |
114 | @end
115 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineSectionHeaderView.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 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/ZhihuDaily/StartupViewController.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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
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 |
--------------------------------------------------------------------------------
/ZhihuDaily/BaseOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // BaseOperation.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "BaseOperation.h"
10 | #import "NSLock+Helpers.h"
11 |
12 | @interface BaseOperation () {
13 | OperationState _state;
14 | }
15 |
16 | @property (readwrite, assign, atomic) OperationState state;
17 | @property (getter=errors, strong, nonatomic) NSArray *errors;
18 | @property (getter=hasErrors, assign, nonatomic) BOOL hasErrors;
19 | @property (readwrite, weak, nonatomic) NSOperationQueue *parentQueue;
20 |
21 | @property (strong, nonatomic) NSLock *stateLock;
22 | @property (strong, nonatomic) NSMutableArray *internalErrors;
23 |
24 | @end
25 |
26 | @implementation BaseOperation
27 |
28 | + (NSSet *)keyPathsForValuesAffectingIsReady {
29 | return [NSSet setWithObject:@"state"];
30 | }
31 |
32 | + (NSSet *)keyPathsForValuesAffectingIsExecuting {
33 | return [NSSet setWithObject:@"state"];
34 | }
35 |
36 | + (NSSet *)keyPathsForValuesAffectingIsFinished {
37 | return [NSSet setWithObject:@"state"];
38 | }
39 |
40 | - (instancetype)init
41 | {
42 | self = [super init];
43 | if (self) {
44 | self.stateLock = [[NSLock alloc] init];
45 | self.internalErrors = [[NSMutableArray alloc] init];
46 | }
47 | return self;
48 | }
49 |
50 | - (BOOL)isReady {
51 | if (self.state >= OperationStateReady) {
52 | return super.ready || self.cancelled;
53 | }
54 |
55 | if ((self.state == OperationStateInitialized || self.state == OperationStatePending) && self.cancelled) {
56 | return YES;
57 | }
58 |
59 | if (self.state == OperationStatePending) {
60 | if (super.ready) {
61 | [self evaluateConditions];
62 | }
63 | }
64 |
65 | return NO;
66 | }
67 |
68 | - (BOOL)isExecuting {
69 | return self.state == OperationStateExecuting;
70 | }
71 |
72 | - (BOOL)isFinished {
73 | return self.state == OperationStateFinished;
74 | }
75 |
76 | - (void)start {
77 | [super start];
78 |
79 | if (self.cancelled) {
80 | [self finish];
81 | }
82 | }
83 |
84 | - (void)main {
85 | if (self.state != OperationStateReady) {
86 | NSLog(@"Invalid state.");
87 | abort();
88 | }
89 |
90 | if (self.cancelled || self.hasErrors) {
91 | [self finish];
92 | } else {
93 | __block BOOL needCancel = NO;
94 |
95 | [self.dependencies enumerateObjectsUsingBlock:^(NSOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
96 | if ([obj isKindOfClass:[BaseOperation class]]) {
97 | if (((BaseOperation *) obj).hasErrors) {
98 | needCancel = YES;
99 | *stop = YES;
100 | }
101 | }
102 | }];
103 |
104 | if (needCancel) {
105 | [self finish];
106 | return;
107 | }
108 |
109 | self.state = OperationStateExecuting;
110 |
111 | [self executeMain];
112 | }
113 | }
114 |
115 | - (void)executeMain {
116 | NSLog(@"executeMain not implemented.");
117 | [self finish];
118 | }
119 |
120 | - (OperationState)state {
121 | return *(OperationState *) [[self.stateLock withCriticalZone:^id{
122 | return [NSValue valueWithPointer:&self->_state];
123 | }] pointerValue];
124 | }
125 |
126 | - (void)setState:(OperationState)state {
127 | [self willChangeValueForKey:@"state"];
128 |
129 | [self.stateLock withCriticalZone:^id{
130 | if (self->_state == OperationStateFinished) {
131 | return nil;
132 | }
133 |
134 | if (![self validateTranslationFromState:self->_state toState:state]) {
135 | NSLog(@"Invalid state translation");
136 | abort();
137 | }
138 |
139 | self->_state = state;
140 |
141 | return nil;
142 | }];
143 |
144 | [self didChangeValueForKey:@"state"];
145 | }
146 |
147 | - (NSArray *)errors {
148 | return [self.internalErrors copy];
149 | }
150 |
151 | - (BOOL)hasErrors {
152 | return self.internalErrors.count > 0;
153 | }
154 |
155 | - (BOOL)validateTranslationFromState:(OperationState)state toState:(OperationState)toState {
156 | if (state == OperationStateInitialized && toState == OperationStatePending) {
157 | return YES;
158 | }
159 |
160 | if (state == OperationStatePending && toState == OperationStateEvaluating) {
161 | return YES;
162 | }
163 |
164 | if (state == OperationStateEvaluating && toState == OperationStateReady) {
165 | return YES;
166 | }
167 |
168 | if (state == OperationStateReady && toState == OperationStateReady) {
169 | return YES;
170 | }
171 |
172 | if (state == OperationStateReady && toState == OperationStateExecuting) {
173 | return YES;
174 | }
175 |
176 | if (state == OperationStateReady && toState == OperationStateFinished) {
177 | return YES;
178 | }
179 |
180 | if (state == OperationStateExecuting && toState == OperationStateFinished) {
181 | return YES;
182 | }
183 |
184 | return NO;
185 | }
186 |
187 | - (void)evaluateConditions {
188 | self.state = OperationStateEvaluating;
189 | self.state = OperationStateReady; // TODO: not support conditions yet.
190 | }
191 |
192 | - (void)willEnqueue:(NSOperationQueue *)queue {
193 | self.state = OperationStatePending;
194 | self.parentQueue = queue;
195 | }
196 |
197 | - (void)finish {
198 | [self finishWithError:nil];
199 | }
200 |
201 | - (void)finishWithError:(NSError *)error {
202 | if (self.state == OperationStateFinished) {
203 | return;
204 | }
205 |
206 | if (error) {
207 | [self.internalErrors addObject:error];
208 | }
209 |
210 | self.state = OperationStateFinished;
211 | }
212 |
213 | @end
214 |
--------------------------------------------------------------------------------
/ZhihuDaily/TimelineViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimelineViewController.m
3 | // ZhihuDaily
4 | //
5 | // Created by 杨弘宇 on 16/5/21.
6 | // Copyright © 2016年 Cyandev. All rights reserved.
7 | //
8 |
9 | #import "TimelineViewController.h"
10 | #import "TimelineTableViewController.h"
11 | #import "TimelineDataSource.h"
12 | #import "TimelineController.h"
13 | #import "ReaderViewController.h"
14 | #import "HUDManager.h"
15 | #import "UIImage+Helpers.h"
16 |
17 | @interface TimelineViewController ()
18 |
19 | @property (strong, nonatomic) TimelineController *timelineController;
20 | @property (strong, nonatomic) TimelineDataSource *timelineDataSource;
21 |
22 | @property (strong, nonatomic) TimelineTableViewController *timelineTableViewController;
23 |
24 | @property (strong, nonatomic) UIView *progressBar;
25 |
26 | @end
27 |
28 | @implementation TimelineViewController
29 |
30 | - (void)viewDidLoad {
31 | [super viewDidLoad];
32 |
33 | self.navigationItem.title = @"知乎日报";
34 | self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"主页" style:UIBarButtonItemStylePlain target:nil action:nil];
35 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"设置" style:UIBarButtonItemStylePlain target:self action:@selector(showSettingsViewController:)];
36 |
37 | self.automaticallyAdjustsScrollViewInsets = NO;
38 |
39 | self.timelineController = [[TimelineController alloc] init];
40 | self.timelineController.delegate = self;
41 |
42 | self.timelineDataSource = [[TimelineDataSource alloc] init];
43 | self.timelineDataSource.cellIdentifier = @"Cell";
44 |
45 | self.timelineTableViewController = [[TimelineTableViewController alloc] init];
46 | [self.timelineTableViewController willMoveToParentViewController:self];
47 | [self addChildViewController:self.timelineTableViewController];
48 | self.timelineTableViewController.view.frame = self.view.bounds;
49 | [self.view addSubview:self.timelineTableViewController.view];
50 | [self.timelineTableViewController didMoveToParentViewController:self];
51 |
52 | self.timelineTableViewController.dataSource = self.timelineDataSource;
53 | [self.timelineTableViewController.tableView registerNib:[UINib nibWithNibName:@"TimelineCell" bundle:nil] forCellReuseIdentifier:@"Cell"];
54 | [self.timelineTableViewController.refreshControl addTarget:self.timelineController action:@selector(reload) forControlEvents:UIControlEventValueChanged];
55 |
56 | self.progressBar = [[UIView alloc] init];
57 | self.progressBar.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 5);
58 | self.progressBar.backgroundColor = [UIColor colorWithRed:0 green:0.51 blue:1 alpha:1];
59 | [self.view addSubview:self.progressBar];
60 |
61 | dispatch_async(dispatch_get_main_queue(), ^{
62 | [self.timelineController reload];
63 | });
64 |
65 | [self.timelineController addObserver:self forKeyPath:@"busy" options:NSKeyValueObservingOptionNew context:nil];
66 |
67 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateRefreshState) name:kNeedsUpdateRefreshStateNotification object:nil];
68 | [[NSNotificationCenter defaultCenter] addObserver:self.timelineController selector:@selector(reserve) name:kTimelineNeedsReserveNotification object:nil];
69 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showReaderViewController:) name:kStoryShouldShowNotification object:nil];
70 | }
71 |
72 | - (void)viewWillAppear:(BOOL)animated {
73 | [super viewWillAppear:animated];
74 | }
75 |
76 | - (void)viewDidAppear:(BOOL)animated {
77 | [super viewDidAppear:animated];
78 | }
79 |
80 | - (void)viewWillDisappear:(BOOL)animated {
81 | [super viewWillDisappear:animated];
82 | }
83 |
84 | - (UIStatusBarStyle)preferredStatusBarStyle {
85 | return UIStatusBarStyleLightContent;
86 | }
87 |
88 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
89 | if ([keyPath isEqualToString:@"busy"] && object == self.timelineController) {
90 | [self updateRefreshState];
91 | }
92 | }
93 |
94 | - (void)dealloc {
95 | [[NSNotificationCenter defaultCenter] removeObserver:self.timelineController];
96 | [[NSNotificationCenter defaultCenter] removeObserver:self];
97 | }
98 |
99 | - (void)showSettingsViewController:(id)sender {
100 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Settings" bundle:nil];
101 | [self showViewController:[storyboard instantiateInitialViewController] sender:sender];
102 | }
103 |
104 | - (void)showReaderViewController:(NSNotification *)aNotification {
105 | ReaderViewController *readerVC = [[ReaderViewController alloc] init];
106 | readerVC.story = (aNotification.userInfo)[kStoryUserInfoKey];
107 |
108 | [self showViewController:readerVC sender:self];
109 | }
110 |
111 | - (void)updateRefreshState {
112 | if (!self.timelineController.busy) {
113 | if (self.timelineTableViewController.refreshControl.refreshing) {
114 | [self.timelineTableViewController.refreshControl endRefreshing];
115 | }
116 |
117 | [UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:1.0 initialSpringVelocity:0 options:kNilOptions animations:^{
118 | CGRect frame = self.progressBar.frame;
119 | frame.size.width = CGRectGetWidth(self.view.frame);
120 | self.progressBar.frame = frame;
121 | } completion:^(BOOL finished) {
122 | [UIView animateWithDuration:0.4 animations:^{
123 | self.progressBar.alpha = 0;
124 | }];
125 | }];
126 | } else {
127 | __block CGRect frame = self.progressBar.frame;
128 | frame.size.width = 0;
129 | self.progressBar.frame = frame;
130 | self.progressBar.alpha = 1;
131 |
132 | [UIView animateWithDuration:0.3 animations:^{
133 | frame.size.width = 30;
134 | self.progressBar.frame = frame;
135 | }];
136 | }
137 | }
138 |
139 | #pragma mark - Timeline Controller Delegate
140 |
141 | - (void)timelineControllerDidFinishLoading:(TimelineController *)timelineController {
142 | [self.timelineTableViewController.refreshControl endRefreshing];
143 | self.timelineDataSource.timelines = timelineController.timelines;
144 | [self.timelineTableViewController.tableView reloadData];
145 | }
146 |
147 | - (void)timelineControllerDidFailedLoading:(TimelineController *)timelineController {
148 | [self.timelineTableViewController.refreshControl endRefreshing];
149 | [[HUDManager defaultManager] showToastWithString:@"无法获取最新消息,请检查网络设置。"];
150 | }
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/ZhihuDaily/Settings.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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
141 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/ZhihuDaily.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 9D8166FB1CF014DB00570DF6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8166FA1CF014DB00570DF6 /* main.m */; };
11 | 9D8166FE1CF014DB00570DF6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8166FD1CF014DB00570DF6 /* AppDelegate.m */; };
12 | 9D8167071CF014DB00570DF6 /* ZhihuDaily.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167051CF014DB00570DF6 /* ZhihuDaily.xcdatamodeld */; };
13 | 9D8167091CF014DB00570DF6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9D8167081CF014DB00570DF6 /* Assets.xcassets */; };
14 | 9D81670C1CF014DB00570DF6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9D81670A1CF014DB00570DF6 /* LaunchScreen.storyboard */; };
15 | 9D8167191CF0184900570DF6 /* TimelineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167171CF0184900570DF6 /* TimelineViewController.m */; };
16 | 9D81671F1CF0186D00570DF6 /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81671E1CF0186D00570DF6 /* BaseViewController.m */; };
17 | 9D8167221CF018D600570DF6 /* NavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167211CF018D600570DF6 /* NavigationController.m */; };
18 | 9D8167271CF01A9C00570DF6 /* StartupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167251CF01A9C00570DF6 /* StartupViewController.m */; };
19 | 9D8167281CF01A9C00570DF6 /* StartupViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9D8167261CF01A9C00570DF6 /* StartupViewController.xib */; };
20 | 9D81672C1CF0271600570DF6 /* BaseOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81672B1CF0271600570DF6 /* BaseOperation.m */; };
21 | 9D8167311CF02A6F00570DF6 /* NSLock+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167301CF02A6F00570DF6 /* NSLock+Helpers.m */; };
22 | 9D8167371CF0595200570DF6 /* OperationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167361CF0595200570DF6 /* OperationQueue.m */; };
23 | 9D81673B1CF05C8B00570DF6 /* URLDownloadingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81673A1CF05C8B00570DF6 /* URLDownloadingOperation.m */; };
24 | 9D81673F1CF05E6300570DF6 /* StartupImageParsingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81673E1CF05E6300570DF6 /* StartupImageParsingOperation.m */; };
25 | 9D8167421CF0637E00570DF6 /* BlockOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167411CF0637E00570DF6 /* BlockOperation.m */; };
26 | 9D8167461CF069DF00570DF6 /* CacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167451CF069DF00570DF6 /* CacheManager.m */; };
27 | 9D8167491CF06C1000570DF6 /* NSString+MD5.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167481CF06C1000570DF6 /* NSString+MD5.m */; };
28 | 9D81674C1CF07BEC00570DF6 /* UIImage+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81674B1CF07BEC00570DF6 /* UIImage+Helpers.m */; };
29 | 9D81674E1CF0807C00570DF6 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9D81674D1CF0807C00570DF6 /* Settings.storyboard */; };
30 | 9D8167501CF0878000570DF6 /* TimelineCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9D81674F1CF0878000570DF6 /* TimelineCell.xib */; };
31 | 9D8167541CF088EE00570DF6 /* TimelineDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167531CF088EE00570DF6 /* TimelineDataSource.m */; };
32 | 9D8167571CF08ACD00570DF6 /* Timeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167561CF08ACD00570DF6 /* Timeline.m */; };
33 | 9D81675A1CF08AD700570DF6 /* Story.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167591CF08AD700570DF6 /* Story.m */; };
34 | 9D81675D1CF08BE800570DF6 /* StoryBody.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81675C1CF08BE800570DF6 /* StoryBody.m */; };
35 | 9D8167611CF08D4A00570DF6 /* TimelineController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167601CF08D4A00570DF6 /* TimelineController.m */; };
36 | 9D8167641CF08ED100570DF6 /* TimelineParsingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167631CF08ED100570DF6 /* TimelineParsingOperation.m */; };
37 | 9D8167671CF08F7D00570DF6 /* ParsingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D8167661CF08F7D00570DF6 /* ParsingOperation.m */; };
38 | 9D81676B1CF0970300570DF6 /* TimelineCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D81676A1CF0970300570DF6 /* TimelineCell.m */; };
39 | 9DD4A2E51CF2CB6E006FE0EA /* ImageDownloadingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DD4A2E41CF2CB6E006FE0EA /* ImageDownloadingOperation.m */; };
40 | 9DD4A2E81CF2CFA6006FE0EA /* TimelineSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9DD4A2E71CF2CFA6006FE0EA /* TimelineSectionHeaderView.xib */; };
41 | 9DD4A2EB1CF2D1E1006FE0EA /* TimelineSectionHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DD4A2EA1CF2D1E1006FE0EA /* TimelineSectionHeaderView.m */; };
42 | 9DD4A2ED1CF2D475006FE0EA /* CentralProgressView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9DD4A2EC1CF2D475006FE0EA /* CentralProgressView.xib */; };
43 | 9DFCC78F1CF15380005DD5AC /* TimelineTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC78E1CF15380005DD5AC /* TimelineTableViewController.m */; };
44 | 9DFCC7921CF15851005DD5AC /* ThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7911CF15851005DD5AC /* ThemeManager.m */; };
45 | 9DFCC7961CF15BB0005DD5AC /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7951CF15BB0005DD5AC /* SettingsViewController.m */; };
46 | 9DFCC7991CF15DF7005DD5AC /* BaseTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7981CF15DF7005DD5AC /* BaseTableViewController.m */; };
47 | 9DFCC79C1CF15ECF005DD5AC /* BaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC79B1CF15ECF005DD5AC /* BaseTableViewCell.m */; };
48 | 9DFCC79F1CF1620E005DD5AC /* HUDManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC79E1CF1620E005DD5AC /* HUDManager.m */; };
49 | 9DFCC7A11CF16397005DD5AC /* BottomToastView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9DFCC7A01CF16397005DD5AC /* BottomToastView.xib */; };
50 | 9DFCC7A41CF1699A005DD5AC /* TopStoryPageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7A31CF1699A005DD5AC /* TopStoryPageController.m */; };
51 | 9DFCC7A71CF16CCC005DD5AC /* TopStoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9DFCC7A51CF16CCC005DD5AC /* TopStoryView.xib */; };
52 | 9DFCC7AB1CF16E6E005DD5AC /* TopStoryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7AA1CF16E6E005DD5AC /* TopStoryViewController.m */; };
53 | 9DFCC7AF1CF17692005DD5AC /* TopStoryCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7AD1CF17692005DD5AC /* TopStoryCell.m */; };
54 | 9DFCC7B41CF17B0F005DD5AC /* ReaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7B31CF17B0F005DD5AC /* ReaderViewController.m */; };
55 | 9DFCC7B71CF17C1E005DD5AC /* StoryBodyParsingOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7B61CF17C1E005DD5AC /* StoryBodyParsingOperation.m */; };
56 | 9DFCC7BA1CF19585005DD5AC /* CacheManagmentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DFCC7B91CF19585005DD5AC /* CacheManagmentViewController.m */; };
57 | /* End PBXBuildFile section */
58 |
59 | /* Begin PBXFileReference section */
60 | 9D8166F61CF014DB00570DF6 /* ZhihuDaily.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZhihuDaily.app; sourceTree = BUILT_PRODUCTS_DIR; };
61 | 9D8166FA1CF014DB00570DF6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
62 | 9D8166FC1CF014DB00570DF6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
63 | 9D8166FD1CF014DB00570DF6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
64 | 9D8167061CF014DB00570DF6 /* ZhihuDaily.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ZhihuDaily.xcdatamodel; sourceTree = ""; };
65 | 9D8167081CF014DB00570DF6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
66 | 9D81670B1CF014DB00570DF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
67 | 9D81670D1CF014DB00570DF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
68 | 9D8167161CF0184900570DF6 /* TimelineViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineViewController.h; sourceTree = ""; };
69 | 9D8167171CF0184900570DF6 /* TimelineViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineViewController.m; sourceTree = ""; };
70 | 9D81671D1CF0186D00570DF6 /* BaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseViewController.h; sourceTree = ""; };
71 | 9D81671E1CF0186D00570DF6 /* BaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseViewController.m; sourceTree = ""; };
72 | 9D8167201CF018D600570DF6 /* NavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationController.h; sourceTree = ""; };
73 | 9D8167211CF018D600570DF6 /* NavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationController.m; sourceTree = ""; };
74 | 9D8167241CF01A9C00570DF6 /* StartupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartupViewController.h; sourceTree = ""; };
75 | 9D8167251CF01A9C00570DF6 /* StartupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StartupViewController.m; sourceTree = ""; };
76 | 9D8167261CF01A9C00570DF6 /* StartupViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StartupViewController.xib; sourceTree = ""; };
77 | 9D81672A1CF0271600570DF6 /* BaseOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseOperation.h; sourceTree = ""; };
78 | 9D81672B1CF0271600570DF6 /* BaseOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseOperation.m; sourceTree = ""; };
79 | 9D81672F1CF02A6F00570DF6 /* NSLock+Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSLock+Helpers.h"; sourceTree = ""; };
80 | 9D8167301CF02A6F00570DF6 /* NSLock+Helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSLock+Helpers.m"; sourceTree = ""; };
81 | 9D8167351CF0595200570DF6 /* OperationQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OperationQueue.h; sourceTree = ""; };
82 | 9D8167361CF0595200570DF6 /* OperationQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OperationQueue.m; sourceTree = ""; };
83 | 9D8167391CF05C8B00570DF6 /* URLDownloadingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLDownloadingOperation.h; sourceTree = ""; };
84 | 9D81673A1CF05C8B00570DF6 /* URLDownloadingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLDownloadingOperation.m; sourceTree = ""; };
85 | 9D81673D1CF05E6300570DF6 /* StartupImageParsingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartupImageParsingOperation.h; sourceTree = ""; };
86 | 9D81673E1CF05E6300570DF6 /* StartupImageParsingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StartupImageParsingOperation.m; sourceTree = ""; };
87 | 9D8167401CF0637E00570DF6 /* BlockOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockOperation.h; sourceTree = ""; };
88 | 9D8167411CF0637E00570DF6 /* BlockOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlockOperation.m; sourceTree = ""; };
89 | 9D8167441CF069DF00570DF6 /* CacheManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheManager.h; sourceTree = ""; };
90 | 9D8167451CF069DF00570DF6 /* CacheManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CacheManager.m; sourceTree = ""; };
91 | 9D8167471CF06C1000570DF6 /* NSString+MD5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MD5.h"; sourceTree = ""; };
92 | 9D8167481CF06C1000570DF6 /* NSString+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MD5.m"; sourceTree = ""; };
93 | 9D81674A1CF07BEC00570DF6 /* UIImage+Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Helpers.h"; sourceTree = ""; };
94 | 9D81674B1CF07BEC00570DF6 /* UIImage+Helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Helpers.m"; sourceTree = ""; };
95 | 9D81674D1CF0807C00570DF6 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; };
96 | 9D81674F1CF0878000570DF6 /* TimelineCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TimelineCell.xib; sourceTree = ""; };
97 | 9D8167521CF088EE00570DF6 /* TimelineDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineDataSource.h; sourceTree = ""; };
98 | 9D8167531CF088EE00570DF6 /* TimelineDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineDataSource.m; sourceTree = ""; };
99 | 9D8167551CF08ACD00570DF6 /* Timeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timeline.h; sourceTree = ""; };
100 | 9D8167561CF08ACD00570DF6 /* Timeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Timeline.m; sourceTree = ""; };
101 | 9D8167581CF08AD700570DF6 /* Story.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Story.h; sourceTree = ""; };
102 | 9D8167591CF08AD700570DF6 /* Story.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Story.m; sourceTree = ""; };
103 | 9D81675B1CF08BE800570DF6 /* StoryBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StoryBody.h; sourceTree = ""; };
104 | 9D81675C1CF08BE800570DF6 /* StoryBody.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoryBody.m; sourceTree = ""; };
105 | 9D81675F1CF08D4A00570DF6 /* TimelineController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineController.h; sourceTree = ""; };
106 | 9D8167601CF08D4A00570DF6 /* TimelineController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineController.m; sourceTree = ""; };
107 | 9D8167621CF08ED100570DF6 /* TimelineParsingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineParsingOperation.h; sourceTree = ""; };
108 | 9D8167631CF08ED100570DF6 /* TimelineParsingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineParsingOperation.m; sourceTree = ""; };
109 | 9D8167651CF08F7D00570DF6 /* ParsingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParsingOperation.h; sourceTree = ""; };
110 | 9D8167661CF08F7D00570DF6 /* ParsingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ParsingOperation.m; sourceTree = ""; };
111 | 9D8167691CF0970300570DF6 /* TimelineCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineCell.h; sourceTree = ""; };
112 | 9D81676A1CF0970300570DF6 /* TimelineCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineCell.m; sourceTree = ""; };
113 | 9DD4A2E31CF2CB6E006FE0EA /* ImageDownloadingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageDownloadingOperation.h; sourceTree = ""; };
114 | 9DD4A2E41CF2CB6E006FE0EA /* ImageDownloadingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageDownloadingOperation.m; sourceTree = ""; };
115 | 9DD4A2E71CF2CFA6006FE0EA /* TimelineSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TimelineSectionHeaderView.xib; sourceTree = ""; };
116 | 9DD4A2E91CF2D1E1006FE0EA /* TimelineSectionHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineSectionHeaderView.h; sourceTree = ""; };
117 | 9DD4A2EA1CF2D1E1006FE0EA /* TimelineSectionHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineSectionHeaderView.m; sourceTree = ""; };
118 | 9DD4A2EC1CF2D475006FE0EA /* CentralProgressView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CentralProgressView.xib; sourceTree = ""; };
119 | 9DFCC78D1CF15380005DD5AC /* TimelineTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineTableViewController.h; sourceTree = ""; };
120 | 9DFCC78E1CF15380005DD5AC /* TimelineTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimelineTableViewController.m; sourceTree = ""; };
121 | 9DFCC7901CF15851005DD5AC /* ThemeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThemeManager.h; sourceTree = ""; };
122 | 9DFCC7911CF15851005DD5AC /* ThemeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeManager.m; sourceTree = ""; };
123 | 9DFCC7941CF15BB0005DD5AC /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; };
124 | 9DFCC7951CF15BB0005DD5AC /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; };
125 | 9DFCC7971CF15DF7005DD5AC /* BaseTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTableViewController.h; sourceTree = ""; };
126 | 9DFCC7981CF15DF7005DD5AC /* BaseTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseTableViewController.m; sourceTree = ""; };
127 | 9DFCC79A1CF15ECF005DD5AC /* BaseTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTableViewCell.h; sourceTree = ""; };
128 | 9DFCC79B1CF15ECF005DD5AC /* BaseTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseTableViewCell.m; sourceTree = ""; };
129 | 9DFCC79D1CF1620E005DD5AC /* HUDManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HUDManager.h; sourceTree = ""; };
130 | 9DFCC79E1CF1620E005DD5AC /* HUDManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HUDManager.m; sourceTree = ""; };
131 | 9DFCC7A01CF16397005DD5AC /* BottomToastView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BottomToastView.xib; sourceTree = ""; };
132 | 9DFCC7A21CF1699A005DD5AC /* TopStoryPageController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopStoryPageController.h; sourceTree = ""; };
133 | 9DFCC7A31CF1699A005DD5AC /* TopStoryPageController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopStoryPageController.m; sourceTree = ""; };
134 | 9DFCC7A61CF16CCC005DD5AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/TopStoryView.xib; sourceTree = ""; };
135 | 9DFCC7A91CF16E6E005DD5AC /* TopStoryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopStoryViewController.h; sourceTree = ""; };
136 | 9DFCC7AA1CF16E6E005DD5AC /* TopStoryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopStoryViewController.m; sourceTree = ""; };
137 | 9DFCC7AC1CF17692005DD5AC /* TopStoryCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopStoryCell.h; sourceTree = ""; };
138 | 9DFCC7AD1CF17692005DD5AC /* TopStoryCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopStoryCell.m; sourceTree = ""; };
139 | 9DFCC7B21CF17B0F005DD5AC /* ReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderViewController.h; sourceTree = ""; };
140 | 9DFCC7B31CF17B0F005DD5AC /* ReaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderViewController.m; sourceTree = ""; };
141 | 9DFCC7B51CF17C1E005DD5AC /* StoryBodyParsingOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StoryBodyParsingOperation.h; sourceTree = ""; };
142 | 9DFCC7B61CF17C1E005DD5AC /* StoryBodyParsingOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoryBodyParsingOperation.m; sourceTree = ""; };
143 | 9DFCC7B81CF19585005DD5AC /* CacheManagmentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheManagmentViewController.h; sourceTree = ""; };
144 | 9DFCC7B91CF19585005DD5AC /* CacheManagmentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CacheManagmentViewController.m; sourceTree = ""; };
145 | /* End PBXFileReference section */
146 |
147 | /* Begin PBXFrameworksBuildPhase section */
148 | 9D8166F31CF014DB00570DF6 /* Frameworks */ = {
149 | isa = PBXFrameworksBuildPhase;
150 | buildActionMask = 2147483647;
151 | files = (
152 | );
153 | runOnlyForDeploymentPostprocessing = 0;
154 | };
155 | /* End PBXFrameworksBuildPhase section */
156 |
157 | /* Begin PBXGroup section */
158 | 9D8166ED1CF014DA00570DF6 = {
159 | isa = PBXGroup;
160 | children = (
161 | 9D8166F81CF014DB00570DF6 /* ZhihuDaily */,
162 | 9D8166F71CF014DB00570DF6 /* Products */,
163 | );
164 | sourceTree = "";
165 | };
166 | 9D8166F71CF014DB00570DF6 /* Products */ = {
167 | isa = PBXGroup;
168 | children = (
169 | 9D8166F61CF014DB00570DF6 /* ZhihuDaily.app */,
170 | );
171 | name = Products;
172 | sourceTree = "";
173 | };
174 | 9D8166F81CF014DB00570DF6 /* ZhihuDaily */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 9D81672E1CF02A2300570DF6 /* Extensions */,
178 | 9D8167431CF069BE00570DF6 /* Services */,
179 | 9D8167291CF026FA00570DF6 /* Operations */,
180 | 9D8167131CF015E000570DF6 /* Models */,
181 | 9D8167511CF088D500570DF6 /* Data Sources */,
182 | 9D81675E1CF08D3100570DF6 /* Controllers */,
183 | 9D8167681CF096E500570DF6 /* Views */,
184 | 9D8167141CF0181700570DF6 /* View Controllers */,
185 | 9D8166F91CF014DB00570DF6 /* Supporting Files */,
186 | );
187 | path = ZhihuDaily;
188 | sourceTree = "";
189 | };
190 | 9D8166F91CF014DB00570DF6 /* Supporting Files */ = {
191 | isa = PBXGroup;
192 | children = (
193 | 9D8167151CF0182300570DF6 /* Xibs & Storyboards */,
194 | 9D8166FC1CF014DB00570DF6 /* AppDelegate.h */,
195 | 9D8166FD1CF014DB00570DF6 /* AppDelegate.m */,
196 | 9D8167081CF014DB00570DF6 /* Assets.xcassets */,
197 | 9D81670A1CF014DB00570DF6 /* LaunchScreen.storyboard */,
198 | 9D81670D1CF014DB00570DF6 /* Info.plist */,
199 | 9D8167051CF014DB00570DF6 /* ZhihuDaily.xcdatamodeld */,
200 | 9D8166FA1CF014DB00570DF6 /* main.m */,
201 | );
202 | name = "Supporting Files";
203 | sourceTree = "";
204 | };
205 | 9D8167131CF015E000570DF6 /* Models */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 9D8167551CF08ACD00570DF6 /* Timeline.h */,
209 | 9D8167561CF08ACD00570DF6 /* Timeline.m */,
210 | 9D8167581CF08AD700570DF6 /* Story.h */,
211 | 9D8167591CF08AD700570DF6 /* Story.m */,
212 | 9D81675B1CF08BE800570DF6 /* StoryBody.h */,
213 | 9D81675C1CF08BE800570DF6 /* StoryBody.m */,
214 | );
215 | name = Models;
216 | sourceTree = "";
217 | };
218 | 9D8167141CF0181700570DF6 /* View Controllers */ = {
219 | isa = PBXGroup;
220 | children = (
221 | 9D8167231CF01A8400570DF6 /* Startup */,
222 | 9D81671C1CF0185E00570DF6 /* Common */,
223 | 9D81671B1CF0185100570DF6 /* Timeline */,
224 | 9DFCC7A81CF16E58005DD5AC /* Top Story */,
225 | 9DFCC7B11CF17A6C005DD5AC /* Reader */,
226 | 9DFCC7931CF15B94005DD5AC /* Settings */,
227 | );
228 | name = "View Controllers";
229 | sourceTree = "";
230 | };
231 | 9D8167151CF0182300570DF6 /* Xibs & Storyboards */ = {
232 | isa = PBXGroup;
233 | children = (
234 | 9D8167261CF01A9C00570DF6 /* StartupViewController.xib */,
235 | 9DFCC7A51CF16CCC005DD5AC /* TopStoryView.xib */,
236 | 9D81674F1CF0878000570DF6 /* TimelineCell.xib */,
237 | 9D81674D1CF0807C00570DF6 /* Settings.storyboard */,
238 | 9DFCC7A01CF16397005DD5AC /* BottomToastView.xib */,
239 | 9DD4A2EC1CF2D475006FE0EA /* CentralProgressView.xib */,
240 | 9DD4A2E71CF2CFA6006FE0EA /* TimelineSectionHeaderView.xib */,
241 | );
242 | name = "Xibs & Storyboards";
243 | sourceTree = "";
244 | };
245 | 9D81671B1CF0185100570DF6 /* Timeline */ = {
246 | isa = PBXGroup;
247 | children = (
248 | 9D8167161CF0184900570DF6 /* TimelineViewController.h */,
249 | 9D8167171CF0184900570DF6 /* TimelineViewController.m */,
250 | 9DFCC78D1CF15380005DD5AC /* TimelineTableViewController.h */,
251 | 9DFCC78E1CF15380005DD5AC /* TimelineTableViewController.m */,
252 | 9DFCC7A21CF1699A005DD5AC /* TopStoryPageController.h */,
253 | 9DFCC7A31CF1699A005DD5AC /* TopStoryPageController.m */,
254 | );
255 | name = Timeline;
256 | sourceTree = "";
257 | };
258 | 9D81671C1CF0185E00570DF6 /* Common */ = {
259 | isa = PBXGroup;
260 | children = (
261 | 9D81671D1CF0186D00570DF6 /* BaseViewController.h */,
262 | 9D81671E1CF0186D00570DF6 /* BaseViewController.m */,
263 | 9D8167201CF018D600570DF6 /* NavigationController.h */,
264 | 9D8167211CF018D600570DF6 /* NavigationController.m */,
265 | 9DFCC7971CF15DF7005DD5AC /* BaseTableViewController.h */,
266 | 9DFCC7981CF15DF7005DD5AC /* BaseTableViewController.m */,
267 | );
268 | name = Common;
269 | sourceTree = "";
270 | };
271 | 9D8167231CF01A8400570DF6 /* Startup */ = {
272 | isa = PBXGroup;
273 | children = (
274 | 9D8167241CF01A9C00570DF6 /* StartupViewController.h */,
275 | 9D8167251CF01A9C00570DF6 /* StartupViewController.m */,
276 | );
277 | name = Startup;
278 | sourceTree = "";
279 | };
280 | 9D8167291CF026FA00570DF6 /* Operations */ = {
281 | isa = PBXGroup;
282 | children = (
283 | 9D81672D1CF0271B00570DF6 /* Common */,
284 | 9D8167381CF05AD200570DF6 /* Networking */,
285 | 9D81673C1CF05E1B00570DF6 /* Parsing */,
286 | );
287 | name = Operations;
288 | sourceTree = "";
289 | };
290 | 9D81672D1CF0271B00570DF6 /* Common */ = {
291 | isa = PBXGroup;
292 | children = (
293 | 9D81672A1CF0271600570DF6 /* BaseOperation.h */,
294 | 9D81672B1CF0271600570DF6 /* BaseOperation.m */,
295 | 9D8167351CF0595200570DF6 /* OperationQueue.h */,
296 | 9D8167361CF0595200570DF6 /* OperationQueue.m */,
297 | 9D8167401CF0637E00570DF6 /* BlockOperation.h */,
298 | 9D8167411CF0637E00570DF6 /* BlockOperation.m */,
299 | );
300 | name = Common;
301 | sourceTree = "";
302 | };
303 | 9D81672E1CF02A2300570DF6 /* Extensions */ = {
304 | isa = PBXGroup;
305 | children = (
306 | 9D81672F1CF02A6F00570DF6 /* NSLock+Helpers.h */,
307 | 9D8167301CF02A6F00570DF6 /* NSLock+Helpers.m */,
308 | 9D8167471CF06C1000570DF6 /* NSString+MD5.h */,
309 | 9D8167481CF06C1000570DF6 /* NSString+MD5.m */,
310 | 9D81674A1CF07BEC00570DF6 /* UIImage+Helpers.h */,
311 | 9D81674B1CF07BEC00570DF6 /* UIImage+Helpers.m */,
312 | );
313 | name = Extensions;
314 | sourceTree = "";
315 | };
316 | 9D8167381CF05AD200570DF6 /* Networking */ = {
317 | isa = PBXGroup;
318 | children = (
319 | 9D8167391CF05C8B00570DF6 /* URLDownloadingOperation.h */,
320 | 9D81673A1CF05C8B00570DF6 /* URLDownloadingOperation.m */,
321 | 9DD4A2E31CF2CB6E006FE0EA /* ImageDownloadingOperation.h */,
322 | 9DD4A2E41CF2CB6E006FE0EA /* ImageDownloadingOperation.m */,
323 | );
324 | name = Networking;
325 | sourceTree = "";
326 | };
327 | 9D81673C1CF05E1B00570DF6 /* Parsing */ = {
328 | isa = PBXGroup;
329 | children = (
330 | 9D8167651CF08F7D00570DF6 /* ParsingOperation.h */,
331 | 9D8167661CF08F7D00570DF6 /* ParsingOperation.m */,
332 | 9D81673D1CF05E6300570DF6 /* StartupImageParsingOperation.h */,
333 | 9D81673E1CF05E6300570DF6 /* StartupImageParsingOperation.m */,
334 | 9D8167621CF08ED100570DF6 /* TimelineParsingOperation.h */,
335 | 9D8167631CF08ED100570DF6 /* TimelineParsingOperation.m */,
336 | 9DFCC7B51CF17C1E005DD5AC /* StoryBodyParsingOperation.h */,
337 | 9DFCC7B61CF17C1E005DD5AC /* StoryBodyParsingOperation.m */,
338 | );
339 | name = Parsing;
340 | sourceTree = "";
341 | };
342 | 9D8167431CF069BE00570DF6 /* Services */ = {
343 | isa = PBXGroup;
344 | children = (
345 | 9D8167441CF069DF00570DF6 /* CacheManager.h */,
346 | 9D8167451CF069DF00570DF6 /* CacheManager.m */,
347 | 9DFCC7901CF15851005DD5AC /* ThemeManager.h */,
348 | 9DFCC7911CF15851005DD5AC /* ThemeManager.m */,
349 | 9DFCC79D1CF1620E005DD5AC /* HUDManager.h */,
350 | 9DFCC79E1CF1620E005DD5AC /* HUDManager.m */,
351 | );
352 | name = Services;
353 | sourceTree = "";
354 | };
355 | 9D8167511CF088D500570DF6 /* Data Sources */ = {
356 | isa = PBXGroup;
357 | children = (
358 | 9D8167521CF088EE00570DF6 /* TimelineDataSource.h */,
359 | 9D8167531CF088EE00570DF6 /* TimelineDataSource.m */,
360 | );
361 | name = "Data Sources";
362 | sourceTree = "";
363 | };
364 | 9D81675E1CF08D3100570DF6 /* Controllers */ = {
365 | isa = PBXGroup;
366 | children = (
367 | 9D81675F1CF08D4A00570DF6 /* TimelineController.h */,
368 | 9D8167601CF08D4A00570DF6 /* TimelineController.m */,
369 | );
370 | name = Controllers;
371 | sourceTree = "";
372 | };
373 | 9D8167681CF096E500570DF6 /* Views */ = {
374 | isa = PBXGroup;
375 | children = (
376 | 9DD4A2E61CF2CF62006FE0EA /* Supplementaries */,
377 | 9D81676C1CF0970600570DF6 /* Cells */,
378 | );
379 | name = Views;
380 | sourceTree = "";
381 | };
382 | 9D81676C1CF0970600570DF6 /* Cells */ = {
383 | isa = PBXGroup;
384 | children = (
385 | 9DFCC79A1CF15ECF005DD5AC /* BaseTableViewCell.h */,
386 | 9DFCC79B1CF15ECF005DD5AC /* BaseTableViewCell.m */,
387 | 9D8167691CF0970300570DF6 /* TimelineCell.h */,
388 | 9D81676A1CF0970300570DF6 /* TimelineCell.m */,
389 | 9DFCC7AC1CF17692005DD5AC /* TopStoryCell.h */,
390 | 9DFCC7AD1CF17692005DD5AC /* TopStoryCell.m */,
391 | );
392 | name = Cells;
393 | sourceTree = "";
394 | };
395 | 9DD4A2E61CF2CF62006FE0EA /* Supplementaries */ = {
396 | isa = PBXGroup;
397 | children = (
398 | 9DD4A2E91CF2D1E1006FE0EA /* TimelineSectionHeaderView.h */,
399 | 9DD4A2EA1CF2D1E1006FE0EA /* TimelineSectionHeaderView.m */,
400 | );
401 | name = Supplementaries;
402 | sourceTree = "";
403 | };
404 | 9DFCC7931CF15B94005DD5AC /* Settings */ = {
405 | isa = PBXGroup;
406 | children = (
407 | 9DFCC7941CF15BB0005DD5AC /* SettingsViewController.h */,
408 | 9DFCC7951CF15BB0005DD5AC /* SettingsViewController.m */,
409 | 9DFCC7B81CF19585005DD5AC /* CacheManagmentViewController.h */,
410 | 9DFCC7B91CF19585005DD5AC /* CacheManagmentViewController.m */,
411 | );
412 | name = Settings;
413 | sourceTree = "";
414 | };
415 | 9DFCC7A81CF16E58005DD5AC /* Top Story */ = {
416 | isa = PBXGroup;
417 | children = (
418 | 9DFCC7A91CF16E6E005DD5AC /* TopStoryViewController.h */,
419 | 9DFCC7AA1CF16E6E005DD5AC /* TopStoryViewController.m */,
420 | );
421 | name = "Top Story";
422 | sourceTree = "";
423 | };
424 | 9DFCC7B11CF17A6C005DD5AC /* Reader */ = {
425 | isa = PBXGroup;
426 | children = (
427 | 9DFCC7B21CF17B0F005DD5AC /* ReaderViewController.h */,
428 | 9DFCC7B31CF17B0F005DD5AC /* ReaderViewController.m */,
429 | );
430 | name = Reader;
431 | sourceTree = "";
432 | };
433 | /* End PBXGroup section */
434 |
435 | /* Begin PBXNativeTarget section */
436 | 9D8166F51CF014DB00570DF6 /* ZhihuDaily */ = {
437 | isa = PBXNativeTarget;
438 | buildConfigurationList = 9D8167101CF014DB00570DF6 /* Build configuration list for PBXNativeTarget "ZhihuDaily" */;
439 | buildPhases = (
440 | 9D8166F21CF014DB00570DF6 /* Sources */,
441 | 9D8166F31CF014DB00570DF6 /* Frameworks */,
442 | 9D8166F41CF014DB00570DF6 /* Resources */,
443 | );
444 | buildRules = (
445 | );
446 | dependencies = (
447 | );
448 | name = ZhihuDaily;
449 | productName = ZhihuDaily;
450 | productReference = 9D8166F61CF014DB00570DF6 /* ZhihuDaily.app */;
451 | productType = "com.apple.product-type.application";
452 | };
453 | /* End PBXNativeTarget section */
454 |
455 | /* Begin PBXProject section */
456 | 9D8166EE1CF014DA00570DF6 /* Project object */ = {
457 | isa = PBXProject;
458 | attributes = {
459 | LastUpgradeCheck = 0730;
460 | ORGANIZATIONNAME = Cyandev;
461 | TargetAttributes = {
462 | 9D8166F51CF014DB00570DF6 = {
463 | CreatedOnToolsVersion = 7.3;
464 | DevelopmentTeam = S8ZP685HLU;
465 | };
466 | };
467 | };
468 | buildConfigurationList = 9D8166F11CF014DA00570DF6 /* Build configuration list for PBXProject "ZhihuDaily" */;
469 | compatibilityVersion = "Xcode 3.2";
470 | developmentRegion = English;
471 | hasScannedForEncodings = 0;
472 | knownRegions = (
473 | en,
474 | Base,
475 | );
476 | mainGroup = 9D8166ED1CF014DA00570DF6;
477 | productRefGroup = 9D8166F71CF014DB00570DF6 /* Products */;
478 | projectDirPath = "";
479 | projectRoot = "";
480 | targets = (
481 | 9D8166F51CF014DB00570DF6 /* ZhihuDaily */,
482 | );
483 | };
484 | /* End PBXProject section */
485 |
486 | /* Begin PBXResourcesBuildPhase section */
487 | 9D8166F41CF014DB00570DF6 /* Resources */ = {
488 | isa = PBXResourcesBuildPhase;
489 | buildActionMask = 2147483647;
490 | files = (
491 | 9D8167281CF01A9C00570DF6 /* StartupViewController.xib in Resources */,
492 | 9DFCC7A71CF16CCC005DD5AC /* TopStoryView.xib in Resources */,
493 | 9DFCC7A11CF16397005DD5AC /* BottomToastView.xib in Resources */,
494 | 9DD4A2ED1CF2D475006FE0EA /* CentralProgressView.xib in Resources */,
495 | 9D8167501CF0878000570DF6 /* TimelineCell.xib in Resources */,
496 | 9D81670C1CF014DB00570DF6 /* LaunchScreen.storyboard in Resources */,
497 | 9D8167091CF014DB00570DF6 /* Assets.xcassets in Resources */,
498 | 9DD4A2E81CF2CFA6006FE0EA /* TimelineSectionHeaderView.xib in Resources */,
499 | 9D81674E1CF0807C00570DF6 /* Settings.storyboard in Resources */,
500 | );
501 | runOnlyForDeploymentPostprocessing = 0;
502 | };
503 | /* End PBXResourcesBuildPhase section */
504 |
505 | /* Begin PBXSourcesBuildPhase section */
506 | 9D8166F21CF014DB00570DF6 /* Sources */ = {
507 | isa = PBXSourcesBuildPhase;
508 | buildActionMask = 2147483647;
509 | files = (
510 | 9D81673F1CF05E6300570DF6 /* StartupImageParsingOperation.m in Sources */,
511 | 9DFCC7B41CF17B0F005DD5AC /* ReaderViewController.m in Sources */,
512 | 9D8167271CF01A9C00570DF6 /* StartupViewController.m in Sources */,
513 | 9D8167671CF08F7D00570DF6 /* ParsingOperation.m in Sources */,
514 | 9DFCC7961CF15BB0005DD5AC /* SettingsViewController.m in Sources */,
515 | 9D8167461CF069DF00570DF6 /* CacheManager.m in Sources */,
516 | 9D81675A1CF08AD700570DF6 /* Story.m in Sources */,
517 | 9DFCC7AB1CF16E6E005DD5AC /* TopStoryViewController.m in Sources */,
518 | 9DFCC7AF1CF17692005DD5AC /* TopStoryCell.m in Sources */,
519 | 9D8166FE1CF014DB00570DF6 /* AppDelegate.m in Sources */,
520 | 9D81675D1CF08BE800570DF6 /* StoryBody.m in Sources */,
521 | 9D81671F1CF0186D00570DF6 /* BaseViewController.m in Sources */,
522 | 9D81676B1CF0970300570DF6 /* TimelineCell.m in Sources */,
523 | 9DFCC7991CF15DF7005DD5AC /* BaseTableViewController.m in Sources */,
524 | 9DFCC7B71CF17C1E005DD5AC /* StoryBodyParsingOperation.m in Sources */,
525 | 9DFCC79F1CF1620E005DD5AC /* HUDManager.m in Sources */,
526 | 9DFCC7A41CF1699A005DD5AC /* TopStoryPageController.m in Sources */,
527 | 9D8167371CF0595200570DF6 /* OperationQueue.m in Sources */,
528 | 9D8167191CF0184900570DF6 /* TimelineViewController.m in Sources */,
529 | 9DFCC7921CF15851005DD5AC /* ThemeManager.m in Sources */,
530 | 9D8167611CF08D4A00570DF6 /* TimelineController.m in Sources */,
531 | 9D8167221CF018D600570DF6 /* NavigationController.m in Sources */,
532 | 9D8167071CF014DB00570DF6 /* ZhihuDaily.xcdatamodeld in Sources */,
533 | 9DFCC78F1CF15380005DD5AC /* TimelineTableViewController.m in Sources */,
534 | 9D81672C1CF0271600570DF6 /* BaseOperation.m in Sources */,
535 | 9DFCC79C1CF15ECF005DD5AC /* BaseTableViewCell.m in Sources */,
536 | 9D8167641CF08ED100570DF6 /* TimelineParsingOperation.m in Sources */,
537 | 9D8167421CF0637E00570DF6 /* BlockOperation.m in Sources */,
538 | 9D81674C1CF07BEC00570DF6 /* UIImage+Helpers.m in Sources */,
539 | 9DD4A2E51CF2CB6E006FE0EA /* ImageDownloadingOperation.m in Sources */,
540 | 9D8167571CF08ACD00570DF6 /* Timeline.m in Sources */,
541 | 9D8166FB1CF014DB00570DF6 /* main.m in Sources */,
542 | 9D8167491CF06C1000570DF6 /* NSString+MD5.m in Sources */,
543 | 9DFCC7BA1CF19585005DD5AC /* CacheManagmentViewController.m in Sources */,
544 | 9D8167311CF02A6F00570DF6 /* NSLock+Helpers.m in Sources */,
545 | 9D81673B1CF05C8B00570DF6 /* URLDownloadingOperation.m in Sources */,
546 | 9DD4A2EB1CF2D1E1006FE0EA /* TimelineSectionHeaderView.m in Sources */,
547 | 9D8167541CF088EE00570DF6 /* TimelineDataSource.m in Sources */,
548 | );
549 | runOnlyForDeploymentPostprocessing = 0;
550 | };
551 | /* End PBXSourcesBuildPhase section */
552 |
553 | /* Begin PBXVariantGroup section */
554 | 9D81670A1CF014DB00570DF6 /* LaunchScreen.storyboard */ = {
555 | isa = PBXVariantGroup;
556 | children = (
557 | 9D81670B1CF014DB00570DF6 /* Base */,
558 | );
559 | name = LaunchScreen.storyboard;
560 | sourceTree = "";
561 | };
562 | 9DFCC7A51CF16CCC005DD5AC /* TopStoryView.xib */ = {
563 | isa = PBXVariantGroup;
564 | children = (
565 | 9DFCC7A61CF16CCC005DD5AC /* Base */,
566 | );
567 | name = TopStoryView.xib;
568 | sourceTree = "";
569 | };
570 | /* End PBXVariantGroup section */
571 |
572 | /* Begin XCBuildConfiguration section */
573 | 9D81670E1CF014DB00570DF6 /* Debug */ = {
574 | isa = XCBuildConfiguration;
575 | buildSettings = {
576 | ALWAYS_SEARCH_USER_PATHS = NO;
577 | CLANG_ANALYZER_NONNULL = YES;
578 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
579 | CLANG_CXX_LIBRARY = "libc++";
580 | CLANG_ENABLE_MODULES = YES;
581 | CLANG_ENABLE_OBJC_ARC = YES;
582 | CLANG_WARN_BOOL_CONVERSION = YES;
583 | CLANG_WARN_CONSTANT_CONVERSION = YES;
584 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
585 | CLANG_WARN_EMPTY_BODY = YES;
586 | CLANG_WARN_ENUM_CONVERSION = YES;
587 | CLANG_WARN_INT_CONVERSION = YES;
588 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
589 | CLANG_WARN_UNREACHABLE_CODE = YES;
590 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
591 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
592 | COPY_PHASE_STRIP = NO;
593 | DEBUG_INFORMATION_FORMAT = dwarf;
594 | ENABLE_STRICT_OBJC_MSGSEND = YES;
595 | ENABLE_TESTABILITY = YES;
596 | GCC_C_LANGUAGE_STANDARD = gnu99;
597 | GCC_DYNAMIC_NO_PIC = NO;
598 | GCC_NO_COMMON_BLOCKS = YES;
599 | GCC_OPTIMIZATION_LEVEL = 0;
600 | GCC_PREPROCESSOR_DEFINITIONS = (
601 | "DEBUG=1",
602 | "$(inherited)",
603 | );
604 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
605 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
606 | GCC_WARN_UNDECLARED_SELECTOR = YES;
607 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
608 | GCC_WARN_UNUSED_FUNCTION = YES;
609 | GCC_WARN_UNUSED_VARIABLE = YES;
610 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
611 | MTL_ENABLE_DEBUG_INFO = YES;
612 | ONLY_ACTIVE_ARCH = YES;
613 | SDKROOT = iphoneos;
614 | };
615 | name = Debug;
616 | };
617 | 9D81670F1CF014DB00570DF6 /* Release */ = {
618 | isa = XCBuildConfiguration;
619 | buildSettings = {
620 | ALWAYS_SEARCH_USER_PATHS = NO;
621 | CLANG_ANALYZER_NONNULL = YES;
622 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
623 | CLANG_CXX_LIBRARY = "libc++";
624 | CLANG_ENABLE_MODULES = YES;
625 | CLANG_ENABLE_OBJC_ARC = YES;
626 | CLANG_WARN_BOOL_CONVERSION = YES;
627 | CLANG_WARN_CONSTANT_CONVERSION = YES;
628 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
629 | CLANG_WARN_EMPTY_BODY = YES;
630 | CLANG_WARN_ENUM_CONVERSION = YES;
631 | CLANG_WARN_INT_CONVERSION = YES;
632 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
633 | CLANG_WARN_UNREACHABLE_CODE = YES;
634 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
635 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
636 | COPY_PHASE_STRIP = NO;
637 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
638 | ENABLE_NS_ASSERTIONS = NO;
639 | ENABLE_STRICT_OBJC_MSGSEND = YES;
640 | GCC_C_LANGUAGE_STANDARD = gnu99;
641 | GCC_NO_COMMON_BLOCKS = YES;
642 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
643 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
644 | GCC_WARN_UNDECLARED_SELECTOR = YES;
645 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
646 | GCC_WARN_UNUSED_FUNCTION = YES;
647 | GCC_WARN_UNUSED_VARIABLE = YES;
648 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
649 | MTL_ENABLE_DEBUG_INFO = NO;
650 | SDKROOT = iphoneos;
651 | VALIDATE_PRODUCT = YES;
652 | };
653 | name = Release;
654 | };
655 | 9D8167111CF014DB00570DF6 /* Debug */ = {
656 | isa = XCBuildConfiguration;
657 | buildSettings = {
658 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
659 | INFOPLIST_FILE = ZhihuDaily/Info.plist;
660 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
661 | PRODUCT_BUNDLE_IDENTIFIER = com.cyandev.ZhihuDaily;
662 | PRODUCT_NAME = "$(TARGET_NAME)";
663 | };
664 | name = Debug;
665 | };
666 | 9D8167121CF014DB00570DF6 /* Release */ = {
667 | isa = XCBuildConfiguration;
668 | buildSettings = {
669 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
670 | INFOPLIST_FILE = ZhihuDaily/Info.plist;
671 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
672 | PRODUCT_BUNDLE_IDENTIFIER = com.cyandev.ZhihuDaily;
673 | PRODUCT_NAME = "$(TARGET_NAME)";
674 | };
675 | name = Release;
676 | };
677 | /* End XCBuildConfiguration section */
678 |
679 | /* Begin XCConfigurationList section */
680 | 9D8166F11CF014DA00570DF6 /* Build configuration list for PBXProject "ZhihuDaily" */ = {
681 | isa = XCConfigurationList;
682 | buildConfigurations = (
683 | 9D81670E1CF014DB00570DF6 /* Debug */,
684 | 9D81670F1CF014DB00570DF6 /* Release */,
685 | );
686 | defaultConfigurationIsVisible = 0;
687 | defaultConfigurationName = Release;
688 | };
689 | 9D8167101CF014DB00570DF6 /* Build configuration list for PBXNativeTarget "ZhihuDaily" */ = {
690 | isa = XCConfigurationList;
691 | buildConfigurations = (
692 | 9D8167111CF014DB00570DF6 /* Debug */,
693 | 9D8167121CF014DB00570DF6 /* Release */,
694 | );
695 | defaultConfigurationIsVisible = 0;
696 | defaultConfigurationName = Release;
697 | };
698 | /* End XCConfigurationList section */
699 |
700 | /* Begin XCVersionGroup section */
701 | 9D8167051CF014DB00570DF6 /* ZhihuDaily.xcdatamodeld */ = {
702 | isa = XCVersionGroup;
703 | children = (
704 | 9D8167061CF014DB00570DF6 /* ZhihuDaily.xcdatamodel */,
705 | );
706 | currentVersion = 9D8167061CF014DB00570DF6 /* ZhihuDaily.xcdatamodel */;
707 | path = ZhihuDaily.xcdatamodeld;
708 | sourceTree = "";
709 | versionGroupType = wrapper.xcdatamodel;
710 | };
711 | /* End XCVersionGroup section */
712 | };
713 | rootObject = 9D8166EE1CF014DA00570DF6 /* Project object */;
714 | }
715 |
--------------------------------------------------------------------------------