mirror of
https://github.com/zhigang1992/FunctionalReactivePixels.git
synced 2026-04-30 04:54:55 +08:00
Added photo detail view model.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
5E730B141815FE97003FCB43 /* FRPFullSizePhotoViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E730B131815FE97003FCB43 /* FRPFullSizePhotoViewModel.m */; };
|
||||
5EAD1F5818173A3200C67860 /* FRPPhotoViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD1F5718173A3200C67860 /* FRPPhotoViewModel.m */; };
|
||||
5EAD1F5C18173F1500C67860 /* FRPLoginViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD1F5B18173F1500C67860 /* FRPLoginViewModel.m */; };
|
||||
5EAD1F601817418800C67860 /* FRPPhotoDetailViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EAD1F5F1817418800C67860 /* FRPPhotoDetailViewModel.m */; };
|
||||
5EBC599E180B247500B683A7 /* FRPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EBC599D180B247500B683A7 /* FRPCell.m */; };
|
||||
5EBC59A6180B2AA200B683A7 /* FRPPhotoImporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EBC59A5180B2AA200B683A7 /* FRPPhotoImporter.m */; };
|
||||
5EBC59A9180B2C4F00B683A7 /* FRPGalleryFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EBC59A8180B2C4F00B683A7 /* FRPGalleryFlowLayout.m */; };
|
||||
@@ -66,6 +67,8 @@
|
||||
5EAD1F5718173A3200C67860 /* FRPPhotoViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FRPPhotoViewModel.m; sourceTree = "<group>"; };
|
||||
5EAD1F5A18173F1500C67860 /* FRPLoginViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FRPLoginViewModel.h; sourceTree = "<group>"; };
|
||||
5EAD1F5B18173F1500C67860 /* FRPLoginViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FRPLoginViewModel.m; sourceTree = "<group>"; };
|
||||
5EAD1F5E1817418800C67860 /* FRPPhotoDetailViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FRPPhotoDetailViewModel.h; sourceTree = "<group>"; };
|
||||
5EAD1F5F1817418800C67860 /* FRPPhotoDetailViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FRPPhotoDetailViewModel.m; sourceTree = "<group>"; };
|
||||
5EBC599C180B247500B683A7 /* FRPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FRPCell.h; sourceTree = "<group>"; };
|
||||
5EBC599D180B247500B683A7 /* FRPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FRPCell.m; sourceTree = "<group>"; };
|
||||
5EBC59A0180B268600B683A7 /* FRPPhotoModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FRPPhotoModel.h; sourceTree = "<group>"; };
|
||||
@@ -131,8 +134,7 @@
|
||||
5E730B0B1815F39D003FCB43 /* Gallery */,
|
||||
5E730B111815FE81003FCB43 /* Full Size Photo */,
|
||||
5EAD1F5518173A1E00C67860 /* Photo View Controller */,
|
||||
5E595118180E21E0002F44FA /* FRPPhotoDetailViewController.h */,
|
||||
5E595119180E21E0002F44FA /* FRPPhotoDetailViewController.m */,
|
||||
5EAD1F5D1817417B00C67860 /* Photo Details */,
|
||||
5EAD1F5918173E9700C67860 /* Login */,
|
||||
);
|
||||
name = "View Controllers";
|
||||
@@ -183,6 +185,17 @@
|
||||
name = Login;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EAD1F5D1817417B00C67860 /* Photo Details */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5E595118180E21E0002F44FA /* FRPPhotoDetailViewController.h */,
|
||||
5E595119180E21E0002F44FA /* FRPPhotoDetailViewController.m */,
|
||||
5EAD1F5E1817418800C67860 /* FRPPhotoDetailViewModel.h */,
|
||||
5EAD1F5F1817418800C67860 /* FRPPhotoDetailViewModel.m */,
|
||||
);
|
||||
name = "Photo Details";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5EBC599F180B267400B683A7 /* UICollectionView Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -451,6 +464,7 @@
|
||||
5E595111180E065F002F44FA /* FRPFullSizePhotoViewController.m in Sources */,
|
||||
5EBC59A9180B2C4F00B683A7 /* FRPGalleryFlowLayout.m in Sources */,
|
||||
5EAD1F5C18173F1500C67860 /* FRPLoginViewModel.m in Sources */,
|
||||
5EAD1F601817418800C67860 /* FRPPhotoDetailViewModel.m in Sources */,
|
||||
5E59511D181219AC002F44FA /* FRPLoginViewController.m in Sources */,
|
||||
5EBE2B03180B07D0007B6BF3 /* main.m in Sources */,
|
||||
5E730B0E1815F3E4003FCB43 /* FRPGalleryViewModel.m in Sources */,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "FRPPhotoModel.h"
|
||||
#import "FRPFullSizePhotoViewModel.h"
|
||||
#import "FRPPhotoViewModel.h"
|
||||
#import "FRPPhotoDetailViewModel.h"
|
||||
|
||||
@interface FRPFullSizePhotoViewController () <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
|
||||
|
||||
@@ -55,7 +56,12 @@
|
||||
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
|
||||
@strongify(self);
|
||||
|
||||
FRPPhotoDetailViewController *viewController = [[FRPPhotoDetailViewController alloc] initWithPhotoModel:[self.pageViewController.viewControllers.firstObject photoModel]];
|
||||
FRPPhotoViewController *photoViewController = self.pageViewController.viewControllers.firstObject;
|
||||
FRPPhotoModel *photoModel = photoViewController.viewModel.photoModel;
|
||||
|
||||
FRPPhotoDetailViewModel *viewModel = [[FRPPhotoDetailViewModel alloc] initWithPhotoModel:photoModel];
|
||||
|
||||
FRPPhotoDetailViewController *viewController = [[FRPPhotoDetailViewController alloc] initWithViewModel:viewModel];
|
||||
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
|
||||
|
||||
[self presentViewController:navigationController animated:YES completion:^{
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
@property (nonatomic, readonly) NSInteger initialPhotoIndex;
|
||||
|
||||
-(NSInteger)numberOfPhotos;
|
||||
-(FRPPhotoModel *)initialPhotoModel;
|
||||
-(FRPPhotoModel *)photoModelAtIndex:(NSInteger)index;
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class FRPPhotoModel;
|
||||
@class FRPPhotoDetailViewModel;
|
||||
|
||||
@interface FRPPhotoDetailViewController : UIViewController
|
||||
|
||||
-(instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel;
|
||||
-(instancetype)initWithViewModel:(FRPPhotoDetailViewModel *)viewModel;
|
||||
|
||||
@property (nonatomic, readonly) FRPPhotoModel *photoModel;
|
||||
@property (nonatomic, readonly) FRPPhotoDetailViewModel *viewModel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,27 +11,26 @@
|
||||
#import "FRPLoginViewController.h"
|
||||
|
||||
// Model
|
||||
#import "FRPPhotoModel.h"
|
||||
#import "FRPPhotoDetailViewModel.h"
|
||||
|
||||
// Utilities
|
||||
#import "FRPPhotoImporter.h"
|
||||
#import <SVProgressHUD/SVProgressHUD.h>
|
||||
|
||||
@interface FRPPhotoDetailViewController ()
|
||||
|
||||
// Private assignment
|
||||
@property (nonatomic, strong) FRPPhotoModel *photoModel;
|
||||
@property (nonatomic, strong) FRPPhotoDetailViewModel *viewModel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FRPPhotoDetailViewController
|
||||
|
||||
-(instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel
|
||||
-(instancetype)initWithViewModel:(FRPPhotoDetailViewModel *)viewModel
|
||||
{
|
||||
self = [self init];
|
||||
if (!self) return nil;
|
||||
|
||||
self.photoModel = photoModel;
|
||||
self.viewModel = viewModel;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -43,7 +42,7 @@
|
||||
@weakify(self);
|
||||
|
||||
// Configure self
|
||||
self.title = self.photoModel.photoName;
|
||||
self.title = self.viewModel.photoName;
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil];
|
||||
self.navigationItem.rightBarButtonItem.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
|
||||
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
|
||||
@@ -61,23 +60,21 @@
|
||||
|
||||
// Configure subviews
|
||||
UILabel *ratingLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), 100)];
|
||||
RAC(ratingLabel, text) = [RACObserve(self.photoModel, rating) map:^id(id value) {
|
||||
return [NSString stringWithFormat:@"%0.2f", [value floatValue]];
|
||||
}];
|
||||
RAC(ratingLabel, text) = RACObserve(self.viewModel, photoRating);
|
||||
ratingLabel.font = [UIFont boldSystemFontOfSize:80];
|
||||
ratingLabel.textColor = [UIColor whiteColor];
|
||||
ratingLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.view addSubview:ratingLabel];
|
||||
|
||||
UILabel *photoNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(ratingLabel.frame), CGRectGetWidth(self.view.bounds), 20)];
|
||||
RAC(photoNameLabel, text) = RACObserve(self.photoModel, photoName);
|
||||
RAC(photoNameLabel, text) = RACObserve(self.viewModel, photoName);
|
||||
photoNameLabel.font = [UIFont systemFontOfSize:16];
|
||||
photoNameLabel.textColor = [UIColor whiteColor];
|
||||
photoNameLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.view addSubview:photoNameLabel];
|
||||
|
||||
UILabel *photographerNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(photoNameLabel.frame), CGRectGetWidth(self.view.bounds), 20)];
|
||||
RAC(photographerNameLabel, text) = RACObserve(self.photoModel, photographerName);
|
||||
RAC(photographerNameLabel, text) = RACObserve(self.viewModel, photographerName);
|
||||
photographerNameLabel.font = [UIFont systemFontOfSize:16];
|
||||
photographerNameLabel.textColor = [UIColor colorWithWhite:0.5f alpha:1.0f];
|
||||
photographerNameLabel.textAlignment = NSTextAlignmentCenter;
|
||||
@@ -87,14 +84,10 @@
|
||||
voteButton.frame = CGRectMake(20, CGRectGetHeight(self.view.bounds) - 44 - 20, CGRectGetWidth(self.view.bounds) - 40, 44);
|
||||
voteButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
|
||||
// Note: can't use getter keypath
|
||||
[RACObserve(self.photoModel, votedFor) subscribeNext:^(id x) {
|
||||
if ([x boolValue]) {
|
||||
[voteButton setTitle:@"Voted For!" forState:UIControlStateNormal];
|
||||
} else {
|
||||
[voteButton setTitle:@"Vote" forState:UIControlStateNormal];
|
||||
}
|
||||
[RACObserve(self.viewModel, voteButtonText) subscribeNext:^(id value) {
|
||||
[voteButton setTitle:value forState:UIControlStateNormal];
|
||||
}];
|
||||
voteButton.rac_command = [[RACCommand alloc] initWithEnabled:[RACObserve(self.photoModel, isVotedFor) not] signalBlock:^RACSignal *(id input) {
|
||||
voteButton.rac_command = [[RACCommand alloc] initWithEnabled:self.viewModel.ableToVoteSignal signalBlock:^RACSignal *(id input) {
|
||||
// Assume that we're logged in at first. We'll replace this signal later if not.
|
||||
RACSignal *authSignal = [RACSignal empty];
|
||||
|
||||
@@ -120,7 +113,8 @@
|
||||
|
||||
return [authSignal then:^RACSignal *{
|
||||
@strongify(self);
|
||||
return [FRPPhotoImporter voteForPhoto:self.photoModel];
|
||||
[self.viewModel.voteCommand execute:nil];
|
||||
return [RACSignal empty];
|
||||
}];
|
||||
}];
|
||||
[voteButton.rac_command.errors subscribeNext:^(id x) {
|
||||
|
||||
29
FRP/FRPPhotoDetailViewModel.h
Normal file
29
FRP/FRPPhotoDetailViewModel.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// FRPPhotoDetailViewModel.h
|
||||
// FRP
|
||||
//
|
||||
// Created by Ash Furrow on 10/22/2013.
|
||||
// Copyright (c) 2013 Ash Furrow. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FRPPhotoModel;
|
||||
|
||||
@interface FRPPhotoDetailViewModel : NSObject
|
||||
|
||||
-(instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel;
|
||||
|
||||
@property (nonatomic, readonly) FRPPhotoModel *photoModel;
|
||||
|
||||
@property (nonatomic, readonly) NSString *photoName;
|
||||
@property (nonatomic, readonly) NSString *photoRating;
|
||||
@property (nonatomic, readonly) NSString *photographerName;
|
||||
@property (nonatomic, readonly) NSString *voteButtonText;
|
||||
|
||||
@property (nonatomic, readonly) RACCommand *voteCommand;
|
||||
|
||||
@property (nonatomic, readonly) BOOL loggedIn;
|
||||
@property (nonatomic, readonly) RACSignal *ableToVoteSignal;
|
||||
|
||||
@end
|
||||
69
FRP/FRPPhotoDetailViewModel.m
Normal file
69
FRP/FRPPhotoDetailViewModel.m
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// FRPPhotoDetailViewModel.m
|
||||
// FRP
|
||||
//
|
||||
// Created by Ash Furrow on 10/22/2013.
|
||||
// Copyright (c) 2013 Ash Furrow. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FRPPhotoDetailViewModel.h"
|
||||
|
||||
// Model
|
||||
#import "FRPPhotoModel.h"
|
||||
|
||||
// Utilities
|
||||
#import "FRPPhotoImporter.h"
|
||||
|
||||
@interface FRPPhotoDetailViewModel ()
|
||||
|
||||
@property (nonatomic, strong) FRPPhotoModel *photoModel;
|
||||
|
||||
@property (nonatomic, strong) NSString *photoName;
|
||||
@property (nonatomic, strong) NSString *photoRating;
|
||||
@property (nonatomic, strong) NSString *photographerName;
|
||||
@property (nonatomic, strong) NSString *voteButtonText;
|
||||
|
||||
@property (nonatomic, strong) RACCommand *voteCommand;
|
||||
|
||||
@property (nonatomic, strong) RACSignal *ableToVoteSignal;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FRPPhotoDetailViewModel
|
||||
|
||||
-(instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel {
|
||||
self = [self init];
|
||||
if (!self) return nil;
|
||||
|
||||
self.photoModel = photoModel;
|
||||
|
||||
RAC(self, photoName) = RACObserve(self.photoModel, photoName);
|
||||
RAC(self, photoRating) = [RACObserve(self.photoModel, rating) map:^id(id value) {
|
||||
return [NSString stringWithFormat:@"%0.2f", [value floatValue]];
|
||||
}];
|
||||
RAC(self, photographerName) = RACObserve(self.photoModel, photographerName);
|
||||
RAC(self, voteButtonText) = [RACObserve(self.photoModel, votedFor) map:^id(id value) {
|
||||
if ([value boolValue]) {
|
||||
return @"Voted For!";
|
||||
} else {
|
||||
return @"Vote";
|
||||
}
|
||||
}];
|
||||
|
||||
@weakify(self);
|
||||
self.voteCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
|
||||
|
||||
@strongify(self);
|
||||
return [FRPPhotoImporter voteForPhoto:self.photoModel];
|
||||
}];
|
||||
|
||||
self.ableToVoteSignal = [RACObserve(self.photoModel, votedFor) not];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(BOOL)loggedIn {
|
||||
return [[PXRequest apiHelper] authMode] == PXAPIHelperModeOAuth;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
-(instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel;
|
||||
|
||||
@property (nonatomic, readonly) FRPPhotoModel *photoModel;
|
||||
@property (nonatomic, readonly) RACCommand *viewDidAppearCommand;
|
||||
@property (nonatomic, readonly) RACSignal *photoImageSignal;
|
||||
|
||||
|
||||
5
Podfile
5
Podfile
@@ -2,7 +2,7 @@ platform :ios, "7.0"
|
||||
|
||||
target "FRP" do
|
||||
|
||||
pod 'ReactiveCocoa', '2.1.3'
|
||||
pod 'ReactiveCocoa', '2.1.4'
|
||||
pod 'libextobjc', '0.3'
|
||||
pod '500px-iOS-api', '1.0.5'
|
||||
pod 'SVProgressHUD', '0.9'
|
||||
@@ -11,7 +11,7 @@ end
|
||||
|
||||
target "FRPTests" do
|
||||
|
||||
pod 'ReactiveCocoa', '2.1.3'
|
||||
pod 'ReactiveCocoa', '2.1.4'
|
||||
pod 'libextobjc', '0.3'
|
||||
pod '500px-iOS-api', '1.0.5'
|
||||
pod 'Specta', '~> 0.1.11'
|
||||
@@ -19,4 +19,3 @@ pod 'Expecta', '~> 0.2'
|
||||
pod 'OCMock', '~> 2.0.1'
|
||||
|
||||
end
|
||||
|
||||
|
||||
10
Podfile.lock
10
Podfile.lock
@@ -87,12 +87,12 @@ PODS:
|
||||
- libextobjc/UmbrellaHeader (0.3)
|
||||
- libffi (3.0.13)
|
||||
- OCMock (2.0.1)
|
||||
- ReactiveCocoa (2.1.3):
|
||||
- ReactiveCocoa (2.1.4):
|
||||
- ReactiveCocoa/Core
|
||||
- ReactiveCocoa/no-arc
|
||||
- ReactiveCocoa/Core (2.1.3):
|
||||
- ReactiveCocoa/Core (2.1.4):
|
||||
- ReactiveCocoa/no-arc
|
||||
- ReactiveCocoa/no-arc (2.1.3)
|
||||
- ReactiveCocoa/no-arc (2.1.4)
|
||||
- Specta (0.1.11)
|
||||
- SVProgressHUD (0.9)
|
||||
|
||||
@@ -101,7 +101,7 @@ DEPENDENCIES:
|
||||
- Expecta (~> 0.2)
|
||||
- libextobjc (= 0.3)
|
||||
- OCMock (~> 2.0.1)
|
||||
- ReactiveCocoa (= 2.1.3)
|
||||
- ReactiveCocoa (= 2.1.4)
|
||||
- Specta (~> 0.1.11)
|
||||
- SVProgressHUD (= 0.9)
|
||||
|
||||
@@ -111,7 +111,7 @@ SPEC CHECKSUMS:
|
||||
libextobjc: 820a79dbbbc498611e04fffd07d2d52a5588e7ac
|
||||
libffi: 64ef39353e747bb2b25e1026afd96a157bf9231c
|
||||
OCMock: f0c099603f851d07f8d7f2efe26d05da721ec43f
|
||||
ReactiveCocoa: 0142b823f4737effe6100f9fbb0a43bdcb629aae
|
||||
ReactiveCocoa: 0e8725dd3c609128144c15192f4dbdab827f54ae
|
||||
Specta: 82746c6fd70b104c5d37cfbadb7bf15a2cbc4da0
|
||||
SVProgressHUD: 03d4845ec8e64591726428a08236c6a5489d45f8
|
||||
|
||||
|
||||
Reference in New Issue
Block a user