├── .gitignore ├── .travis.yml ├── LZAlbum.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── LZAlbum.xccheckout └── xcshareddata │ └── xcschemes │ ├── LZAlbum.xcscheme │ └── LZAlbumTests.xcscheme ├── LZAlbum.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── LZAlbum.xccheckout ├── LZAlbum ├── Base.lproj │ └── LaunchScreen.xib ├── FPSObject.h ├── FPSObject.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── album │ ├── LZAlbumCreateVC.h │ ├── LZAlbumCreateVC.m │ ├── LZAlbumCreateVC.xib │ ├── LZAlbumVC.h │ └── LZAlbumVC.m ├── base │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── LZCommon.h │ └── LZMacros.h ├── main.m ├── manager │ ├── LZAlbumManager.h │ └── LZAlbumManager.m ├── model │ ├── LZAlbum.h │ ├── LZAlbum.m │ ├── LZAlbumComment.h │ ├── LZAlbumComment.m │ ├── avobject │ │ ├── AVUser+MCCustomUser.h │ │ ├── AVUser+MCCustomUser.m │ │ ├── LCAlbum.h │ │ ├── LCAlbum.m │ │ ├── LZAVObjectHeader.h │ │ ├── LZComment.h │ │ └── LZComment.m │ └── form │ │ ├── LZEntryForm.h │ │ ├── LZEntryForm.m │ │ ├── LZLoginForm.h │ │ ├── LZLoginForm.m │ │ ├── LZRegisterForm.h │ │ └── LZRegisterForm.m ├── resources │ ├── AlbumAddBtn@2x.png │ ├── AlbumAddBtnHL@2x.png │ ├── AlbumFlagMark@2x.png │ ├── AlbumHeaderBackgrounImage@2x.png │ ├── AlbumInformationLikeHL@2x.png │ ├── AlbumInformationLikeHL@3x.png │ ├── AlbumOperateMore@2x.png │ ├── AlbumOperateMoreHL@2x.png │ ├── Album_like_icon@2x.png │ ├── Album_likes_comments_background@2x.png │ └── album_add_photo@2x.png ├── ui │ ├── AsyncView.h │ ├── AsyncView.m │ ├── LZBaseTC.h │ ├── LZBaseTC.m │ ├── LZBaseVC.h │ ├── LZBaseVC.m │ ├── LZEntryFormController.h │ ├── LZEntryFormController.m │ └── views │ │ ├── AsyncRichTextView.h │ │ ├── AsyncRichTextView.m │ │ ├── AsyncView.h │ │ ├── AsyncView.m │ │ ├── LZAlbumCollectionFlowLayout.h │ │ ├── LZAlbumCollectionFlowLayout.m │ │ ├── LZAlbumCommentTableViewCell.h │ │ ├── LZAlbumCommentTableViewCell.m │ │ ├── LZAlbumLikesCommentsView.h │ │ ├── LZAlbumLikesCommentsView.m │ │ ├── LZAlbumOperationView.h │ │ ├── LZAlbumOperationView.m │ │ ├── LZAlbumPhotoCollectionViewCell.h │ │ ├── LZAlbumPhotoCollectionViewCell.m │ │ ├── LZAlbumReplyView.h │ │ ├── LZAlbumReplyView.m │ │ ├── LZAlbumRichTextView.h │ │ ├── LZAlbumRichTextView.m │ │ ├── LZAlbumTableViewCell.h │ │ ├── LZAlbumTableViewCell.m │ │ ├── LZInputAccessoryView.h │ │ ├── LZInputAccessoryView.m │ │ └── test.png └── vendor │ ├── PathCover │ ├── Resources │ │ ├── MenuBackground@2x.png │ │ ├── circle.png │ │ ├── circle@2x.png │ │ ├── meicon.png │ │ └── pullrefresh.aif │ ├── XHPathCover.h │ ├── XHPathCover.m │ ├── XHSoundManager.h │ ├── XHSoundManager.m │ ├── XHWaterDropRefresh.h │ └── XHWaterDropRefresh.m │ ├── XHImageViewer │ ├── XHImageViewer.h │ ├── XHImageViewer.m │ ├── XHViewState.h │ ├── XHViewState.m │ ├── XHZoomingImageView.h │ └── XHZoomingImageView.m │ └── XHPhotographyHelper │ ├── XHPhotographyHelper.h │ └── XHPhotographyHelper.m ├── LZAlbumTests ├── Info.plist ├── LZAlbumManagerTest.m ├── LZAlbumTest.m ├── LZBaseTest.h └── LZBaseTest.m ├── Podfile ├── README-cn.md ├── README.md ├── package.sh └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | !default.xcworkspace 13 | xcuserdata 14 | *.moved-aside 15 | *.mobileprovision 16 | DerivedData 17 | .idea/ 18 | # Pods - for those of you who use CocoaPods 19 | Pods 20 | Podfile.lock 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_workspace: LZAlbum.xcworkspace 3 | xcode_scheme: LZAlbum 4 | -------------------------------------------------------------------------------- /LZAlbum.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LZAlbum.xcodeproj/project.xcworkspace/xcshareddata/LZAlbum.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 4F124B68-4C02-48C4-AF0C-03B0F8279820 9 | IDESourceControlProjectName 10 | LZAlbum 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 14 | github.com:lzwjava/MCAlbum.git 15 | 16 | IDESourceControlProjectPath 17 | LZAlbum.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:lzwjava/MCAlbum.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 36 | IDESourceControlWCCName 37 | LZAlbum 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LZAlbum.xcodeproj/xcshareddata/xcschemes/LZAlbum.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 98 | 100 | 106 | 107 | 108 | 109 | 111 | 112 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /LZAlbum.xcodeproj/xcshareddata/xcschemes/LZAlbumTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 80 | 81 | 82 | 83 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /LZAlbum.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LZAlbum.xcworkspace/xcshareddata/LZAlbum.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 54E3E103-9FC3-4B29-B7C5-5445BD964DBF 9 | IDESourceControlProjectName 10 | LZAlbum 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 14 | github.com:lzwjava/MCAlbum.git 15 | 16 | IDESourceControlProjectPath 17 | LZAlbum.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 21 | .. 22 | 23 | IDESourceControlProjectURL 24 | github.com:lzwjava/MCAlbum.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 3BA74ADB239A84502FA731E3A3498C5FDD907AF3 36 | IDESourceControlWCCName 37 | LZAlbum 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LZAlbum/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /LZAlbum/FPSObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // FPSObject.h 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/8/19. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FPSObject : NSObject 12 | @end 13 | 14 | /** 15 | CADisplayLink 16 | A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display. 17 | CADisplayLink是一种定时器,系统的每一帧刷新时都会被调用。 18 | 19 | CADisplayLink中的timestamp是系统每次调用时的系统时间戳,通过计算两次时间戳的间隔,可以得到每一帧所花费的时间,既可以获取当前每秒能刷新多少帧。 20 | */ 21 | -------------------------------------------------------------------------------- /LZAlbum/FPSObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // FPSObject.m 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/8/19. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import "FPSObject.h" 10 | #import 11 | #import 12 | #import "JDStatusBarNotification.h" 13 | @interface FPSObject() 14 | { 15 | //定时器 16 | CADisplayLink *linkTimer; 17 | 18 | //用于计算平均值的数组 19 | CFTimeInterval *fpsBuffer; 20 | //用于计算平均值的数组大小 21 | int fpsBufferSize; 22 | 23 | //计算一次fps的间隔 24 | int refreshSeconds; 25 | //计算一次fps间隔用的临时变量 26 | int refreshCounter; 27 | int count; 28 | //用于记录上一次的时间戳 29 | CFTimeInterval lastTimeStamp; 30 | 31 | // 32 | NSInteger currentSeconds; 33 | NSInteger currentCount; 34 | 35 | UILabel *showFPs; 36 | } 37 | @end 38 | 39 | @implementation FPSObject 40 | 41 | - (instancetype)init 42 | { 43 | self = [super init]; 44 | if (self) { 45 | linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)]; 46 | 47 | fpsBufferSize = 10; 48 | fpsBuffer = malloc(sizeof(CFTimeInterval)*fpsBufferSize); 49 | fpsBufferSize -= 1; 50 | refreshSeconds = 10; 51 | refreshCounter = 0; 52 | 53 | lastTimeStamp = CFAbsoluteTimeGetCurrent(); 54 | //添加到当前runloop中 55 | [linkTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 56 | showFPs = [[UILabel alloc]initWithFrame:CGRectMake(10, 30, 150, 40)]; 57 | showFPs.textColor = [UIColor whiteColor]; 58 | showFPs.font = [UIFont systemFontOfSize:12]; 59 | showFPs.text = @"0"; 60 | // [[UIApplication sharedApplication].delegate.window addSubview:showFPs]; 61 | 62 | 63 | 64 | } 65 | return self; 66 | } 67 | 68 | /** 69 | * @brief 带字符串参数的方法. 70 | 71 | */ 72 | - (void)update:(CADisplayLink *)timer 73 | { 74 | count ++; 75 | if (count >200) { 76 | count = 1; 77 | } 78 | 79 | 80 | for (int i = fpsBufferSize - 1; i > 0; i--) { 81 | fpsBuffer[i] = fpsBuffer[i-1]; 82 | } 83 | fpsBuffer[0] = timer.timestamp - lastTimeStamp; 84 | lastTimeStamp = timer.timestamp; 85 | 86 | double maxTime = 0; 87 | double averTimeSum = 0; 88 | double averTime = 0; 89 | 90 | for (int i = 0; i < fpsBufferSize; i++) 91 | { 92 | maxTime = MAX(fpsBuffer[i],maxTime); 93 | averTimeSum += fpsBuffer[i]; 94 | } 95 | 96 | averTime = averTimeSum / fpsBufferSize; 97 | 98 | refreshCounter ++; 99 | 100 | if (refreshCounter%refreshSeconds == 0) { 101 | refreshCounter = 0; 102 | NSString *info = [NSString stringWithFormat:@"current:%.1f---aver:%.1f",roundf(1.f/maxTime),round(1.f/averTime)]; 103 | [JDStatusBarNotification showWithStatus:info styleName:JDStatusBarStyleDark]; 104 | 105 | } 106 | 107 | 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /LZAlbum/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "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 | } -------------------------------------------------------------------------------- /LZAlbum/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | clouddn.com 30 | 31 | NSIncludesSubdomains 32 | 33 | NSTemporaryExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /LZAlbum/album/LZAlbumCreateVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumCreateVC.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/4/1. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseVC.h" 10 | #import "LZAlbumVC.h" 11 | 12 | @interface LZAlbumCreateVC : LZBaseVC 13 | 14 | @property (nonatomic,strong) LZAlbumVC* albumVC; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /LZAlbum/album/LZAlbumCreateVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumCreateVC.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/4/1. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumCreateVC.h" 10 | #import "XHPhotographyHelper.h" 11 | #import "LZAlbumManager.h" 12 | #import "LZAlbumPhotoCollectionViewCell.h" 13 | #import "AppDelegate.h" 14 | 15 | static CGFloat kLZAlbumCreateVCPhotoSize = 60; 16 | 17 | @interface LZAlbumCreateVC () 18 | 19 | @property (weak, nonatomic) IBOutlet UITextField *contentTextField; 20 | 21 | @property (weak, nonatomic) IBOutlet UICollectionView *photoCollectionView; 22 | 23 | @property (nonatomic,strong) NSMutableArray* selectPhotos; 24 | 25 | @property (strong,nonatomic) XHPhotographyHelper* photographyHelper; 26 | 27 | @end 28 | 29 | static NSString* photoCellIndentifier=@"cell"; 30 | 31 | @implementation LZAlbumCreateVC 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | self.title=@"新建"; 36 | UIBarButtonItem *createItem=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(createFeed)]; 37 | self.navigationItem.rightBarButtonItem = createItem; 38 | self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(dismiss)]; 39 | [self.photoCollectionView registerClass:[LZAlbumPhotoCollectionViewCell class] forCellWithReuseIdentifier:photoCellIndentifier]; 40 | } 41 | 42 | -(void)dismiss{ 43 | [self dismissViewControllerAnimated:YES completion:nil]; 44 | } 45 | 46 | -(void)createFeed{ 47 | if(self.contentTextField.text.length!=0 || self.selectPhotos.count!=0){ 48 | WEAKSELF 49 | [self showProgress]; 50 | [self runInGlobalQueue:^{ 51 | NSError* error; 52 | [[LZAlbumManager manager] createAlbumWithText:self.contentTextField.text photos:self.selectPhotos error:&error]; 53 | [weakSelf runInMainQueue:^{ 54 | [weakSelf hideProgress]; 55 | if(error==nil){ 56 | [_albumVC refresh]; 57 | [weakSelf dismiss]; 58 | }else{ 59 | [weakSelf alertError:error]; 60 | } 61 | }]; 62 | }]; 63 | }else{ 64 | [self alert:@"请完善内容"]; 65 | } 66 | } 67 | 68 | #pragma mark - Propertys 69 | -(XHPhotographyHelper*)photographyHelper{ 70 | if(_photographyHelper==nil){ 71 | _photographyHelper=[[XHPhotographyHelper alloc] init]; 72 | } 73 | return _photographyHelper; 74 | } 75 | 76 | - (void)didReceiveMemoryWarning { 77 | [super didReceiveMemoryWarning]; 78 | } 79 | 80 | -(NSMutableArray*)selectPhotos{ 81 | if(_selectPhotos==nil){ 82 | _selectPhotos=[NSMutableArray array]; 83 | } 84 | return _selectPhotos; 85 | } 86 | 87 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 88 | if(self.selectPhotos.count==9){ 89 | return self.selectPhotos.count; 90 | }else{ 91 | return self.selectPhotos.count+1; 92 | } 93 | } 94 | 95 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 96 | LZAlbumPhotoCollectionViewCell* cell=(LZAlbumPhotoCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:photoCellIndentifier forIndexPath:indexPath]; 97 | if(indexPath.row==self.selectPhotos.count){ 98 | cell.photoImageView.image=[UIImage imageNamed:@"AlbumAddBtn"]; 99 | cell.photoImageView.highlightedImage=[UIImage imageNamed:@"AlbumAddBtnHL"]; 100 | return cell; 101 | }else{ 102 | cell.photoImageView.image=self.selectPhotos[indexPath.row]; 103 | cell.photoImageView.highlightedImage=nil; 104 | return cell; 105 | } 106 | } 107 | 108 | #pragma mark - Delegate 109 | -(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{ 110 | return CGSizeMake(kLZAlbumCreateVCPhotoSize, kLZAlbumCreateVCPhotoSize); 111 | } 112 | 113 | -(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 114 | return YES; 115 | } 116 | 117 | -(void)addImage:(UIImage*)image{ 118 | [self.selectPhotos addObject:image]; 119 | [self.photoCollectionView reloadData]; 120 | } 121 | 122 | -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 123 | if(indexPath.row==_selectPhotos.count){ 124 | [self.photographyHelper showOnPickerViewControllerSourceType:UIImagePickerControllerSourceTypePhotoLibrary onViewController:self compled:^(UIImage *image, NSDictionary *editingInfo) { 125 | if (image) { 126 | [self addImage:image]; 127 | } else { 128 | if (!editingInfo) 129 | return ; 130 | image=[editingInfo valueForKey:UIImagePickerControllerOriginalImage]; 131 | if(image){ 132 | [self addImage:image]; 133 | } 134 | } 135 | }]; 136 | } 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /LZAlbum/album/LZAlbumCreateVC.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 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /LZAlbum/album/LZAlbumVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCClassAlbumVC.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/24. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseTC.h" 10 | #import "FPSObject.h" 11 | @interface LZAlbumVC : LZBaseTC 12 | @property (nonatomic,strong)FPSObject *fps; 13 | -(void)refresh; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /LZAlbum/album/LZAlbumVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumVC.m 3 | // 4 | // Created by lzw on 15/3/24. 5 | // Copyright (c) 2015年 lzw. All rights reserved. 6 | // 7 | 8 | #import "LZAlbumVC.h" 9 | #import "XHPathCover.h" 10 | #import "LZAlbumTableViewCell.h" 11 | #import "LZAlbumOperationView.h" 12 | #import "LZAlbumReplyView.h" 13 | #import "LZAlbumCreateVC.h" 14 | #import 15 | #import "LZAlbumManager.h" 16 | #import "AVUser+MCCustomUser.h" 17 | #import 18 | #import "AsyncRichTextView.h" 19 | 20 | #define ShowNew 21 | 22 | @interface LZAlbumVC () 23 | 24 | @property (nonatomic,strong) XHPathCover *albumHeaderPathCover; 25 | 26 | @property (nonatomic,strong) LZAlbumOperationView *albumOperationView; 27 | 28 | @property (nonatomic,strong) LZAlbumReplyView *albumReplyView; 29 | 30 | @property (nonatomic,strong) NSIndexPath *selectedIndexPath; 31 | 32 | @property (nonatomic,strong) NSMutableArray *lcAlbums; 33 | 34 | @property (nonatomic,strong) NSIndexPath *commentSelectedIndexPath; 35 | 36 | @end 37 | 38 | @implementation LZAlbumVC 39 | 40 | #pragma mark - life cycle 41 | 42 | - (void)viewDidLoad { 43 | [super viewDidLoad]; 44 | [self.view addSubview:self.tableView]; 45 | [self.view addSubview:self.albumReplyView]; 46 | 47 | self.title=@"朋友圈"; 48 | self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"album_add_photo"] style:UIBarButtonItemStylePlain target:self action:@selector(onAddAlbumButtonClicked)]; 49 | self.tableView.tableHeaderView=self.albumHeaderPathCover; 50 | [[LZAlbumManager manager] addUserToCache:[AVUser currentUser]]; 51 | _fps = [[FPSObject alloc]init]; 52 | [self refresh]; 53 | } 54 | 55 | -(void)refresh{ 56 | 57 | DLog(@"refresh"); 58 | [self showNetworkIndicator]; 59 | WEAKSELF 60 | //dhmark - LoadData 61 | [[LZAlbumManager manager] findAlbumWithBlock:^(NSArray *lcAlbums, NSError *error) { 62 | [weakSelf hideNetworkIndicator]; 63 | [weakSelf.albumHeaderPathCover stopRefresh]; 64 | if([weakSelf filterError:error]){ 65 | DLog(@"found"); 66 | _lcAlbums=[lcAlbums mutableCopy]; 67 | NSMutableArray* albums=[NSMutableArray array]; 68 | for(LCAlbum* lcAlbum in lcAlbums){ 69 | [albums addObject:[self showAlbumFromLCAlbum:lcAlbum]]; 70 | } 71 | weakSelf.dataSource=albums; 72 | [weakSelf.tableView reloadData]; 73 | } 74 | }]; 75 | } 76 | //dhmark - model factory 77 | -(LZAlbum*)showAlbumFromLCAlbum:(LCAlbum*)lcAlbum{ 78 | LZAlbum* album=[[LZAlbum alloc] init]; 79 | album.username=lcAlbum.creator.username; 80 | AVFile* avatarFile=[lcAlbum.creator objectForKey:KEY_AVATAR]; 81 | album.avatarUrl=avatarFile.url; 82 | album.albumShareContent=lcAlbum.albumContent; 83 | NSMutableArray* photos=[NSMutableArray array]; 84 | for(AVFile* photoFile in lcAlbum.albumPhotos){ 85 | int photoSize = kLZAlbumPhotoSize * [UIScreen mainScreen].scale; 86 | NSString *thumbnailUrl = [photoFile getThumbnailURLWithScaleToFit:YES width:photoSize height:photoSize]; 87 | LZPhoto *photo = [LZPhoto photoWithOriginUrl:photoFile.url thumbnailUrl:thumbnailUrl]; 88 | [photos addObject:photo]; 89 | } 90 | album.albumSharePhotos=photos; 91 | NSMutableArray* digNames=[NSMutableArray array]; 92 | for(AVUser *digUser in lcAlbum.digUsers){ 93 | if (digUser.username.length > 0) { 94 | [digNames addObject:digUser.username]; 95 | } 96 | } 97 | album.albumShareLikes=digNames; 98 | NSMutableArray* albumComments=[NSMutableArray array]; 99 | for(LZComment* comment in lcAlbum.comments){ 100 | if (comment != nil && ![comment isEqual:[NSNull null]]) { 101 | LZAlbumComment* albumComment=[[LZAlbumComment alloc] init]; 102 | albumComment.fromUsername = comment.commentUser.username; 103 | albumComment.toUsername = comment.toUser.username; 104 | albumComment.commentContent=comment.commentContent; 105 | [albumComments addObject:albumComment]; 106 | } 107 | } 108 | album.albumShareComments=albumComments; 109 | album.albumShareTimestamp=lcAlbum.createdAt; 110 | 111 | album.rowHeight = [LZAlbumTableViewCell calculateCellHeightWithAlbum:album]; 112 | album.contentHeight = [LZAlbumRichTextView getContentLabelHeightWithAlbum:album]; 113 | album.collectionHeight = [LZAlbumRichTextView getPhotoCollectionViewHeightWithAlbum:album]; 114 | album.commentsHeight = [LZAlbumLikesCommentsView caculateLikesCommentsViewHeightWithAlbum:album]; 115 | //只和数据相关的必要计算,在网络加载时就计算好。 116 | 117 | return album; 118 | } 119 | 120 | - (void)didReceiveMemoryWarning { 121 | [super didReceiveMemoryWarning]; 122 | } 123 | 124 | 125 | #pragma mark - views 126 | 127 | -(XHPathCover*)albumHeaderPathCover{ 128 | if(_albumHeaderPathCover==nil){ 129 | _albumHeaderPathCover=[[XHPathCover alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 200)]; 130 | [_albumHeaderPathCover setBackgroundImage:[UIImage imageNamed:@"AlbumHeaderBackgrounImage"]]; 131 | AVUser* user=[AVUser currentUser]; 132 | AVFile* avatar=[user objectForKey:KEY_AVATAR]; 133 | [_albumHeaderPathCover.avatarButton sd_setImageWithURL:[NSURL URLWithString:avatar.url] forState:UIControlStateNormal]; 134 | [_albumHeaderPathCover setInfo:@{XHUserNameKey:user.username}]; 135 | _albumHeaderPathCover.isZoomingEffect=YES; 136 | WEAKSELF 137 | [_albumHeaderPathCover setHandleRefreshEvent:^{ 138 | [weakSelf refresh]; 139 | }]; 140 | 141 | [_albumHeaderPathCover setHandleTapBackgroundImageEvent:^{ 142 | DLog(@"tab background"); 143 | }]; 144 | } 145 | return _albumHeaderPathCover; 146 | } 147 | 148 | -(LZAlbumOperationView*)albumOperationView{ 149 | if(_albumOperationView==nil){ 150 | _albumOperationView=[LZAlbumOperationView initialOperationView]; 151 | _albumOperationView.albumOperationViewDelegate=self; 152 | } 153 | return _albumOperationView; 154 | } 155 | 156 | -(LZAlbumReplyView*)albumReplyView{ 157 | if(_albumReplyView==nil){ 158 | _albumReplyView=[[LZAlbumReplyView alloc] initWithFrame:CGRectZero]; 159 | _albumReplyView.albumReplyViewDelegate=self; 160 | } 161 | return _albumReplyView; 162 | } 163 | 164 | -(void)reloadLCAlbum:(LCAlbum*)lcAlbum AtIndexPath:(NSIndexPath*)indexPath{ 165 | [self.lcAlbums replaceObjectAtIndex:indexPath.row withObject:lcAlbum]; 166 | LZAlbum* album=[self showAlbumFromLCAlbum:lcAlbum]; 167 | [self.dataSource replaceObjectAtIndex:indexPath.row withObject:album]; 168 | [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 169 | } 170 | 171 | -(void)addLike{ 172 | LCAlbum* lcAlbum=self.lcAlbums[self.selectedIndexPath.row]; 173 | [[LZAlbumManager manager] digOrCancelDigOfAlbum:lcAlbum block:^(BOOL succeeded, NSError *error) { 174 | if([self filterError:error]){ 175 | [self reloadLCAlbum:lcAlbum AtIndexPath:self.selectedIndexPath]; 176 | } 177 | }]; 178 | } 179 | 180 | -(void)albumOperationView:(LZAlbumOperationView *)albumOperationView didClickOfType:(MCAlbumOperationType)operationType{ 181 | if(operationType==MCAlbumOperationTypeLike){ 182 | [self addLike]; 183 | }else{ 184 | [self.albumReplyView show]; 185 | } 186 | [albumOperationView dismiss]; 187 | } 188 | 189 | -(void)albumReplyView:(LZAlbumReplyView *)albumReplyView didReply:(NSString *)text{ 190 | LCAlbum* lcAlbum=self.lcAlbums[self.selectedIndexPath.row]; 191 | AVUser* toUser; 192 | if(_commentSelectedIndexPath==nil){ 193 | toUser = nil; 194 | }else{ 195 | LZComment* comment=lcAlbum.comments[_commentSelectedIndexPath.row]; 196 | toUser=comment.commentUser; 197 | } 198 | 199 | [self showProgress]; 200 | [[LZAlbumManager manager] commentToUser:toUser AtAlbum:lcAlbum content:text block:^(BOOL succeeded, NSError *error) { 201 | [self hideProgress]; 202 | if([self filterError:error]){ 203 | [self reloadLCAlbum:lcAlbum AtIndexPath:self.selectedIndexPath]; 204 | [self.albumReplyView finishReply]; 205 | } 206 | }]; 207 | } 208 | 209 | #pragma mark - scroll view delegate 210 | -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ 211 | [_albumHeaderPathCover scrollViewDidScroll:scrollView]; 212 | } 213 | 214 | -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ 215 | [_albumHeaderPathCover scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 216 | } 217 | 218 | -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ 219 | [_albumOperationView dismiss]; 220 | 221 | [self.albumReplyView dismiss]; 222 | 223 | [_albumHeaderPathCover scrollViewWillBeginDragging:scrollView]; 224 | } 225 | 226 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ 227 | [_albumHeaderPathCover scrollViewDidEndDecelerating:scrollView]; 228 | } 229 | 230 | #pragma mark - action 231 | -(void)onAddAlbumButtonClicked{ 232 | LZAlbumCreateVC* albumCreateVC=[[LZAlbumCreateVC alloc] init]; 233 | albumCreateVC.albumVC=self; 234 | UINavigationController* nav=[[UINavigationController alloc] initWithRootViewController:albumCreateVC]; 235 | nav.navigationBar.barStyle = UIBarStyleBlack; 236 | [self.navigationController presentViewController:nav animated:YES completion:nil]; 237 | } 238 | 239 | #pragma mark - tableview delegate 240 | 241 | 242 | 243 | -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 244 | #ifdef ShowNew 245 | LZAlbum *album = self.dataSource[indexPath.row]; 246 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"test"]; 247 | if (cell == nil) { 248 | cell = [[UITableViewCell alloc]init]; 249 | } 250 | 251 | AsyncRichTextView *textview = [[AsyncRichTextView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, album.rowHeight) Album:album]; 252 | [cell.contentView addSubview:textview]; 253 | return cell; 254 | #else 255 | static NSString* cellIdentifer=@"albumTableViewCellIdentifier"; 256 | LZAlbumTableViewCell* albumTableViewCell= [tableView dequeueReusableCellWithIdentifier:cellIdentifer]; 257 | if(albumTableViewCell==nil){ 258 | albumTableViewCell=[[LZAlbumTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifer]; 259 | } 260 | albumTableViewCell.albumTableViewCellDelegate=self; 261 | albumTableViewCell.indexPath=indexPath; 262 | albumTableViewCell.currentAlbum=self.dataSource[indexPath.row]; 263 | return albumTableViewCell; 264 | #endif 265 | 266 | 267 | } 268 | 269 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 270 | #ifdef ShowNew 271 | return [self.dataSource[indexPath.row] rowHeight]; 272 | #else 273 | return [LZAlbumTableViewCell calculateCellHeightWithAlbum:self.dataSource[indexPath.row]]; 274 | #endif 275 | 276 | 277 | } 278 | 279 | #pragma mark - Cell Delegate 280 | 281 | -(void)didCommentButtonClick:(UIButton *)button indexPath:(NSIndexPath *)indexPath{ 282 | if(self.albumOperationView.shouldShowed==YES){ 283 | [self.albumOperationView dismiss]; 284 | return; 285 | } 286 | CGRect rectInTablew=[self.tableView rectForRowAtIndexPath:indexPath]; 287 | CGFloat originY=rectInTablew.origin.y+button.frame.origin.y; 288 | CGRect targeRect=CGRectMake(button.frame.origin.x, originY, CGRectGetWidth(button.frame), CGRectGetHeight(button.frame)); 289 | _commentSelectedIndexPath=nil; 290 | _selectedIndexPath=indexPath; 291 | [self.albumOperationView showAtView:self.tableView rect:targeRect]; 292 | } 293 | 294 | -(void)didSelectCommentAtCellIndexPath:(NSIndexPath *)cellIndexPath commentIndexPath:(NSIndexPath *)commentIndexPath{ 295 | LCAlbum* lcAlbum=self.lcAlbums[cellIndexPath.row]; 296 | LZComment* comment=lcAlbum.comments[commentIndexPath.row]; 297 | if ([comment.commentUser isEqual:[AVUser currentUser]]) { 298 | // Todo 显示是否删除 299 | return; 300 | } 301 | _commentSelectedIndexPath=commentIndexPath; 302 | _selectedIndexPath=cellIndexPath; 303 | [self.albumReplyView show]; 304 | } 305 | 306 | #pragma mark - Data Propertys 307 | 308 | @end 309 | -------------------------------------------------------------------------------- /LZAlbum/base/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | -(void)toNextController; 16 | 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /LZAlbum/base/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "LCAlbum.h" 11 | #import "LZComment.h" 12 | #import "LZEntryFormController.h" 13 | #import "LZAlbumVC.h" 14 | #import "FPSObject.h" 15 | @interface AppDelegate () 16 | 17 | @end 18 | 19 | @implementation AppDelegate 20 | 21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 22 | 23 | [LCAlbum registerSubclass]; 24 | [LZComment registerSubclass]; 25 | [AVOSCloud setApplicationId:@"0y463z4tk9wk4zbtkq4qn21kshdm9zetj8mkouiqkaoovn4e" clientKey:@"j9de7xoza1gbvkbp0b6qudz10s9lkwsxqll2nvwrjfty3a58"]; 26 | [AVAnalytics trackAppOpenedWithLaunchOptions:launchOptions]; 27 | [AVOSCloud setLastModifyEnabled:YES]; 28 | [AVOSCloud setNetworkTimeoutInterval:30]; 29 | [[AVInstallation currentInstallation] saveInBackground]; 30 | 31 | #ifdef DEBUG 32 | [AVAnalytics setAnalyticsEnabled:NO]; 33 | [AVOSCloud setAllLogsEnabled:YES]; 34 | #endif 35 | 36 | UIColor* tintColor=[UIColor colorWithRed:0.071 green:0.060 blue:0.086 alpha:1.000]; 37 | if([[UIDevice currentDevice].systemVersion floatValue]>=7.0){ 38 | [UINavigationBar appearance].barTintColor=tintColor; 39 | [UINavigationBar appearance].tintColor=[UIColor whiteColor]; 40 | }else{ 41 | [UINavigationBar appearance].tintColor=tintColor; 42 | } 43 | [UINavigationBar appearance].titleTextAttributes=@{NSForegroundColorAttributeName:[UIColor whiteColor],NSFontAttributeName:[UIFont boldSystemFontOfSize:17]}; 44 | [application setStatusBarStyle:UIStatusBarStyleLightContent]; 45 | 46 | self.window=[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 47 | self.window.backgroundColor=[UIColor whiteColor]; 48 | [self.window makeKeyAndVisible]; 49 | [self toNextController]; 50 | 51 | return YES; 52 | } 53 | 54 | - (void)toNextController { 55 | UIViewController* nextVC; 56 | if([AVUser currentUser]){ 57 | nextVC=[[LZAlbumVC alloc] init]; 58 | }else{ 59 | nextVC=[[LZEntryFormController alloc] init]; 60 | } 61 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:nextVC]; 62 | nav.navigationBar.barStyle = UIBarStyleBlack; 63 | self.window.rootViewController = nav; 64 | } 65 | 66 | - (void)applicationWillResignActive:(UIApplication *)application { 67 | } 68 | 69 | - (void)applicationDidEnterBackground:(UIApplication *)application { 70 | } 71 | 72 | - (void)applicationWillEnterForeground:(UIApplication *)application { 73 | } 74 | 75 | - (void)applicationDidBecomeActive:(UIApplication *)application { 76 | } 77 | 78 | - (void)applicationWillTerminate:(UIApplication *)application { 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /LZAlbum/base/LZCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // CNCommon.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/10. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #ifndef LZAlbum_MCCommon_h 10 | #define LZAlbum_MCCommon_h 11 | 12 | #import 13 | #import "LZMacros.h" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /LZAlbum/base/LZMacros.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCMacros.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/26. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #ifndef LZAlbum_MCMacros_h 10 | #define LZAlbum_MCMacros_h 11 | 12 | #ifdef DEBUG 13 | # define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 14 | #else 15 | # define DLog(...) 16 | #endif 17 | 18 | #define WEAKSELF typeof(self) __weak weakSelf=self; 19 | 20 | #define RGB(R,G,B) [UIColor colorWithRed:(R)/255.0f green:(G)/255.0f blue:(B)/255.0f alpha:1.0f] 21 | #define RGBA(R,G,B,A) [UIColor colorWithRed:(R)/255.0f green:(G)/255.0f blue:(B)/255.0f alpha:(A)/255] 22 | 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /LZAlbum/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/28. 6 | // Copyright (c) 2015年 lzw. 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 | -------------------------------------------------------------------------------- /LZAlbum/manager/LZAlbumManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumManager.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/4/1. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LCAlbum.h" 10 | #import "LZComment.h" 11 | 12 | @interface LZAlbumManager : NSObject 13 | 14 | + (LZAlbumManager *)manager; 15 | 16 | - (void)createAlbumWithText:(NSString*)text photos:(NSArray*)photos error:(NSError**)error; 17 | 18 | -(void)findAlbumWithBlock:(AVArrayResultBlock)block; 19 | 20 | - (void)addUserToCache:(AVUser *)user; 21 | 22 | -(void)commentToUser:(AVUser *)toUser AtAlbum:(LCAlbum*)album content:(NSString*)content block:(AVBooleanResultBlock)block; 23 | 24 | -(void)digOrCancelDigOfAlbum:(LCAlbum *)album block:(AVBooleanResultBlock)block; 25 | 26 | - (void)deleteUnusedFiles; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LZAlbum/manager/LZAlbumManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCFeedManager.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/4/1. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumManager.h" 10 | 11 | @interface LZAlbumManager() 12 | 13 | @property (nonatomic, strong) NSMutableDictionary *cachedUsers; 14 | 15 | @end 16 | 17 | @implementation LZAlbumManager 18 | 19 | + (LZAlbumManager *)manager { 20 | static LZAlbumManager *albumManager; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | albumManager = [[LZAlbumManager alloc] init]; 24 | }); 25 | return albumManager; 26 | } 27 | 28 | - (instancetype)init { 29 | self = [super init]; 30 | if (self) { 31 | self.cachedUsers = [NSMutableDictionary dictionary]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)createAlbumWithText:(NSString *)text photos:(NSArray *)photos error:(NSError **)error { 37 | if (text == nil) { 38 | text = @""; 39 | } 40 | NSMutableArray *photoFiles = [NSMutableArray array]; 41 | NSError *theError; 42 | for (UIImage *photo in photos) { 43 | AVFile *photoFile = [AVFile fileWithData:UIImageJPEGRepresentation(photo, 0.6)]; 44 | [photoFile save:&theError]; 45 | if (theError) { 46 | *error = theError; 47 | return; 48 | } 49 | [photoFiles addObject:photoFile]; 50 | } 51 | AVUser *user = [AVUser currentUser]; 52 | LCAlbum *album = [LCAlbum object]; 53 | album.creator = user; 54 | album.albumContent = text; 55 | album.albumPhotos = photoFiles; 56 | album.isDel = NO; 57 | album.digUsers = [NSArray array]; 58 | album.comments = [NSArray array]; 59 | [album save:&theError]; 60 | *error = theError; 61 | } 62 | 63 | /** 64 | * @discussion 因为不同朋友圈里的评论或者点赞的人很有可能重复,这里我们遍历所有的 User,然后统一缓存起来。原先这里的逻辑是很简单的,这里为了更好的性能优化了。见这个Commit:https://github.com/lzwjava/LZAlbum/commit/afa731608ecdbbd9e38ae864f04a37cd518cdbb0#diff-7d87db85844b7a3956c139f06e3e1dfbR37 65 | */ 66 | - (void)findAlbumWithBlock:(AVArrayResultBlock)block { 67 | AVQuery *q = [LCAlbum query]; 68 | [q orderByDescending:KEY_CREATED_AT]; 69 | [q includeKey:KEY_ALBUM_PHOTOS]; 70 | [q includeKey:KEY_COMMENTS]; 71 | [q setLimit:100]; 72 | [q whereKey:KEY_IS_DEL equalTo:@(NO)]; 73 | [q setCachePolicy:kAVCachePolicyNetworkElseCache]; 74 | [q findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { 75 | if (error) { 76 | block(nil, error); 77 | } else { 78 | NSMutableSet *userIds = [NSMutableSet set]; 79 | [self iterateUsersInAlbums:objects block:^(AVUser *user) { 80 | [userIds addObject:user.objectId]; 81 | }]; 82 | [self cacheUsersByIds:userIds block:^(BOOL succeeded, NSError *error) { 83 | if (error) { 84 | block(objects, error); 85 | } else { 86 | [self iterateUsersInAlbums:objects block:^(AVUser *user) { 87 | [self fillPointerUser:user]; 88 | }]; 89 | block(objects, nil); 90 | } 91 | }]; 92 | } 93 | }]; 94 | } 95 | 96 | typedef void (^AVUserHandleBlock)(AVUser *user); 97 | 98 | /*! 99 | * 找出 Album 里相关的所有 User,这些 User只有 objectId,没有 username 等数据 100 | */ 101 | - (void)iterateUsersInAlbums:(NSArray *)albums block:(AVUserHandleBlock)block { 102 | for (LCAlbum *album in albums) { 103 | if (album.creator) { 104 | block(album.creator); 105 | } 106 | for (AVUser *digUser in album.digUsers) { 107 | block(digUser); 108 | } 109 | for (LZComment *comment in album.comments) { 110 | if (comment != nil && ![comment isEqual:[NSNull null]]) { 111 | if (comment.commentUser) { 112 | block(comment.commentUser); 113 | } 114 | if (comment.toUser) { 115 | block(comment.toUser); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | /*! 123 | * 填充 username,createdAt 等数据 124 | */ 125 | - (void)fillPointerUser:(AVUser *)pointerUser { 126 | AVUser *fullUser = [self lookUpUserById:pointerUser.objectId]; 127 | [pointerUser objectFromDictionary:[fullUser dictionaryForObject]]; 128 | } 129 | 130 | #pragma mark - User Cache 131 | 132 | - (AVUser *)lookUpUserById:(NSString *)objectId { 133 | return self.cachedUsers[objectId]; 134 | } 135 | 136 | - (void)addUserToCache:(AVUser *)user { 137 | self.cachedUsers[user.objectId] = user; 138 | } 139 | 140 | - (void)cacheUsersByIds:(NSMutableSet *)userIds block:(AVBooleanResultBlock)block { 141 | NSMutableSet *uncached = [NSMutableSet set]; 142 | for (NSString *userId in userIds) { 143 | if ([self lookUpUserById:userId] == nil) { 144 | [uncached addObject:userId]; 145 | } 146 | } 147 | if (uncached.count > 0) { 148 | AVQuery *query = [AVUser query]; 149 | [query whereKey:@"objectId" containedIn:[uncached allObjects]]; 150 | [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { 151 | if (error) { 152 | block(NO, error); 153 | } else { 154 | for (AVUser *user in objects) { 155 | [self addUserToCache:user]; 156 | } 157 | block(YES, nil); 158 | } 159 | }]; 160 | } else { 161 | block(YES, nil); 162 | } 163 | } 164 | 165 | - (NSArray *)getObjectIds:(NSArray *)avObjects { 166 | NSMutableArray *objectIds = [NSMutableArray array]; 167 | for (AVObject *object in avObjects) { 168 | [objectIds addObject:object.objectId]; 169 | } 170 | return objectIds; 171 | } 172 | 173 | - (void)digOrCancelDigOfAlbum:(LCAlbum *)album block:(AVBooleanResultBlock)block { 174 | AVUser *user = [AVUser currentUser]; 175 | if ([album.digUsers containsObject:user]) { 176 | [album removeObject:user forKey:KEY_DIG_USERS]; 177 | } else { 178 | [album addObject:user forKey:KEY_DIG_USERS]; 179 | } 180 | [album saveInBackgroundWithBlock:block]; 181 | } 182 | 183 | - (void)commentToUser:(AVUser *)toUser AtAlbum:(LCAlbum *)album content:(NSString *)content block:(AVBooleanResultBlock)block { 184 | LZComment *comment = [LZComment object]; 185 | comment.commentContent = content; 186 | AVUser *user = [AVUser currentUser]; 187 | comment.commentUser = user; 188 | comment.album = album; 189 | comment.toUser = toUser; 190 | [comment saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { 191 | if (error) { 192 | block(NO, error); 193 | } else { 194 | [album addObject:comment forKey:KEY_COMMENTS]; 195 | [album saveInBackgroundWithBlock:block]; 196 | } 197 | }]; 198 | } 199 | 200 | - (BOOL)file:(AVObject *)file existsInAlbums:(NSArray *)albums { 201 | for (LCAlbum *album in albums) { 202 | for (AVFile *photo in album.albumPhotos) { 203 | if ([photo.objectId isEqualToString:file.objectId]) { 204 | return YES; 205 | } 206 | } 207 | } 208 | return NO; 209 | } 210 | 211 | // 数据维护,删掉没有出现在朋友圈里的文件,这里会把头像文件也删除掉,TODO FIX 212 | - (void)deleteUnusedFiles { 213 | AVQuery *query = [LCAlbum query]; 214 | [query includeKey:KEY_ALBUM_PHOTOS]; 215 | query.limit = 1000; 216 | [query findObjectsInBackgroundWithBlock:^(NSArray *albums, NSError *error) { 217 | if (!error) { 218 | AVQuery *query = [AVQuery queryWithClassName:@"_File"]; 219 | [query orderByAscending:@"createdAt"]; 220 | query.limit = 1000; 221 | [query findObjectsInBackgroundWithBlock:^(NSArray *files, NSError *error) { 222 | if (!error) { 223 | NSMutableArray *toDeletes = [NSMutableArray array]; 224 | for (AVObject *file in files) { 225 | if (![self file:file existsInAlbums:albums]) { 226 | NSLog(@"file not exists will delete"); 227 | [toDeletes addObject:file]; 228 | } else { 229 | NSLog(@"file exists do not delete"); 230 | }; 231 | } 232 | [AVObject deleteAllInBackground:toDeletes block:^(BOOL succeeded, NSError *error) { 233 | NSLog(@"deleted, error %@", error); 234 | }]; 235 | } 236 | }]; 237 | } 238 | }]; 239 | } 240 | 241 | @end 242 | -------------------------------------------------------------------------------- /LZAlbum/model/LZAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbum.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbumComment.h" 11 | #import "LZMacros.h" 12 | 13 | #define LZLinkTextForegroundColor RGB(117, 135, 181) 14 | #define LZLinkTextHighlightColor [UIColor colorWithWhite:0.7 alpha:1] 15 | 16 | static const CGFloat kLZAlbumAvatarSpacing=15.0f; 17 | static const CGFloat kLZAlbumAvatarImageSize=45.0f; 18 | 19 | static const CGFloat kLZAlbumPhotoSize=70; 20 | static const CGFloat kLZAlbumPhotoInset=5; 21 | 22 | static const CGFloat kLZAlbumFontSize=15; 23 | 24 | @interface LZPhoto : NSObject 25 | 26 | @property (nonatomic, copy) NSString *thumbnailUrl; 27 | 28 | @property (nonatomic, copy) NSString *originUrl; 29 | 30 | + (instancetype)photoWithOriginUrl:(NSString *)originUrl thumbnailUrl:(NSString *)thumbnailUrl; 31 | 32 | - (NSString *)actualThumbnailUrl; 33 | 34 | @end 35 | 36 | //dhmark - CellModel 37 | 38 | #define k 39 | 40 | @interface LZAlbum : NSObject 41 | 42 | @property (nonatomic,copy) NSString* username; 43 | 44 | @property (nonatomic,copy) NSString* avatarUrl; 45 | 46 | @property (nonatomic,copy) NSString* albumShareContent; 47 | 48 | @property (nonatomic,strong) NSArray* albumSharePhotos; 49 | 50 | @property (nonatomic,strong) NSArray* albumShareLikes; 51 | 52 | @property (nonatomic,strong) NSArray* albumShareComments; 53 | 54 | @property (nonatomic,copy) NSDate* albumShareTimestamp; 55 | 56 | @property (nonatomic,strong) NSMutableDictionary *drawingContext; 57 | @property (nonatomic,assign) CGFloat rowHeight; 58 | @property (nonatomic,assign) CGFloat contentHeight; 59 | @property (nonatomic,assign) CGFloat collectionHeight; 60 | @property (nonatomic,assign) CGFloat commentsHeight; 61 | + (CGFloat)contentWidth; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /LZAlbum/model/LZAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbum.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbum.h" 10 | 11 | @implementation LZPhoto 12 | 13 | + (instancetype)photoWithOriginUrl:(NSString *)originUrl thumbnailUrl:(NSString *)thumbnailUrl { 14 | LZPhoto *photo = [[LZPhoto alloc] init]; 15 | photo.originUrl = originUrl; 16 | photo.thumbnailUrl = thumbnailUrl; 17 | return photo; 18 | } 19 | 20 | - (NSString *)actualThumbnailUrl { 21 | if (self.thumbnailUrl) { 22 | return self.thumbnailUrl; 23 | } else { 24 | return self.originUrl; 25 | } 26 | } 27 | 28 | @end 29 | 30 | @implementation LZAlbum 31 | 32 | -(NSDictionary *)drawingContext 33 | { 34 | if (!_drawingContext) { 35 | _drawingContext = [[NSMutableDictionary alloc]initWithCapacity:2]; 36 | } 37 | return _drawingContext; 38 | } 39 | 40 | + (CGFloat)contentWidth { 41 | return CGRectGetWidth([[UIScreen mainScreen] bounds])-3*kLZAlbumAvatarSpacing-kLZAlbumAvatarImageSize; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /LZAlbum/model/LZAlbumComment.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumComment.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/8/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LZAlbumComment : NSObject 12 | 13 | @property (nonatomic,copy) NSString *fromUsername; 14 | @property (nonatomic,copy) NSString *toUsername; 15 | @property (nonatomic,copy) NSString *commentContent; 16 | 17 | - (NSRange)fromUsernameRange; 18 | - (NSRange)toUsernameRange; 19 | - (NSString *)fullCommentText; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LZAlbum/model/LZAlbumComment.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumComment.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/8/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumComment.h" 10 | 11 | @interface LZAlbumComment() 12 | 13 | @property (nonatomic, assign) NSRange fromRange; 14 | @property (nonatomic, assign) NSRange toRange; 15 | @property (nonatomic, strong) NSString *fullText; 16 | 17 | @end 18 | 19 | @implementation LZAlbumComment 20 | 21 | - (NSRange)fromUsernameRange { 22 | [self buildCommentText]; 23 | return self.fromRange; 24 | } 25 | 26 | - (NSRange)toUsernameRange { 27 | [self buildCommentText]; 28 | return self.toRange; 29 | } 30 | 31 | - (NSString *)fullCommentText { 32 | [self buildCommentText]; 33 | return self.fullText; 34 | } 35 | 36 | - (void)buildCommentText { 37 | NSMutableString *text = [NSMutableString string]; 38 | 39 | NSRange fromRange = NSMakeRange(NSNotFound, 0); 40 | if (self.fromUsername) { 41 | [text appendString:self.fromUsername]; 42 | fromRange.location = 0; 43 | fromRange.length = self.fromUsername.length; 44 | } 45 | 46 | NSRange toRange = NSMakeRange(NSNotFound, 0); 47 | if (self.toUsername) { 48 | [text appendString:@"回复"]; 49 | toRange.location = text.length; 50 | toRange.length = self.toUsername.length; 51 | [text appendString:self.toUsername]; 52 | } 53 | if (self.commentContent) { 54 | [text appendString:@":"]; 55 | [text appendString:self.commentContent]; 56 | } 57 | 58 | self.fromRange = fromRange; 59 | self.toRange= toRange; 60 | self.fullText = text; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/AVUser+MCCustomUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCUser.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZCommon.h" 11 | 12 | #define KEY_USERNAME @"username" 13 | #define KEY_AVATAR @"avatar" 14 | 15 | @interface AVUser(MCCustomUser) 16 | 17 | -(AVFile*)avatar; 18 | 19 | -(void)setAvatar:(AVFile*)avatar; 20 | 21 | -(NSString*)avatarUrl; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/AVUser+MCCustomUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCUser.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "AVUser+MCCustomUser.h" 10 | 11 | @implementation AVUser(MCCustomUser) 12 | 13 | -(AVFile*)avatar{ 14 | return [self objectForKey:KEY_AVATAR]; 15 | } 16 | 17 | -(void)setAvatar:(AVFile*)avatar{ 18 | [self setObject:avatar forKey:KEY_AVATAR]; 19 | } 20 | 21 | -(NSString*)avatarUrl{ 22 | return [self avatar].url; 23 | } 24 | 25 | @end -------------------------------------------------------------------------------- /LZAlbum/model/avobject/LCAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCFeed.h 3 | // LZAlbum 4 | // 5 | // Created by wangyuansong on 15/3/11. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAVObjectHeader.h" 11 | 12 | #define KEY_CLS @"cls" 13 | #define KEY_ALBUM_TYPE @"albumType" 14 | #define KEY_ALBUM_CONTENT @"albumContent" 15 | #define KEY_ALBUM_PHOTOS @"albumPhotos" 16 | #define KEY_DIG_USERS @"digUsers" 17 | #define KEY_COMMENTS @"comments" 18 | #define KEY_IS_DEL @"isDel" 19 | #define KEY_CREATOR @"creator" 20 | 21 | @interface LCAlbum : AVObject 22 | 23 | @property (nonatomic,strong) AVUser* creator; // 创建者 24 | @property (nonatomic,copy) NSString* albumContent; //分享内容 25 | @property (nonatomic,assign) NSArray* albumPhotos; //分享照片 _File Array 26 | @property (nonatomic,assign) NSArray* digUsers; //赞过的人 _User Array 27 | @property (nonatomic,assign) NSArray* comments; // Comment Array 28 | @property (nonatomic,assign) BOOL isDel; //是否删除 29 | 30 | // createdAt : 发布时间 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/LCAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCFeed.m 3 | // LZAlbum 4 | // 5 | // Created by wangyuansong on 15/3/11. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LCAlbum.h" 10 | 11 | @implementation LCAlbum 12 | 13 | @dynamic creator; 14 | @dynamic albumContent; 15 | @dynamic albumPhotos; 16 | @dynamic comments; 17 | @dynamic digUsers; 18 | @dynamic isDel; 19 | 20 | +(NSString*)parseClassName{ 21 | return @"Album"; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/LZAVObjectHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAVObjectHeader.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/4/4. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #ifndef LZAlbum_MCAVObjectHeader_h 10 | #define LZAlbum_MCAVObjectHeader_h 11 | 12 | #define KEY_CREATED_AT @"createdAt" 13 | #define KEY_UPDATED_AT @"updatedAt" 14 | #define KEY_OBJECT_ID @"objectId" 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/LZComment.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCComment.h 3 | // LZAlbum 4 | // 5 | // Created by wangyuansong on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZCommon.h" 10 | #import "LCAlbum.h" 11 | 12 | #define KEY_ALBUM @"album" 13 | #define KEY_COMMENT_USER @"commentUser" 14 | #define KEY_COMMENT_USERNAME @"commentUsername" 15 | #define KEY_COMMENT_CONTENT @"commentContent" 16 | #define KEY_TO_USER @"toUser" 17 | 18 | @interface LZComment : AVObject 19 | 20 | @property (nonatomic,strong) LCAlbum* album;//关联分享 21 | @property (nonatomic,strong) AVUser* commentUser;//评论用户 22 | @property (nonatomic,strong) NSString* commentContent;//评论内容 23 | @property (nonatomic,strong)AVUser* toUser;//关联用户 24 | 25 | // createdAt:评论时间 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /LZAlbum/model/avobject/LZComment.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCComment.m 3 | // LZAlbum 4 | // 5 | // Created by wangyuansong on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZComment.h" 10 | 11 | @implementation LZComment 12 | 13 | @dynamic album; 14 | @dynamic commentContent; 15 | @dynamic commentUser; 16 | @dynamic toUser; 17 | 18 | +(NSString*)parseClassName{ 19 | return @"Comment"; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZEntryForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCRootForm.h 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZLoginForm.h" 11 | #import "LZRegisterForm.h" 12 | 13 | @interface LZEntryForm : NSObject 14 | 15 | @property (nonatomic,strong) LZLoginForm *loginForm; 16 | @property (nonatomic,strong) LZRegisterForm *registerForm; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZEntryForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCRootForm.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZEntryForm.h" 10 | 11 | @implementation LZEntryForm 12 | 13 | -(NSDictionary*)loginFormField{ 14 | return @{FXFormFieldInline:@YES,FXFormFieldHeader:@"Login"}; 15 | } 16 | 17 | -(NSDictionary*)registerFormField{ 18 | return @{FXFormFieldHeader:@"Not Registered?",FXFormFieldTitle:@"Register"}; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZLoginForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoginForm.h 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZRegisterForm.h" 11 | #import 12 | 13 | @interface LZLoginForm : NSObject 14 | 15 | @property (nonatomic,strong) NSString *username; 16 | @property (nonatomic,strong) NSString *password; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZLoginForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoginForm.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZLoginForm.h" 10 | 11 | @implementation LZLoginForm 12 | 13 | -(NSArray*)extraFields{ 14 | return @[@{FXFormFieldHeader:@"",FXFormFieldTitle:@"Login",FXFormFieldAction:@"submitLogin:"}]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZRegisterForm.h: -------------------------------------------------------------------------------- 1 | // 2 | // RegisterForm.h 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LZRegisterForm : NSObject 13 | 14 | @property (nonatomic,strong) UIImage *avatar; 15 | @property (nonatomic,strong) NSString *username; 16 | @property (nonatomic,strong) NSString *password; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LZAlbum/model/form/LZRegisterForm.m: -------------------------------------------------------------------------------- 1 | // 2 | // RegisterForm.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZRegisterForm.h" 10 | 11 | @implementation LZRegisterForm 12 | 13 | -(NSArray*)extraFields{ 14 | return @[@{FXFormFieldHeader:@"",FXFormFieldTitle:@"Register",FXFormFieldAction:@"submitRegister:"}]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumAddBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumAddBtn@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumAddBtnHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumAddBtnHL@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumFlagMark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumFlagMark@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumHeaderBackgrounImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumHeaderBackgrounImage@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumInformationLikeHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumInformationLikeHL@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumInformationLikeHL@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumInformationLikeHL@3x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumOperateMore@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumOperateMore@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/AlbumOperateMoreHL@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/AlbumOperateMoreHL@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/Album_like_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/Album_like_icon@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/Album_likes_comments_background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/Album_likes_comments_background@2x.png -------------------------------------------------------------------------------- /LZAlbum/resources/album_add_photo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/resources/album_add_photo@2x.png -------------------------------------------------------------------------------- /LZAlbum/ui/AsyncView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncView.h 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/8/4. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AsyncView : UIView 12 | ///// 用于异步绘制的队列,为nil时将使用GCD的global queue进行绘制,默认为nil 13 | @property (nonatomic, assign) dispatch_queue_t dispatchDrawQueue; 14 | 15 | /// 异步绘制时global queue的优先级,默认优先级为DEFAULT。在设置了drawQueue时此参数无效。 16 | @property (nonatomic, assign) dispatch_queue_priority_t dispatchPriority; 17 | 18 | - (BOOL)drawInRect:(CGRect)rect Context:(CGContextRef)context; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LZAlbum/ui/AsyncView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncView.m 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/8/4. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import "AsyncView.h" 10 | 11 | @implementation AsyncView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | self.dispatchPriority = DISPATCH_QUEUE_PRIORITY_DEFAULT; 18 | self.opaque = NO; 19 | self.layer.contentsScale = [[UIScreen mainScreen] scale]; 20 | } 21 | return self; 22 | } 23 | 24 | - (void)didMoveToWindow 25 | { 26 | [super didMoveToWindow]; 27 | 28 | if (!self.window) 29 | { 30 | // 没有 Window 说明View已经没有显示在界面上,此时应该终止绘制 31 | 32 | } 33 | else if (!self.layer.contents) 34 | { 35 | [self.layer setNeedsDisplay]; 36 | } 37 | } 38 | 39 | - (dispatch_queue_t)drawQueue 40 | { 41 | if (self.dispatchDrawQueue) 42 | { 43 | return self.dispatchDrawQueue; 44 | } 45 | 46 | return dispatch_get_global_queue(self.dispatchPriority, 0); 47 | } 48 | 49 | - (void)displayLayer:(CALayer *)layer 50 | { 51 | 52 | dispatch_async([self drawQueue], ^{ 53 | [self displayLayer:layer Rect:layer.frame]; 54 | }); 55 | } 56 | 57 | - (BOOL)drawInRect:(CGRect)rect Context:(CGContextRef)context 58 | { 59 | return YES; 60 | } 61 | 62 | - (void)displayLayer:(CALayer *)layer Rect:(CGRect)rect 63 | { 64 | CGContextRef context = NULL; 65 | UIGraphicsBeginImageContextWithOptions(layer.bounds.size, layer.isOpaque, layer.contentsScale); 66 | context = UIGraphicsGetCurrentContext(); 67 | CGContextSaveGState(context); 68 | 69 | if (rect.origin.x || rect.origin.y) 70 | { 71 | CGContextTranslateCTM(context, rect.origin.x, -rect.origin.y); 72 | } 73 | 74 | if (self.backgroundColor && 75 | self.backgroundColor != [UIColor clearColor]) 76 | { 77 | CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); 78 | CGContextFillRect(context, rect); 79 | } 80 | [self drawInRect:rect Context:context]; 81 | CGContextRestoreGState(context); 82 | 83 | if(true)//所有绘制完成 84 | { 85 | CGImageRef CGImage = context ? CGBitmapContextCreateImage(context) : nil; 86 | 87 | UIImage *image = CGImage ? [UIImage imageWithCGImage:CGImage] : nil; 88 | 89 | dispatch_async(dispatch_get_main_queue(), ^{ 90 | layer.contents = (id)image.CGImage; 91 | }); 92 | } 93 | 94 | } 95 | 96 | 97 | 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZBaseTC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCBaseTC.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZBaseVC.h" 11 | 12 | @interface LZBaseTC : LZBaseVC 13 | 14 | @property (nonatomic,strong) UITableView* tableView; 15 | 16 | @property (nonatomic,assign) UITableViewStyle tableViewStyle; 17 | 18 | @property (nonatomic,strong) NSMutableArray* dataSource; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZBaseTC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCBaseTC.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseTC.h" 10 | 11 | @interface LZBaseTC () 12 | 13 | @end 14 | 15 | @implementation LZBaseTC 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | [self.view addSubview:self.tableView]; 20 | } 21 | 22 | -(UITableView*)tableView{ 23 | if(_tableView==nil){ 24 | CGRect tableViewFrame = self.view.bounds; 25 | tableViewFrame.size.height -= (self.navigationController.viewControllers.count > 1 ? 0 : (CGRectGetHeight(self.tabBarController.tabBar.bounds))); 26 | _tableView = [[UITableView alloc] initWithFrame:tableViewFrame style:self.tableViewStyle]; 27 | _tableView.delegate=self; 28 | _tableView.dataSource=self; 29 | } 30 | return _tableView; 31 | } 32 | 33 | - (void)didReceiveMemoryWarning { 34 | [super didReceiveMemoryWarning]; 35 | } 36 | 37 | -(void)dealloc{ 38 | } 39 | 40 | -(NSMutableArray*)dataSource{ 41 | if(_dataSource==nil){ 42 | _dataSource=[NSMutableArray array]; 43 | } 44 | return _dataSource; 45 | } 46 | 47 | -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 48 | return 1; 49 | } 50 | 51 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 52 | return self.dataSource.count; 53 | } 54 | 55 | -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 56 | return nil; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZBaseVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCViewController.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZMacros.h" 11 | #import 12 | 13 | @interface LZBaseVC : UIViewController 14 | 15 | -(void)showNetworkIndicator; 16 | 17 | -(void)hideNetworkIndicator; 18 | 19 | -(void)showProgress; 20 | 21 | -(void)hideProgress; 22 | 23 | -(void)alert:(NSString*)msg; 24 | 25 | -(BOOL)alertError:(NSError*)error; 26 | 27 | -(BOOL)filterError:(NSError*)error; 28 | 29 | -(void)runInMainQueue:(void (^)())queue; 30 | 31 | -(void)runInGlobalQueue:(void (^)())queue; 32 | 33 | -(void)runAfterSecs:(float)secs block:(void (^)())block; 34 | 35 | -(void)showHUDText:(NSString*)text; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZBaseVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCViewController.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/12. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseVC.h" 10 | 11 | @interface LZBaseVC () 12 | 13 | @end 14 | 15 | @implementation LZBaseVC 16 | 17 | -(void)alert:(NSString*)msg{ 18 | UIAlertView *alertView=[[UIAlertView alloc] 19 | initWithTitle:nil message:msg delegate:nil 20 | cancelButtonTitle:@"确定" otherButtonTitles:nil]; 21 | [alertView show]; 22 | } 23 | 24 | -(BOOL)alertError:(NSError*)error{ 25 | if(error){ 26 | [self alert:[NSString stringWithFormat:@"%@",error]]; 27 | return YES; 28 | } 29 | return NO; 30 | } 31 | 32 | -(BOOL)filterError:(NSError*)error{ 33 | return [self alertError:error]==NO; 34 | } 35 | 36 | -(void)showNetworkIndicator{ 37 | UIApplication* app=[UIApplication sharedApplication]; 38 | app.networkActivityIndicatorVisible=YES; 39 | } 40 | 41 | -(void)hideNetworkIndicator{ 42 | UIApplication* app=[UIApplication sharedApplication]; 43 | app.networkActivityIndicatorVisible=NO; 44 | } 45 | 46 | -(void)showProgress{ 47 | [MBProgressHUD showHUDAddedTo:self.view animated:YES]; 48 | } 49 | 50 | -(void)hideProgress{ 51 | [MBProgressHUD hideHUDForView:self.view animated:YES]; 52 | } 53 | 54 | -(void)showHUDText:(NSString*)text{ 55 | MBProgressHUD* hud=[MBProgressHUD showHUDAddedTo:self.view animated:YES]; 56 | hud.labelText=text; 57 | hud.margin=10.f; 58 | hud.removeFromSuperViewOnHide=YES; 59 | hud.mode=MBProgressHUDModeText; 60 | [hud hide:YES afterDelay:2]; 61 | } 62 | 63 | -(void)runInMainQueue:(void (^)())queue{ 64 | dispatch_async(dispatch_get_main_queue(), queue); 65 | } 66 | 67 | -(void)runInGlobalQueue:(void (^)())queue{ 68 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), queue); 69 | } 70 | 71 | -(void)runAfterSecs:(float)secs block:(void (^)())block{ 72 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, secs*NSEC_PER_SEC), dispatch_get_main_queue(), block); 73 | } 74 | 75 | - (void)viewDidLoad { 76 | [super viewDidLoad]; 77 | } 78 | 79 | - (void)didReceiveMemoryWarning { 80 | [super didReceiveMemoryWarning]; 81 | } 82 | 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZEntryFormController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCEntryFormController.h 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseTC.h" 10 | 11 | @interface LZEntryFormController : LZBaseTC 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LZAlbum/ui/LZEntryFormController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCEntryFormController.m 3 | // MCAlbum 4 | // 5 | // Created by lzw on 15/4/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZEntryFormController.h" 10 | #import "LZEntryForm.h" 11 | #import 12 | #import "AVUser+MCCustomUser.h" 13 | #import "AppDelegate.h" 14 | 15 | @interface LZEntryFormController() 16 | 17 | @property (nonatomic,strong) FXFormController *formController; 18 | 19 | @end 20 | 21 | @implementation LZEntryFormController 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self) { 27 | self.tableViewStyle=UITableViewStyleGrouped; 28 | self.title=@"登录"; 29 | } 30 | return self; 31 | } 32 | 33 | 34 | -(void)viewDidLoad{ 35 | [super viewDidLoad]; 36 | self.formController=[[FXFormController alloc] init]; 37 | self.formController.tableView=self.tableView; 38 | self.formController.form=[[LZEntryForm alloc] init]; 39 | } 40 | 41 | -(void)submitLogin:(UITableViewCell *)cell{ 42 | LZLoginForm *loginForm=cell.field.form; 43 | WEAKSELF 44 | [AVUser logInWithUsernameInBackground:loginForm.username password:loginForm.password block:^(AVUser *user, NSError *error) { 45 | if([weakSelf filterError:error]){ 46 | [weakSelf toMainVC]; 47 | } 48 | }]; 49 | } 50 | 51 | -(void)submitRegister:(UITableViewCell *)cell{ 52 | LZRegisterForm *registerForm=cell.field.form; 53 | if(registerForm.username.length>0 && registerForm.password.length>0 54 | && registerForm.avatar!=nil){ 55 | UIImage *avatarImage=[[self class] roundImage:registerForm.avatar toSize:CGSizeMake(200, 200) radius:10]; 56 | AVFile *avatar=[AVFile fileWithData:UIImagePNGRepresentation(avatarImage)]; 57 | WEAKSELF 58 | [avatar saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { 59 | if([weakSelf filterError:error]){ 60 | AVUser *user=[[AVUser alloc] init]; 61 | user.username=registerForm.username; 62 | user.password=registerForm.password; 63 | [user setAvatar:avatar]; 64 | [user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { 65 | if([weakSelf filterError:error]){ 66 | [weakSelf toMainVC]; 67 | } 68 | }]; 69 | } 70 | }]; 71 | } else { 72 | [self alert:@"请完善资料"]; 73 | } 74 | } 75 | 76 | -(void)toMainVC{ 77 | AppDelegate *appDelegate=[UIApplication sharedApplication].delegate; 78 | [appDelegate toNextController]; 79 | } 80 | 81 | 82 | +(UIImage *)roundImage:(UIImage *) image toSize:(CGSize)size radius: (float) radius; 83 | { 84 | CGRect rect=CGRectMake(0, 0, size.width, size.height); 85 | UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale); 86 | [[UIBezierPath bezierPathWithRoundedRect:rect 87 | cornerRadius:radius] addClip]; 88 | [image drawInRect:rect]; 89 | UIImage* rounded = UIGraphicsGetImageFromCurrentImageContext(); 90 | UIGraphicsEndImageContext(); 91 | return rounded; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/AsyncRichTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncRichTextView.h 3 | // LZAlbum 4 | // 5 | // Created by liudonghuan on 16/10/27. 6 | // Copyright © 2016年 lzw. All rights reserved. 7 | // 8 | 9 | #import "AsyncView.h" 10 | #import 11 | #import "LZAlbum.h" 12 | @protocol LZAlbumRichTextViewDelegate 13 | 14 | -(void)didCommentButtonClick:(UIButton*)button; 15 | 16 | -(void)didSelectCommentAtIndexPath:(NSIndexPath*)indexPath; 17 | 18 | @end 19 | 20 | @interface AsyncRichTextView : AsyncView 21 | @property (nonatomic,strong)LZAlbum *album; 22 | @property (nonatomic,weak) id richTextViewDelegate; 23 | - (instancetype)initWithFrame:(CGRect)frame Album:(LZAlbum *)album; 24 | @end 25 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/AsyncRichTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncRichTextView.m 3 | // LZAlbum 4 | // 5 | // Created by liudonghuan on 16/10/27. 6 | // Copyright © 2016年 lzw. All rights reserved. 7 | // 8 | 9 | #import "AsyncRichTextView.h" 10 | #import "LZAlbumRichTextView.h" 11 | #import 12 | #import "LZAlbumCollectionFlowLayout.h" 13 | #import "LZAlbumPhotoCollectionViewCell.h" 14 | #import "XHImageViewer.h" 15 | #import 16 | 17 | 18 | @interface AsyncRichTextView() 19 | { 20 | 21 | } 22 | @property (nonatomic,strong) UIImageView* avatarImageView; 23 | @property (nonatomic,strong) UICollectionView* shareCollectionView; 24 | @property (nonatomic,strong) UIButton* commentButton; 25 | @property (nonatomic,strong) LZAlbumLikesCommentsView* likesCommentsView; 26 | @end 27 | static NSString* photoCollectionViewIdentifier=@"photoCell"; 28 | @implementation AsyncRichTextView 29 | 30 | /* 31 | // Only override drawRect: if you perform custom drawing. 32 | // An empty implementation adversely affects performance during animation. 33 | - (void)drawRect:(CGRect)rect { 34 | // Drawing code 35 | } 36 | */ 37 | - (instancetype)initWithFrame:(CGRect)frame Album:(LZAlbum *)album; 38 | { 39 | if (self = [super initWithFrame:frame]) { 40 | _album = album; 41 | NSString *text = album.albumShareContent; 42 | [self drawViews]; 43 | [self setupImage]; 44 | } 45 | return self; 46 | } 47 | 48 | -(LZAlbumLikesCommentsView*)likesCommentsView{ 49 | if(_likesCommentsView==nil){ 50 | _likesCommentsView=[[LZAlbumLikesCommentsView alloc] initWithFrame:CGRectZero]; 51 | _likesCommentsView.albumLikesCommentsViewDelegate=self; 52 | } 53 | _likesCommentsView.frame = CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing+kLZAlbumUsernameHeight + _album.contentHeight + _album.collectionHeight + 2*kLZAlbumContentLineSpacing*4, [LZAlbum contentWidth], self.album.commentsHeight); 54 | _likesCommentsView.album = self.album; 55 | return _likesCommentsView; 56 | } 57 | -(UIButton*)commentButton{ 58 | if(_commentButton==nil){ 59 | _commentButton=[[UIButton alloc] initWithFrame:CGRectZero]; 60 | [_commentButton setBackgroundImage:[UIImage imageNamed:@"AlbumOperateMore"] forState:UIControlStateNormal]; 61 | [_commentButton setBackgroundImage:[UIImage imageNamed:@"AlbumOperateMoreHL"] forState:UIControlStateHighlighted]; 62 | [_commentButton addTarget:self action:@selector(didCommentButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 63 | } 64 | _commentButton.frame = CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing + [LZAlbum contentWidth]-kLZAlbumCommentButtonWidth, CGRectGetMaxY(_shareCollectionView.frame)+kLZAlbumContentLineSpacing, kLZAlbumCommentButtonWidth, kLZAlbumCommentButtonHeight); 65 | return _commentButton; 66 | } 67 | 68 | -(UICollectionView*)shareCollectionView{ 69 | if(_shareCollectionView==nil){ 70 | _shareCollectionView=[[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:[[LZAlbumCollectionFlowLayout alloc] init]]; 71 | _shareCollectionView.backgroundColor=[UIColor clearColor]; 72 | [_shareCollectionView registerClass:[LZAlbumPhotoCollectionViewCell class] forCellWithReuseIdentifier:photoCollectionViewIdentifier]; 73 | _shareCollectionView.delegate=self; 74 | _shareCollectionView.dataSource=self; 75 | } 76 | _shareCollectionView.frame=CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing + kLZAlbumUsernameHeight + _album.contentHeight + kLZAlbumContentLineSpacing, kLZAlbumPhotoSize*3+2*kLZAlbumPhotoInset, _album.collectionHeight); 77 | return _shareCollectionView; 78 | } 79 | 80 | -(UIImageView*)avatarImageView{ 81 | if(_avatarImageView==nil){ 82 | _avatarImageView=[[UIImageView alloc] initWithFrame:CGRectMake(kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing, kLZAlbumAvatarImageSize, kLZAlbumAvatarImageSize)]; 83 | } 84 | return _avatarImageView; 85 | } 86 | 87 | - (void)setupImage 88 | { 89 | [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:_album.avatarUrl]]; 90 | } 91 | 92 | - (void)drawViews 93 | { 94 | [self addSubview:self.avatarImageView]; 95 | [self addSubview:self.shareCollectionView]; 96 | [self addSubview:self.commentButton]; 97 | [self addSubview:self.likesCommentsView]; 98 | } 99 | 100 | - (BOOL)drawInRect:(CGRect)rect Context:(CGContextRef)context 101 | { 102 | if (_album == nil) { 103 | return YES; 104 | } 105 | if (_album.username == nil) { 106 | _album.username = @"no name"; 107 | } 108 | // sleep(2); 109 | //名字一般只有一行 110 | NSMutableAttributedString *nameAttributed = [[NSMutableAttributedString alloc]initWithString:_album.username]; 111 | [nameAttributed addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kLZAlbumFontSize],NSForegroundColorAttributeName:RGB(52, 164, 254)} range:NSMakeRange(0, nameAttributed.string.length)]; 112 | [nameAttributed drawInRect:CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing,[LZAlbum contentWidth] , kLZAlbumUsernameHeight)]; 113 | 114 | //时间只有一行 115 | NSMutableAttributedString *timeAttributed = [[NSMutableAttributedString alloc]initWithString:_album.albumShareTimestamp.timeAgoSinceNow]; 116 | [timeAttributed addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12],NSForegroundColorAttributeName:RGB(180, 196, 210)} range:NSMakeRange(0, timeAttributed.string.length)]; 117 | [timeAttributed drawInRect:CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing+kLZAlbumUsernameHeight + _album.contentHeight + _album.collectionHeight + 2*kLZAlbumContentLineSpacing,[LZAlbum contentWidth] , kLZAlbumUsernameHeight)]; 118 | 119 | NSMutableAttributedString *nameAttributed2 = [[NSMutableAttributedString alloc]initWithString:_album.albumShareContent]; 120 | [nameAttributed2 addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kLZAlbumFontSize],NSForegroundColorAttributeName:RGB(52, 164, 254)} range:NSMakeRange(0, _album.albumShareContent.length)]; 121 | // [nameAttributed2 drawInRect:CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing, kLZAlbumAvatarSpacing + kLZAlbumUsernameHeight , [LZAlbum contentWidth], _album.contentHeight)]; 122 | //矩阵变换 123 | CGContextSetTextMatrix(context, CGAffineTransformIdentity); 124 | 125 | 126 | CGContextScaleCTM(context, 1.0, -1.0); 127 | CGContextTranslateCTM(context, 0, -rect.size.height); 128 | 129 | 130 | //正文,图文混排,短链等。需要图文混排,部分文字需要可点击等功能,因此用coretext 131 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_album.albumShareContent]; 132 | [attributedString addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kLZAlbumFontSize],NSForegroundColorAttributeName:[UIColor blackColor]} range:NSMakeRange(0, _album.albumShareContent.length)]; 133 | 134 | 135 | CTFramesetterRef ctFramesetter = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef)attributedString); 136 | CGMutablePathRef path = CGPathCreateMutable(); 137 | CGRect bounds = CGRectMake(kLZAlbumAvatarSpacing+kLZAlbumAvatarImageSize+kLZAlbumAvatarSpacing, kLZAlbumAvatarSpacing + kLZAlbumUsernameHeight + kLZAlbumContentLineSpacing , [LZAlbum contentWidth], rect.size.height); 138 | 139 | CGPathAddRect(path, NULL, [self changeRect:bounds ParentRect:rect]); 140 | 141 | CTFrameRef kFrameRef = CTFramesetterCreateFrame(ctFramesetter,CFRangeMake(0, 0), path, NULL); 142 | CTFrameDraw(kFrameRef, context); 143 | 144 | CFRelease(path); 145 | CFRelease(ctFramesetter); 146 | return YES; 147 | } 148 | 149 | - (CGRect)changeRect:(CGRect)rect ParentRect:(CGRect)pRect; 150 | { 151 | CGRect newRect = rect; 152 | newRect.origin.y = pRect.size.height - rect.size.height - rect.origin.y; 153 | return newRect; 154 | } 155 | 156 | #pragma mark - collection view 157 | 158 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 159 | return _album.albumSharePhotos.count; 160 | } 161 | 162 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 163 | LZAlbumPhotoCollectionViewCell* photoCell=[collectionView dequeueReusableCellWithReuseIdentifier:photoCollectionViewIdentifier forIndexPath:indexPath]; 164 | if(photoCell==nil){ 165 | photoCell=[[LZAlbumPhotoCollectionViewCell alloc] initWithFrame:CGRectMake(0, 0, kLZAlbumPhotoSize, kLZAlbumPhotoSize)]; 166 | } 167 | LZPhoto *photo = self.album.albumSharePhotos[indexPath.row]; 168 | [photoCell.photoImageView sd_setImageWithURL:[NSURL URLWithString:photo.actualThumbnailUrl]]; 169 | photoCell.indexPath=indexPath; 170 | return photoCell; 171 | } 172 | 173 | -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 174 | // self.selectedIndexPath = indexPath; 175 | 176 | LZAlbumPhotoCollectionViewCell* photoCell=(LZAlbumPhotoCollectionViewCell*)[self.shareCollectionView cellForItemAtIndexPath:indexPath]; 177 | 178 | NSArray* visibleCells=self.shareCollectionView.visibleCells; 179 | NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"indexPath" ascending:YES]; 180 | visibleCells = [visibleCells sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]]; 181 | 182 | NSMutableArray* imageViews=[NSMutableArray array]; 183 | [visibleCells enumerateObjectsUsingBlock:^(LZAlbumPhotoCollectionViewCell* cell, NSUInteger idx, BOOL *stop) { 184 | [imageViews addObject:cell.photoImageView]; 185 | }]; 186 | XHImageViewer* imageViewer=[[XHImageViewer alloc] init]; 187 | imageViewer.delegate = self; 188 | [imageViewer showWithImageViews:imageViews selectedView:photoCell.photoImageView]; 189 | } 190 | 191 | #pragma mark - XHImageViewerDelegate 192 | 193 | - (void)imageViewer:(XHImageViewer *)imageViewer didShowImageView:(UIImageView *)selectedView atIndex:(NSInteger)index { 194 | LZPhoto *photo = self.album.albumSharePhotos[index]; 195 | // [self.imageLoadingIndicator startAnimating]; 196 | [selectedView sd_setImageWithURL:[NSURL URLWithString:photo.originUrl] placeholderImage:selectedView.image completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { 197 | // [self.imageLoadingIndicator stopAnimating]; 198 | }]; 199 | } 200 | 201 | - (void)imageViewer:(XHImageViewer *)imageViewer willDismissWithSelectedView:(UIImageView *)selectedView { 202 | // [self.imageLoadingIndicator stopAnimating]; 203 | } 204 | 205 | #pragma mark - action 206 | 207 | -(void)didCommentButtonClick:(UIButton*)sender{ 208 | if([_richTextViewDelegate respondsToSelector:@selector(didCommentButtonClick:)]){ 209 | [_richTextViewDelegate didCommentButtonClick:sender]; 210 | } 211 | } 212 | 213 | -(void)didSelectCommentAtIndexPath:(NSIndexPath *)indexPath{ 214 | if([_richTextViewDelegate respondsToSelector:@selector(didSelectCommentAtIndexPath:)]){ 215 | [_richTextViewDelegate didSelectCommentAtIndexPath:indexPath]; 216 | } 217 | } 218 | 219 | @end 220 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/AsyncView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncView.h 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/10/28. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AsyncView : UIView 12 | // 用于异步绘制的队列,为nil时将使用GCD的global queue进行绘制,默认为nil 13 | @property (nonatomic, assign) dispatch_queue_t dispatchDrawQueue; 14 | 15 | // 异步绘制时global queue的优先级,默认优先级为DEFAULT。在设置了drawQueue时此参数无效。 16 | @property (nonatomic, assign) dispatch_queue_priority_t dispatchPriority; 17 | 18 | - (BOOL)drawInRect:(CGRect)rect Context:(CGContextRef)context; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/AsyncView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncView.m 3 | // LearnObjective-C 4 | // 5 | // Created by donghuan1 on 16/10/28. 6 | // Copyright © 2016年 Dwight. All rights reserved. 7 | // 8 | 9 | #import "AsyncView.h" 10 | 11 | @implementation AsyncView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | self.dispatchPriority = DISPATCH_QUEUE_PRIORITY_DEFAULT; 18 | self.opaque = NO; 19 | self.layer.contentsScale = [[UIScreen mainScreen] scale]; 20 | } 21 | return self; 22 | } 23 | 24 | - (void)didMoveToWindow 25 | { 26 | [super didMoveToWindow]; 27 | 28 | if (!self.window) 29 | { 30 | // 没有 Window 说明View已经没有显示在界面上,此时应该终止绘制 31 | 32 | } 33 | else if (!self.layer.contents) 34 | { 35 | [self.layer setNeedsDisplay]; 36 | } 37 | } 38 | 39 | - (dispatch_queue_t)drawQueue 40 | { 41 | if (self.dispatchDrawQueue) 42 | { 43 | return self.dispatchDrawQueue; 44 | } 45 | 46 | return dispatch_get_global_queue(self.dispatchPriority, 0); 47 | } 48 | 49 | - (void)displayLayer:(CALayer *)layer 50 | { 51 | 52 | dispatch_async([self drawQueue], ^{ 53 | [self displayLayer:layer Rect:layer.frame]; 54 | }); 55 | } 56 | 57 | - (BOOL)drawInRect:(CGRect)rect Context:(CGContextRef)context 58 | { 59 | return YES; 60 | } 61 | 62 | - (void)displayLayer:(CALayer *)layer Rect:(CGRect)rect 63 | { 64 | CGContextRef context = NULL; 65 | UIGraphicsBeginImageContextWithOptions(layer.bounds.size, layer.isOpaque, layer.contentsScale); 66 | context = UIGraphicsGetCurrentContext(); 67 | CGContextSaveGState(context); 68 | 69 | if (rect.origin.x || rect.origin.y) 70 | { 71 | CGContextTranslateCTM(context, rect.origin.x, -rect.origin.y); 72 | } 73 | 74 | if (self.backgroundColor && 75 | self.backgroundColor != [UIColor clearColor]) 76 | { 77 | CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); 78 | CGContextFillRect(context, rect); 79 | } 80 | [self drawInRect:rect Context:context]; 81 | CGContextRestoreGState(context); 82 | 83 | if(true)//所有绘制完成 84 | { 85 | CGImageRef CGImage = context ? CGBitmapContextCreateImage(context) : nil; 86 | 87 | UIImage *image = CGImage ? [UIImage imageWithCGImage:CGImage] : nil; 88 | 89 | dispatch_async(dispatch_get_main_queue(), ^{ 90 | layer.contents = (id)image.CGImage; 91 | }); 92 | if (CGImage) { 93 | //不释放内存一下就爆 94 | CGImageRelease(CGImage); 95 | } 96 | } 97 | UIGraphicsEndImageContext(); 98 | 99 | } 100 | 101 | //共用绘制方法 102 | //绘制文字 103 | 104 | 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumCollectionFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumCollectionFlowLayout.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/27. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LZAlbumCollectionFlowLayout : UICollectionViewFlowLayout 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumCollectionFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumCollectionFlowLayout.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/27. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumCollectionFlowLayout.h" 10 | #import "LZAlbum.h" 11 | 12 | @implementation LZAlbumCollectionFlowLayout 13 | 14 | - (instancetype)init 15 | { 16 | self = [super init]; 17 | if (self) { 18 | self.itemSize=CGSizeMake(kLZAlbumPhotoSize, kLZAlbumPhotoSize); 19 | self.minimumInteritemSpacing=kLZAlbumPhotoInset; 20 | self.minimumLineSpacing=kLZAlbumPhotoInset; 21 | } 22 | return self; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumCommentTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumCommentTableViewCell.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/8/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbum.h" 11 | 12 | @interface LZAlbumCommentTableViewCell : UITableViewCell 13 | 14 | +(CGFloat)calculateCellHeightWithAlbumComment:(LZAlbumComment*)albumComment fixWidth:(CGFloat)width; 15 | 16 | -(void)setupItem:(LZAlbumComment*)item atIndexPath:(NSIndexPath*)indexPath; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumCommentTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumCommentTableViewCell.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/8/29. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbumCommentTableViewCell.h" 11 | 12 | @interface LZAlbumCommentTableViewCell() 13 | 14 | @property (nonatomic,strong) LZAlbumComment* albumComment; 15 | @property (nonatomic,strong) TTTAttributedLabel* commentLabel; 16 | 17 | @end 18 | 19 | @implementation LZAlbumCommentTableViewCell 20 | 21 | - (void)awakeFromNib { 22 | } 23 | 24 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 25 | [super setSelected:selected animated:animated]; 26 | } 27 | 28 | +(NSString*)textOfAlbumComment:(LZAlbumComment*)albumComment{ 29 | if(albumComment==nil){ 30 | return @""; 31 | } 32 | return [NSString stringWithFormat:@"%@ : %@",albumComment.fromUsername,albumComment.commentContent]; 33 | } 34 | 35 | +(CGFloat)calculateCellHeightWithAlbumComment:(LZAlbumComment*)albumComment fixWidth:(CGFloat)width{ 36 | if(albumComment==nil){ 37 | return 0; 38 | } 39 | NSString* text=[albumComment fullCommentText]; 40 | TTTAttributedLabel *mockLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(0, 0, [LZAlbum contentWidth], CGFLOAT_MAX)]; 41 | [[self class] customCommentLabel:mockLabel]; 42 | mockLabel.text = text; 43 | [mockLabel sizeToFit]; 44 | CGFloat height = mockLabel.frame.size.height; 45 | return height; 46 | } 47 | 48 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ 49 | self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]; 50 | if(self){ 51 | self.backgroundColor=[UIColor clearColor]; 52 | self.contentView.backgroundColor=[UIColor clearColor]; 53 | self.selectionStyle = UITableViewCellSelectionStyleDefault; 54 | [self.contentView addSubview:self.commentLabel]; 55 | } 56 | return self; 57 | } 58 | 59 | -(void)layoutSubviews{ 60 | [super layoutSubviews]; 61 | CGRect commentFrame=self.commentLabel.frame; 62 | commentFrame.size.height=[[self class] calculateCellHeightWithAlbumComment:_albumComment fixWidth:[LZAlbum contentWidth]]; 63 | self.commentLabel.frame=commentFrame; 64 | } 65 | 66 | -(void)setupItem:(LZAlbumComment*)item atIndexPath:(NSIndexPath*)indexPath { 67 | _albumComment=item; 68 | NSRange fromUsernameRange = item.fromUsernameRange; 69 | NSRange toUsernameRange = item.toUsernameRange; 70 | NSString *fullCommentText = item.fullCommentText; 71 | if (fullCommentText.length > 0) { 72 | NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:fullCommentText]; 73 | [attributedText setAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor]} range:fromUsernameRange]; 74 | [attributedText setAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor]} range:toUsernameRange]; 75 | [self.commentLabel setText:attributedText afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 76 | return mutableAttributedString; 77 | }]; 78 | if (fromUsernameRange.location != NSNotFound) { 79 | [self.commentLabel addLinkToURL:[NSURL URLWithString:item.fromUsername] withRange:fromUsernameRange]; 80 | } 81 | if (toUsernameRange.location != NSNotFound) { 82 | [self.commentLabel addLinkToURL:[NSURL URLWithString:item.toUsername] withRange:toUsernameRange]; 83 | } 84 | } else { 85 | self.commentLabel.text = nil; 86 | } 87 | } 88 | 89 | -(TTTAttributedLabel *)commentLabel{ 90 | if(_commentLabel==nil){ 91 | _commentLabel=[[TTTAttributedLabel alloc] initWithFrame:CGRectMake(0, 0, [LZAlbum contentWidth], 10)]; 92 | _commentLabel.textColor = [UIColor darkGrayColor]; 93 | _commentLabel.linkAttributes = @{(id)NSUnderlineStyleAttributeName:@(NO), (id)kCTForegroundColorAttributeName:(id)LZLinkTextForegroundColor.CGColor}; 94 | _commentLabel.activeLinkAttributes = @{(id)kTTTBackgroundFillColorAttributeName:(id)LZLinkTextHighlightColor.CGColor}; 95 | _commentLabel.delegate = self; 96 | [[self class] customCommentLabel:_commentLabel]; 97 | } 98 | return _commentLabel; 99 | } 100 | 101 | + (void)customCommentLabel:(TTTAttributedLabel *)label { 102 | label.font=[UIFont systemFontOfSize:kLZAlbumFontSize]; 103 | label.numberOfLines = 0; 104 | label.lineBreakMode = NSLineBreakByWordWrapping; 105 | } 106 | 107 | - (void)attributedLabel:(TTTAttributedLabel *)label 108 | didSelectLinkWithURL:(NSURL *)url { 109 | DLog(@"%@ clicked", url.absoluteString); 110 | } 111 | 112 | - (void)prepareForReuse { 113 | [super prepareForReuse]; 114 | self.commentLabel.text = nil; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumLikesCommentsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumLikesCommentsView.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbum.h" 11 | #import "AsyncView.h" 12 | 13 | static CGFloat kLZAlbumCommentCellUsernameWidth=22; 14 | static CGFloat kLZAlbumCommentIconHeight = 16; 15 | 16 | @protocol MCAlbumLikesCommentsViewDelegate 17 | 18 | -(void)didSelectCommentAtIndexPath:(NSIndexPath*)indexPath; 19 | 20 | @end 21 | 22 | @interface LZAlbumLikesCommentsView : AsyncView 23 | 24 | @property (nonatomic,strong) id albumLikesCommentsViewDelegate; 25 | 26 | @property (nonatomic, strong) LZAlbum *album; 27 | 28 | + (BOOL)shouldShowLikesCommentsViewWithAlbum:(LZAlbum *)album; 29 | + (CGFloat)caculateLikesCommentsViewHeightWithAlbum:(LZAlbum*)album; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumLikesCommentsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumLikesCommentsView.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumLikesCommentsView.h" 10 | #import "LZMacros.h" 11 | #import "LZAlbumCommentTableViewCell.h" 12 | #import 13 | 14 | static NSString* kLZAlbumCommentCellIndetifier=@"albumCommentCell"; 15 | 16 | @interface LZAlbumLikesCommentsView () 17 | 18 | @property (nonatomic,strong) UIView *likeContainerView; 19 | @property (nonatomic,strong) TTTAttributedLabel* likesLabel; 20 | @property (nonatomic,strong) UIImageView *likeIconView; 21 | @property (nonatomic,strong) UITableView *commentTableView; 22 | 23 | @end 24 | 25 | @implementation LZAlbumLikesCommentsView 26 | 27 | + (BOOL)shouldShowLikesViewWithAlbum:(LZAlbum *)album { 28 | return album.albumShareLikes.count > 0; 29 | } 30 | 31 | + (BOOL)shouldShowCommentsViewWithAlbum:(LZAlbum *)album { 32 | return album.albumShareComments.count > 0; 33 | } 34 | 35 | + (BOOL)shouldShowLikesCommentsViewWithAlbum:(LZAlbum *)album { 36 | return [self shouldShowLikesViewWithAlbum:album] | [self shouldShowCommentsViewWithAlbum:album]; 37 | } 38 | 39 | + (CGFloat)caculateLikesViewHeightWithAlbum:(LZAlbum *)album { 40 | if (![self shouldShowLikesViewWithAlbum:album]) { 41 | return 0; 42 | } 43 | TTTAttributedLabel *mockLikesLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(0, 0, [LZAlbum contentWidth] - kLZAlbumCommentIconHeight, CGFLOAT_MAX)]; 44 | [[self class] customLikesLabel:mockLikesLabel]; 45 | [[self class] reloadLikesWithAlbum:album likesLabel:mockLikesLabel]; 46 | [mockLikesLabel sizeToFit]; 47 | return CGRectGetHeight(mockLikesLabel.frame); 48 | } 49 | 50 | + (CGFloat)caculateCommentsViewHeightWithAlbum:(LZAlbum *)album { 51 | CGFloat fixWidth=[LZAlbum contentWidth]; 52 | CGFloat commentsHeight=0; 53 | for(LZAlbumComment* albumComment in album.albumShareComments){ 54 | commentsHeight+=[LZAlbumCommentTableViewCell calculateCellHeightWithAlbumComment:albumComment fixWidth:fixWidth]; 55 | } 56 | return commentsHeight; 57 | } 58 | 59 | + (CGFloat)caculateLikesCommentsViewHeightWithAlbum:(LZAlbum*)album { 60 | CGFloat height=0; 61 | if ([self shouldShowLikesViewWithAlbum:album]) { 62 | height += [self caculateLikesViewHeightWithAlbum:album]; 63 | } 64 | if([self shouldShowCommentsViewWithAlbum:album]){ 65 | height += [self caculateCommentsViewHeightWithAlbum:album]; 66 | } 67 | return height; 68 | } 69 | 70 | - (instancetype)initWithFrame:(CGRect)frame 71 | { 72 | self = [super initWithFrame:frame]; 73 | if (self) { 74 | self.clipsToBounds=YES; 75 | self.backgroundColor=RGB(240, 245, 249); 76 | [self addSubview:self.likeContainerView]; 77 | [self addSubview:self.commentTableView]; 78 | } 79 | return self; 80 | } 81 | 82 | #pragma mark - Propertys 83 | 84 | -(UIImageView*)likeIconView{ 85 | if(_likeIconView==nil){ 86 | _likeIconView=[[UIImageView alloc] initWithFrame:CGRectMake(0, 2, kLZAlbumCommentIconHeight, kLZAlbumCommentIconHeight)]; 87 | _likeIconView.image = [UIImage imageNamed:@"AlbumInformationLikeHL"]; 88 | } 89 | return _likeIconView; 90 | } 91 | 92 | -(TTTAttributedLabel *)likesLabel{ 93 | if(_likesLabel==nil){ 94 | _likesLabel= [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.likeIconView.frame), 0, [LZAlbum contentWidth] - CGRectGetWidth(self.likeIconView.frame), CGFLOAT_MAX)]; 95 | _likesLabel.linkAttributes = @{(id)NSUnderlineStyleAttributeName:@(NO), (id)kCTForegroundColorAttributeName:(id)LZLinkTextForegroundColor.CGColor}; 96 | _likesLabel.activeLinkAttributes = @{(id)kTTTBackgroundFillColorAttributeName:(id)LZLinkTextHighlightColor.CGColor}; 97 | _likesLabel.textColor = [UIColor darkGrayColor]; 98 | _likesLabel.delegate = self; 99 | [[self class] customLikesLabel:_likesLabel]; 100 | } 101 | return _likesLabel; 102 | } 103 | 104 | + (void)customLikesLabel:(TTTAttributedLabel *)likesLabel { 105 | likesLabel.font = [UIFont systemFontOfSize:kLZAlbumFontSize]; 106 | likesLabel.numberOfLines = 0; 107 | likesLabel.lineBreakMode = NSLineBreakByWordWrapping; 108 | } 109 | 110 | -(UIView*)likeContainerView{ 111 | if(_likeContainerView==nil){ 112 | _likeContainerView=[[UIView alloc] initWithFrame:CGRectZero]; 113 | _likeContainerView.autoresizesSubviews=YES; 114 | [_likeContainerView addSubview:self.likeIconView]; 115 | [_likeContainerView addSubview:self.likesLabel]; 116 | _likeContainerView.clipsToBounds = YES; 117 | } 118 | return _likeContainerView; 119 | } 120 | 121 | -(UITableView*)commentTableView{ 122 | if(_commentTableView==nil){ 123 | _commentTableView=[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 124 | _commentTableView.separatorColor=[UIColor clearColor]; 125 | _commentTableView.separatorStyle=UITableViewCellSeparatorStyleNone; 126 | [_commentTableView registerClass:[LZAlbumCommentTableViewCell class] forCellReuseIdentifier:kLZAlbumCommentCellIndetifier]; 127 | _commentTableView.dataSource=self; 128 | _commentTableView.delegate=self; 129 | _commentTableView.scrollEnabled=NO; 130 | _commentTableView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 131 | _commentTableView.backgroundColor=[UIColor clearColor]; 132 | } 133 | return _commentTableView; 134 | } 135 | 136 | #pragma mark - 137 | 138 | - (void)attributedLabel:(TTTAttributedLabel *)label 139 | didSelectLinkWithURL:(NSURL *)url { 140 | DLog(); 141 | } 142 | 143 | - (void)setAlbum:(LZAlbum *)album { 144 | _album = album; 145 | [[self class] reloadLikesWithAlbum:album likesLabel:self.likesLabel]; 146 | [self.commentTableView reloadData]; 147 | [self setNeedsLayout]; 148 | } 149 | 150 | + (void)reloadLikesWithAlbum:(LZAlbum *)album likesLabel:(TTTAttributedLabel *)likesLabel { 151 | NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init]; 152 | NSMutableArray *locations = [NSMutableArray array]; 153 | for(int i=0;i0; 172 | BOOL shouldShowComment=self.album.albumShareComments.count>0; 173 | 174 | if(!shouldShowLike && !shouldShowComment){ 175 | self.frame=CGRectZero; 176 | return; 177 | } 178 | 179 | CGFloat likesViewHeight = [[self class] caculateLikesViewHeightWithAlbum:self.album]; 180 | CGRect likesLabelFrame = self.likesLabel.frame; 181 | likesLabelFrame.size.height = likesViewHeight; 182 | self.likesLabel.frame = likesLabelFrame; 183 | 184 | CGRect likeContainerFrame=self.likeContainerView.frame; 185 | likeContainerFrame.size=CGSizeMake(CGRectGetWidth(self.bounds), likesViewHeight); 186 | self.likeContainerView.frame=likeContainerFrame; 187 | 188 | CGRect commentTableFrame=self.commentTableView.frame; 189 | commentTableFrame.origin.y=CGRectGetMaxY(self.likeContainerView.frame); 190 | commentTableFrame.size.height = [[self class] caculateCommentsViewHeightWithAlbum:self.album]; 191 | self.commentTableView.frame=commentTableFrame; 192 | 193 | // CGRect frame=self.frame; 194 | // frame.size.height= CGRectGetMaxY(self.commentTableView.frame); 195 | // self.frame=frame; 196 | } 197 | 198 | #pragma mark - UITablView DataSource 199 | 200 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 201 | return self.album.albumShareComments.count; 202 | } 203 | 204 | -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 205 | LZAlbumCommentTableViewCell* commentCell=[tableView dequeueReusableCellWithIdentifier:kLZAlbumCommentCellIndetifier forIndexPath:indexPath]; 206 | if(commentCell==nil){ 207 | commentCell=[[LZAlbumCommentTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kLZAlbumCommentCellIndetifier]; 208 | } 209 | LZAlbumComment* albumComment=self.album.albumShareComments[indexPath.row]; 210 | [commentCell setupItem:albumComment atIndexPath:indexPath]; 211 | return commentCell; 212 | } 213 | 214 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 215 | LZAlbumComment* albumComment=self.album.albumShareComments[indexPath.row]; 216 | CGFloat cellHeight=[LZAlbumCommentTableViewCell calculateCellHeightWithAlbumComment:albumComment fixWidth:[LZAlbum contentWidth]]; 217 | return cellHeight; 218 | } 219 | 220 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 221 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 222 | if([self.albumLikesCommentsViewDelegate respondsToSelector:@selector(didSelectCommentAtIndexPath:)]){ 223 | [self.albumLikesCommentsViewDelegate didSelectCommentAtIndexPath:indexPath]; 224 | } 225 | } 226 | 227 | @end 228 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumOperationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumOperationView.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger{ 12 | MCAlbumOperationTypeLike=0, 13 | MCAlbumOperationTypeReply 14 | }MCAlbumOperationType; 15 | 16 | static CGFloat kLZAlbumOperationViewWidth=120; 17 | static CGFloat kLZAlbumOperationViewHeight=34; 18 | 19 | @class LZAlbumOperationView; 20 | 21 | 22 | @protocol MCAlbumOperationViewDelegate 23 | 24 | @optional 25 | 26 | -(void)albumOperationView:(LZAlbumOperationView*)albumOperationView didClickOfType:(MCAlbumOperationType)operationType; 27 | 28 | @end 29 | 30 | @interface LZAlbumOperationView : UIView 31 | 32 | @property (nonatomic,strong) id albumOperationViewDelegate; 33 | 34 | @property (nonatomic,assign) BOOL shouldShowed; 35 | 36 | +(instancetype)initialOperationView; 37 | 38 | -(void)showAtView:(UIView*)containerView rect:(CGRect)targetRect; 39 | 40 | -(void)dismiss; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumOperationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumOperationView.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/28. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumOperationView.h" 10 | 11 | @interface LZAlbumOperationView () 12 | 13 | @property (nonatomic,strong) UIButton* likeButton; 14 | @property (nonatomic,strong) UIButton* replyButton; 15 | @property (nonatomic,assign) CGRect targetRect; 16 | 17 | @end 18 | 19 | @implementation LZAlbumOperationView 20 | 21 | +(instancetype)initialOperationView{ 22 | LZAlbumOperationView* operationView=[[LZAlbumOperationView alloc] initWithFrame:CGRectZero]; 23 | return operationView; 24 | } 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | self.backgroundColor=[UIColor colorWithWhite:0.0 alpha:0.8]; 31 | self.layer.masksToBounds=YES; 32 | self.layer.cornerRadius=5; 33 | [self addSubview:self.likeButton]; 34 | [self addSubview:self.replyButton]; 35 | } 36 | return self; 37 | } 38 | 39 | -(UIButton*)likeButton{ 40 | if(_likeButton==nil){ 41 | _likeButton=[[UIButton alloc] initWithFrame:CGRectMake(0, 0, kLZAlbumOperationViewWidth/2, kLZAlbumOperationViewHeight)]; 42 | _likeButton.tag=MCAlbumOperationTypeLike; 43 | _likeButton.titleLabel.font=[UIFont systemFontOfSize:14]; 44 | [_likeButton addTarget:self action:@selector(operationViewDidClick:) forControlEvents:UIControlEventTouchUpInside]; 45 | } 46 | return _likeButton; 47 | } 48 | 49 | -(UIButton*)replyButton{ 50 | if(_replyButton==nil){ 51 | _replyButton=[[UIButton alloc] initWithFrame:CGRectMake(kLZAlbumOperationViewWidth/2, 0, kLZAlbumOperationViewWidth/2, kLZAlbumOperationViewHeight)]; 52 | _replyButton.tag=MCAlbumOperationTypeReply; 53 | _replyButton.titleLabel.font=[UIFont systemFontOfSize:14]; 54 | [_replyButton addTarget:self action:@selector(operationViewDidClick:) forControlEvents:UIControlEventTouchUpInside]; 55 | } 56 | return _replyButton; 57 | } 58 | 59 | -(void)operationViewDidClick:(UIButton*)sender{ 60 | if([_albumOperationViewDelegate respondsToSelector:@selector(albumOperationView:didClickOfType:)]){ 61 | [_albumOperationViewDelegate albumOperationView:self didClickOfType:sender.tag]; 62 | } 63 | } 64 | 65 | -(void)showAtView:(UIView*)containerView rect:(CGRect)targetRect{ 66 | if(_shouldShowed){ 67 | return; 68 | } 69 | _shouldShowed=YES; 70 | self.targetRect=targetRect; 71 | [containerView addSubview:self]; 72 | 73 | self.frame=CGRectMake(targetRect.origin.x, targetRect.origin.y, 0, kLZAlbumOperationViewHeight); 74 | 75 | [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ 76 | self.frame=CGRectMake(targetRect.origin.x-kLZAlbumOperationViewWidth, targetRect.origin.y, kLZAlbumOperationViewWidth, kLZAlbumOperationViewHeight); 77 | } completion:^(BOOL finished) { 78 | [_likeButton setTitle:@"赞" forState:UIControlStateNormal]; 79 | [_replyButton setTitle:@"评论" forState:UIControlStateNormal]; 80 | }]; 81 | } 82 | 83 | -(void)dismiss{ 84 | if(self.shouldShowed==NO){ 85 | return; 86 | } 87 | self.shouldShowed=NO; 88 | [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 89 | self.frame=CGRectMake(_targetRect.origin.x, _targetRect.origin.y, 0, kLZAlbumOperationViewHeight); 90 | } completion:^(BOOL finished) { 91 | [_likeButton setTitle:nil forState:UIControlStateNormal]; 92 | [_replyButton setTitle:nil forState:UIControlStateNormal]; 93 | [self removeFromSuperview]; 94 | }]; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumPhotoCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumPhotoCollectionViewCell.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/27. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LZAlbumPhotoCollectionViewCell : UICollectionViewCell 12 | 13 | @property (nonatomic,strong) NSIndexPath* indexPath; 14 | 15 | @property (nonatomic,strong) UIImageView* photoImageView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumPhotoCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumPhotoCollectionViewCell.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/27. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumPhotoCollectionViewCell.h" 10 | 11 | @implementation LZAlbumPhotoCollectionViewCell 12 | 13 | -(UIImageView*)photoImageView{ 14 | if(_photoImageView==nil){ 15 | _photoImageView=[[UIImageView alloc] initWithFrame:self.bounds]; 16 | _photoImageView.contentMode=UIViewContentModeScaleAspectFill; 17 | _photoImageView.layer.masksToBounds=YES; 18 | } 19 | return _photoImageView; 20 | } 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame 23 | { 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | [self.contentView addSubview:self.photoImageView]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)prepareForReuse { 32 | [super prepareForReuse]; 33 | self.photoImageView.image = nil; 34 | self.indexPath = nil; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumReplyView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumReplyView.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/30. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | static CGFloat kLZAlbumReplyViewPadding=10; 11 | static CGFloat kLZAlbumReplyViewHeight=44; 12 | 13 | @class LZAlbumReplyView; 14 | 15 | @protocol MCAlbumReplyViewDelegate 16 | 17 | -(void)albumReplyView:(LZAlbumReplyView*)albumReplyView didReply:(NSString*)text; 18 | 19 | @end 20 | 21 | @interface LZAlbumReplyView : UIView 22 | 23 | @property (nonatomic,strong) id albumReplyViewDelegate; 24 | 25 | -(void)show; 26 | 27 | -(void)dismiss; 28 | 29 | -(void)finishReply; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumReplyView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumReplyView.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/30. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumReplyView.h" 10 | #import "LZInputAccessoryView.h" 11 | 12 | @interface LZAlbumReplyView () 13 | 14 | @property (nonatomic,strong) UITextField* textField; 15 | 16 | @property (nonatomic,strong) UITextField* inputTextField; 17 | 18 | @property (nonatomic,strong) LZInputAccessoryView* customInputAccessoryView; 19 | 20 | @end 21 | 22 | @implementation LZAlbumReplyView 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame 25 | { 26 | self = [super initWithFrame:frame]; 27 | if (self) { 28 | self.backgroundColor=[UIColor colorWithWhite:0.910 alpha:1.000]; 29 | [self addSubview:self.textField]; 30 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow) name:UIKeyboardDidShowNotification object:nil]; 31 | } 32 | return self; 33 | } 34 | 35 | -(void)dealloc{ 36 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil]; 37 | } 38 | 39 | -(void)keyboardDidShow{ 40 | [self becomeFirstResponderForInputTextField]; 41 | } 42 | 43 | -(void)becomeFirstResponderForTextField{ 44 | if([self.textField isFirstResponder]==NO){ 45 | [self.textField becomeFirstResponder]; 46 | } 47 | } 48 | 49 | -(void)becomeFirstResponderForInputTextField{ 50 | if([self.inputTextField isFirstResponder]==NO){ 51 | [self.inputTextField becomeFirstResponder]; 52 | } 53 | } 54 | 55 | -(void)resignFirstResponderForTwoTextFields{ 56 | if([self.inputTextField isFirstResponder]){ 57 | [self.inputTextField resignFirstResponder]; 58 | } 59 | 60 | if([self.textField isFirstResponder]){ 61 | [self.textField resignFirstResponder]; 62 | } 63 | CGRect frame = self.customInputAccessoryView.keyboardViewProxy.frame; 64 | CGFloat keyboardMaxH = CGRectGetHeight([UIScreen mainScreen].bounds); 65 | if (self.customInputAccessoryView.keyboardViewProxy != nil && frame.origin.y != keyboardMaxH) { 66 | frame.origin.y = keyboardMaxH; 67 | self.customInputAccessoryView.keyboardViewProxy.frame = frame; 68 | } 69 | } 70 | 71 | -(void)show{ 72 | [self becomeFirstResponderForTextField]; 73 | } 74 | 75 | -(void)dismiss{ 76 | [self resignFirstResponderForTwoTextFields]; 77 | } 78 | 79 | -(UITextField*)textField{ 80 | if(_textField==nil){ 81 | _textField=[[UITextField alloc] initWithFrame:CGRectZero]; 82 | _textField.inputAccessoryView=self.customInputAccessoryView; 83 | _textField.returnKeyType=UIReturnKeySend; 84 | } 85 | return _textField; 86 | } 87 | 88 | -(UIView*)customInputAccessoryView{ 89 | if(_customInputAccessoryView==nil){ 90 | _customInputAccessoryView=[[LZInputAccessoryView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), 44)]; 91 | _customInputAccessoryView.backgroundColor = [UIColor colorWithWhite:0.910 alpha:1.000]; 92 | [_customInputAccessoryView addSubview:self.inputTextField]; 93 | } 94 | return _customInputAccessoryView; 95 | } 96 | 97 | -(UITextField*)inputTextField{ 98 | if(_inputTextField==nil){ 99 | _inputTextField=[[UITextField alloc] initWithFrame:CGRectMake(kLZAlbumReplyViewPadding, kLZAlbumReplyViewPadding, CGRectGetWidth(_customInputAccessoryView.frame)-kLZAlbumReplyViewPadding*2, 44-kLZAlbumReplyViewPadding*2)]; 100 | _inputTextField.delegate=self; 101 | _inputTextField.returnKeyType=UIReturnKeySend; 102 | _inputTextField.borderStyle=UITextBorderStyleRoundedRect; 103 | } 104 | return _inputTextField; 105 | } 106 | 107 | -(BOOL)textFieldShouldReturn:(UITextField *)textField{ 108 | if(textField.text.length>0){ 109 | if([_albumReplyViewDelegate respondsToSelector:@selector(albumReplyView:didReply:)]){ 110 | [_albumReplyViewDelegate albumReplyView:self didReply:textField.text]; 111 | } 112 | } 113 | return YES; 114 | } 115 | 116 | -(void)finishReply{ 117 | [self resignFirstResponderForTwoTextFields]; 118 | self.inputTextField.text=nil; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumRichTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumRichTextView.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbum.h" 11 | #import "LZAlbumLikesCommentsView.h" 12 | #import "AsyncView.h" 13 | 14 | static const CGFloat kLZAlbumUsernameHeight=18.0f; 15 | static const CGFloat kLZAlbumContentLineSpacing=4.0f; 16 | static const CGFloat kLZAlbumCommentButtonWidth=25.0f; 17 | static const CGFloat kLZAlbumCommentButtonHeight=25.0f; 18 | 19 | @protocol LZAlbumRichTextViewDelegate 20 | 21 | -(void)didCommentButtonClick:(UIButton*)button; 22 | 23 | -(void)didSelectCommentAtIndexPath:(NSIndexPath*)indexPath; 24 | 25 | @end 26 | 27 | @interface LZAlbumRichTextView : UIView 28 | 29 | @property (nonatomic,strong) LZAlbum* album; 30 | 31 | @property (nonatomic,strong) id richTextViewDelegate; 32 | 33 | +(CGFloat)calculateRichTextHeightWithAlbum:(LZAlbum*)album; 34 | 35 | +(CGFloat)getLabelHeightWithText:(NSString*)text maxWidth:(CGFloat)maxWidth font:(NSFont*)font; 36 | +(CGFloat)getContentLabelHeightWithAlbum:(LZAlbum *)album; 37 | +(CGFloat)getPhotoCollectionViewHeightWithAlbum:(LZAlbum *)album; 38 | @end 39 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumRichTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumRichTextView.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumRichTextView.h" 10 | #import "LZAlbumCollectionFlowLayout.h" 11 | #import "LZAlbumPhotoCollectionViewCell.h" 12 | #import "LZAlbumLikesCommentsView.h" 13 | #import "LZAlbumCommentTableViewCell.h" 14 | #import "LZMacros.h" 15 | #import "XHImageViewer.h" 16 | #import 17 | #import 18 | 19 | @interface LZAlbumRichTextView() 20 | 21 | @property (nonatomic,strong) UIImageView* avatarImageView; 22 | 23 | @property (nonatomic,strong) UILabel* usernameLabel; 24 | 25 | @property (nonatomic,strong) UILabel* contentLabel; 26 | 27 | @property (nonatomic,strong) UICollectionView* shareCollectionView; 28 | 29 | @property (nonatomic,strong) UILabel* timestampLabel; 30 | 31 | @property (nonatomic,strong) UIButton* commentButton; 32 | 33 | @property (nonatomic,strong) LZAlbumLikesCommentsView* likesCommentsView; 34 | 35 | @property (nonatomic, strong) NSIndexPath *selectedIndexPath; 36 | 37 | @property (nonatomic, strong) UIActivityIndicatorView *imageLoadingIndicator; 38 | 39 | @end 40 | 41 | static NSString* photoCollectionViewIdentifier=@"photoCell"; 42 | 43 | @implementation LZAlbumRichTextView 44 | 45 | +(NSFont*)contentFont{ 46 | return [UIFont systemFontOfSize:kLZAlbumFontSize]; 47 | } 48 | 49 | +(CGFloat)getLabelHeightWithText:(NSString*)text maxWidth:(CGFloat)maxWidth font:(NSFont*)font{ 50 | return [text boundingRectWithSize:CGSizeMake(maxWidth,CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size.height; 51 | } 52 | 53 | +(CGFloat)getContentLabelHeightWithAlbum:(LZAlbum *)album{ 54 | NSString *text = album.albumShareContent; 55 | if(text.length == 0){ 56 | return 0; 57 | } 58 | return [self getLabelHeightWithText:text maxWidth:[LZAlbum contentWidth] font:[self contentFont]]; 59 | } 60 | 61 | +(CGFloat)getPhotoCollectionViewHeightWithAlbum:(LZAlbum *)album{ 62 | NSInteger photoCount = album.albumSharePhotos.count; 63 | if(photoCount==0){ 64 | return 0; 65 | } 66 | NSInteger row=photoCount/3+(photoCount%3 ? 1:0); 67 | return row*kLZAlbumPhotoSize+(row-1)*kLZAlbumPhotoInset; 68 | } 69 | 70 | +(CGFloat)calculateRichTextHeightWithAlbum:(LZAlbum*)album { 71 | if(album==nil) { 72 | return 0; 73 | } 74 | CGFloat richTextHeight=kLZAlbumAvatarSpacing; 75 | richTextHeight += kLZAlbumUsernameHeight; 76 | if ([self shouldShowContentLabelWithAlbum:album]) { 77 | richTextHeight += kLZAlbumContentLineSpacing; 78 | richTextHeight += [[self class] getContentLabelHeightWithAlbum:album]; 79 | } 80 | if ([self shouldShowPhotoCollectionViewWithAlbum:album]) { 81 | richTextHeight += kLZAlbumContentLineSpacing; 82 | richTextHeight += [[self class] getPhotoCollectionViewHeightWithAlbum:album]; 83 | } 84 | richTextHeight+=kLZAlbumContentLineSpacing; 85 | richTextHeight+=kLZAlbumCommentButtonHeight; 86 | if([LZAlbumLikesCommentsView shouldShowLikesCommentsViewWithAlbum:album]) { 87 | richTextHeight += kLZAlbumContentLineSpacing; 88 | richTextHeight += [LZAlbumLikesCommentsView caculateLikesCommentsViewHeightWithAlbum:album]; 89 | } 90 | richTextHeight+=kLZAlbumAvatarSpacing; 91 | return richTextHeight; 92 | } 93 | 94 | #pragma mark - should show 95 | 96 | + (BOOL)shouldShowContentLabelWithAlbum:(LZAlbum *)album { 97 | return album.albumShareContent.length > 0; 98 | } 99 | 100 | + (BOOL)shouldShowPhotoCollectionViewWithAlbum:(LZAlbum *)album { 101 | return album.albumSharePhotos.count > 0; 102 | } 103 | 104 | #pragma mark - View init 105 | 106 | -(UIImageView*)avatarImageView{ 107 | if(_avatarImageView==nil){ 108 | _avatarImageView=[[UIImageView alloc] initWithFrame:CGRectMake(kLZAlbumAvatarSpacing,kLZAlbumAvatarSpacing, kLZAlbumAvatarImageSize, kLZAlbumAvatarImageSize)]; 109 | } 110 | return _avatarImageView; 111 | } 112 | 113 | -(UILabel*)usernameLabel{ 114 | if(_usernameLabel==nil){ 115 | _usernameLabel=[[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(_avatarImageView.frame)+kLZAlbumAvatarSpacing, kLZAlbumAvatarSpacing,[LZAlbum contentWidth] , kLZAlbumUsernameHeight)]; 116 | _usernameLabel.backgroundColor=[UIColor clearColor]; 117 | _usernameLabel.textColor=RGB(52, 164, 254); 118 | } 119 | return _usernameLabel; 120 | } 121 | 122 | -(UILabel*)contentLabel{ 123 | if(!_contentLabel){ 124 | _contentLabel=[[UILabel alloc] initWithFrame:CGRectZero]; 125 | _contentLabel.font=[UIFont systemFontOfSize:kLZAlbumFontSize]; 126 | _contentLabel.backgroundColor=[UIColor clearColor]; 127 | _contentLabel.numberOfLines=0; 128 | } 129 | return _contentLabel; 130 | } 131 | 132 | -(UICollectionView*)shareCollectionView{ 133 | if(_shareCollectionView==nil){ 134 | _shareCollectionView=[[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:[[LZAlbumCollectionFlowLayout alloc] init]]; 135 | _shareCollectionView.backgroundColor=[UIColor clearColor]; 136 | [_shareCollectionView registerClass:[LZAlbumPhotoCollectionViewCell class] forCellWithReuseIdentifier:photoCollectionViewIdentifier]; 137 | _shareCollectionView.delegate=self; 138 | _shareCollectionView.dataSource=self; 139 | } 140 | return _shareCollectionView; 141 | } 142 | 143 | -(UILabel*)timestampLabel{ 144 | if(_timestampLabel==nil){ 145 | _timestampLabel=[[UILabel alloc] initWithFrame:CGRectZero]; 146 | _timestampLabel.font=[UIFont systemFontOfSize:12]; 147 | _timestampLabel.textColor=RGB(180, 196, 210); 148 | } 149 | return _timestampLabel; 150 | } 151 | 152 | -(UIButton*)commentButton{ 153 | if(_commentButton==nil){ 154 | _commentButton=[[UIButton alloc] initWithFrame:CGRectZero]; 155 | [_commentButton setBackgroundImage:[UIImage imageNamed:@"AlbumOperateMore"] forState:UIControlStateNormal]; 156 | [_commentButton setBackgroundImage:[UIImage imageNamed:@"AlbumOperateMoreHL"] forState:UIControlStateHighlighted]; 157 | [_commentButton addTarget:self action:@selector(didCommentButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 158 | } 159 | return _commentButton; 160 | } 161 | 162 | -(LZAlbumLikesCommentsView*)likesCommentsView{ 163 | if(_likesCommentsView==nil){ 164 | _likesCommentsView=[[LZAlbumLikesCommentsView alloc] initWithFrame:CGRectZero]; 165 | _likesCommentsView.albumLikesCommentsViewDelegate=self; 166 | } 167 | return _likesCommentsView; 168 | } 169 | 170 | - (UIActivityIndicatorView *)imageLoadingIndicator { 171 | if (_imageLoadingIndicator == nil) { 172 | _imageLoadingIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 173 | _imageLoadingIndicator.center = self.window.center; 174 | _imageLoadingIndicator.hidesWhenStopped = YES; 175 | _imageLoadingIndicator.hidden = NO; 176 | [self.window addSubview:_imageLoadingIndicator]; 177 | } 178 | [self.window bringSubviewToFront:_imageLoadingIndicator]; 179 | return _imageLoadingIndicator; 180 | } 181 | 182 | -(void)setAlbum:(LZAlbum *)album{ 183 | _album=album; 184 | self.usernameLabel.text=_album.username; 185 | self.contentLabel.text=album.albumShareContent; 186 | [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:album.avatarUrl]]; 187 | [self.shareCollectionView reloadData]; 188 | self.timestampLabel.text=_album.albumShareTimestamp.timeAgoSinceNow; 189 | 190 | self.likesCommentsView.album = album; 191 | 192 | [self setNeedsLayout]; 193 | } 194 | 195 | -(void)setup{ 196 | [self addSubview:self.avatarImageView]; 197 | [self addSubview:self.usernameLabel]; 198 | [self addSubview:self.contentLabel]; 199 | [self addSubview:self.shareCollectionView]; 200 | [self addSubview:self.timestampLabel]; 201 | [self addSubview:self.commentButton]; 202 | [self addSubview:self.likesCommentsView]; 203 | } 204 | 205 | - (instancetype)initWithFrame:(CGRect)frame 206 | { 207 | self = [super initWithFrame:frame]; 208 | if (self) { 209 | [self setup]; 210 | } 211 | return self; 212 | } 213 | 214 | - (void)resetSubviewRects 215 | { 216 | 217 | } 218 | 219 | -(void)layoutSubviews{ 220 | [super layoutSubviews]; 221 | 222 | CGFloat contentLabelY = CGRectGetMaxY(_usernameLabel.frame); 223 | if ([[self class] shouldShowContentLabelWithAlbum:self.album]) { 224 | contentLabelY += kLZAlbumContentLineSpacing; 225 | } 226 | CGRect contentFrame = CGRectMake(CGRectGetMinX(_usernameLabel.frame), contentLabelY, CGRectGetWidth(_usernameLabel.frame), [[self class] getContentLabelHeightWithAlbum:_album]); 227 | _contentLabel.frame=contentFrame; 228 | 229 | CGFloat shareCollectionViewY = CGRectGetMaxY(_contentLabel.frame); 230 | if ([[self class] shouldShowPhotoCollectionViewWithAlbum:self.album]) { 231 | shareCollectionViewY += kLZAlbumContentLineSpacing; 232 | } 233 | _shareCollectionView.frame=CGRectMake(CGRectGetMinX(_usernameLabel.frame),shareCollectionViewY, kLZAlbumPhotoSize*3+2*kLZAlbumPhotoInset, [[self class] getPhotoCollectionViewHeightWithAlbum:self.album]); 234 | 235 | _commentButton.frame=CGRectMake(CGRectGetMaxX(_contentLabel.frame)-kLZAlbumCommentButtonWidth, CGRectGetMaxY(_shareCollectionView.frame)+kLZAlbumContentLineSpacing, kLZAlbumCommentButtonWidth, kLZAlbumCommentButtonHeight); 236 | 237 | _timestampLabel.frame=CGRectMake(CGRectGetMinX(_contentLabel.frame), CGRectGetMinY(_commentButton.frame), CGRectGetWidth(_contentLabel.frame)-kLZAlbumCommentButtonWidth, CGRectGetHeight(_commentButton.frame)); 238 | 239 | CGFloat likesCommentsViewY = CGRectGetMaxY(_timestampLabel.frame); 240 | if ([LZAlbumLikesCommentsView shouldShowLikesCommentsViewWithAlbum:self.album]) { 241 | likesCommentsViewY += kLZAlbumContentLineSpacing; 242 | } 243 | _likesCommentsView.frame=CGRectMake(CGRectGetMinX(_timestampLabel.frame), likesCommentsViewY , CGRectGetWidth(_contentLabel.frame), [LZAlbumLikesCommentsView caculateLikesCommentsViewHeightWithAlbum:_album]); 244 | 245 | CGRect frame=self.frame; 246 | frame.size.height = CGRectGetMaxY(_likesCommentsView.frame) + kLZAlbumAvatarSpacing; 247 | self.frame=frame; 248 | } 249 | 250 | #pragma mark - collection view 251 | 252 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 253 | return _album.albumSharePhotos.count; 254 | } 255 | 256 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 257 | LZAlbumPhotoCollectionViewCell* photoCell=[collectionView dequeueReusableCellWithReuseIdentifier:photoCollectionViewIdentifier forIndexPath:indexPath]; 258 | if(photoCell==nil){ 259 | photoCell=[[LZAlbumPhotoCollectionViewCell alloc] initWithFrame:CGRectMake(0, 0, kLZAlbumPhotoSize, kLZAlbumPhotoSize)]; 260 | } 261 | LZPhoto *photo = self.album.albumSharePhotos[indexPath.row]; 262 | [photoCell.photoImageView sd_setImageWithURL:[NSURL URLWithString:photo.actualThumbnailUrl]]; 263 | photoCell.indexPath=indexPath; 264 | return photoCell; 265 | } 266 | 267 | -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 268 | self.selectedIndexPath = indexPath; 269 | 270 | LZAlbumPhotoCollectionViewCell* photoCell=(LZAlbumPhotoCollectionViewCell*)[self.shareCollectionView cellForItemAtIndexPath:indexPath]; 271 | 272 | NSArray* visibleCells=self.shareCollectionView.visibleCells; 273 | NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"indexPath" ascending:YES]; 274 | visibleCells = [visibleCells sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]]; 275 | 276 | NSMutableArray* imageViews=[NSMutableArray array]; 277 | [visibleCells enumerateObjectsUsingBlock:^(LZAlbumPhotoCollectionViewCell* cell, NSUInteger idx, BOOL *stop) { 278 | [imageViews addObject:cell.photoImageView]; 279 | }]; 280 | XHImageViewer* imageViewer=[[XHImageViewer alloc] init]; 281 | imageViewer.delegate = self; 282 | [imageViewer showWithImageViews:imageViews selectedView:photoCell.photoImageView]; 283 | } 284 | 285 | #pragma mark - XHImageViewerDelegate 286 | 287 | - (void)imageViewer:(XHImageViewer *)imageViewer didShowImageView:(UIImageView *)selectedView atIndex:(NSInteger)index { 288 | LZPhoto *photo = self.album.albumSharePhotos[index]; 289 | [self.imageLoadingIndicator startAnimating]; 290 | [selectedView sd_setImageWithURL:[NSURL URLWithString:photo.originUrl] placeholderImage:selectedView.image completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { 291 | [self.imageLoadingIndicator stopAnimating]; 292 | }]; 293 | } 294 | 295 | - (void)imageViewer:(XHImageViewer *)imageViewer willDismissWithSelectedView:(UIImageView *)selectedView { 296 | [self.imageLoadingIndicator stopAnimating]; 297 | } 298 | 299 | #pragma mark - action 300 | 301 | -(void)didCommentButtonClick:(UIButton*)sender{ 302 | if([_richTextViewDelegate respondsToSelector:@selector(didCommentButtonClick:)]){ 303 | [_richTextViewDelegate didCommentButtonClick:sender]; 304 | } 305 | } 306 | 307 | -(void)didSelectCommentAtIndexPath:(NSIndexPath *)indexPath{ 308 | if([_richTextViewDelegate respondsToSelector:@selector(didSelectCommentAtIndexPath:)]){ 309 | [_richTextViewDelegate didSelectCommentAtIndexPath:indexPath]; 310 | } 311 | } 312 | 313 | @end 314 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumTableViewCell.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZAlbum.h" 11 | #import "LZAlbumRichTextView.h" 12 | @protocol LZAlbumTableViewCellDelegate 13 | 14 | @optional 15 | 16 | -(void)didCommentButtonClick:(UIButton*)button indexPath:(NSIndexPath*)indexPath; 17 | 18 | -(void)didSelectCommentAtCellIndexPath:(NSIndexPath*)cellIndexPath commentIndexPath:(NSIndexPath*)commentIndexPath; 19 | 20 | @end 21 | 22 | @interface LZAlbumTableViewCell : UITableViewCell 23 | 24 | @property (nonatomic,strong) LZAlbum* currentAlbum; 25 | 26 | @property (nonatomic,strong) NSIndexPath* indexPath; 27 | 28 | @property (nonatomic,strong) id albumTableViewCellDelegate; 29 | @property (nonatomic,strong) LZAlbumRichTextView* albumRichTextView; 30 | +(CGFloat)calculateCellHeightWithAlbum:(LZAlbum*)album; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZAlbumTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAlbumTableViewCell.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/3/25. 6 | // Copyright (c) 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZAlbumTableViewCell.h" 10 | 11 | 12 | @interface LZAlbumTableViewCell () 13 | 14 | 15 | 16 | @end 17 | 18 | @implementation LZAlbumTableViewCell 19 | 20 | +(CGFloat)calculateCellHeightWithAlbum:(LZAlbum*)album{ 21 | return [LZAlbumRichTextView calculateRichTextHeightWithAlbum:album]; 22 | } 23 | 24 | - (void)awakeFromNib { 25 | // Initialization code 26 | } 27 | 28 | -(LZAlbumRichTextView*)albumRichTextView{ 29 | if(_albumRichTextView==nil){ 30 | _albumRichTextView=[[LZAlbumRichTextView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([[UIScreen mainScreen] bounds]), 40)]; 31 | _albumRichTextView.richTextViewDelegate=self; 32 | } 33 | _albumRichTextView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 34 | return _albumRichTextView; 35 | } 36 | 37 | -(void)setCurrentAlbum:(LZAlbum *)currentAlbum{ 38 | _currentAlbum=currentAlbum; 39 | _albumRichTextView.album=currentAlbum; 40 | } 41 | 42 | -(void)setup{ 43 | self.selectionStyle=UITableViewCellSelectionStyleNone; 44 | [self.contentView addSubview:self.albumRichTextView]; 45 | } 46 | 47 | -(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ 48 | self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]; 49 | if(self){ 50 | [self setup]; 51 | } 52 | return self; 53 | } 54 | 55 | -(void)dealloc{ 56 | _albumRichTextView=nil; 57 | } 58 | 59 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 60 | [super setSelected:selected animated:animated]; 61 | // Configure the view for the selected state 62 | } 63 | 64 | -(void)didCommentButtonClick:(UIButton *)button{ 65 | if([_albumTableViewCellDelegate respondsToSelector:@selector(didCommentButtonClick:indexPath:)]){ 66 | [_albumTableViewCellDelegate didCommentButtonClick:button indexPath:self.indexPath]; 67 | } 68 | } 69 | 70 | -(void)didSelectCommentAtIndexPath:(NSIndexPath *)indexPath{ 71 | if([_albumTableViewCellDelegate respondsToSelector:@selector(didSelectCommentAtCellIndexPath:commentIndexPath:)]){ 72 | [_albumTableViewCellDelegate didSelectCommentAtCellIndexPath:self.indexPath commentIndexPath:indexPath]; 73 | } 74 | } 75 | 76 | - (void)prepareForReuse { 77 | [super prepareForReuse]; 78 | self.albumRichTextView.album = nil; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZInputAccessoryView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZInputAccessoryView.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/10/1. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LZInputAccessoryView : UIView 12 | 13 | /* The system keyboard view used as reference. */ 14 | @property (nonatomic, weak, readonly) UIView *keyboardViewProxy; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/LZInputAccessoryView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZInputAccessoryView.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/10/1. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #define SLK_IS_IOS9_AND_HIGHER ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) 10 | 11 | #import "LZInputAccessoryView.h" 12 | 13 | @implementation LZInputAccessoryView 14 | 15 | 16 | #pragma mark - Super Overrides 17 | 18 | - (void)willMoveToSuperview:(UIView *)newSuperview 19 | { 20 | if (newSuperview) { 21 | if (SLK_IS_IOS9_AND_HIGHER) { 22 | 23 | NSPredicate *windowPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", NSClassFromString(@"UIRemoteKeyboardWindow")]; 24 | UIWindow *keyboardWindow = [[[UIApplication sharedApplication].windows filteredArrayUsingPredicate:windowPredicate] firstObject]; 25 | 26 | for (UIView *subview in keyboardWindow.subviews) { 27 | for (UIView *hostview in subview.subviews) { 28 | if ([hostview isMemberOfClass:NSClassFromString(@"UIInputSetHostView")]) { 29 | _keyboardViewProxy = hostview; 30 | break; 31 | } 32 | } 33 | } 34 | } 35 | else { 36 | _keyboardViewProxy = newSuperview; 37 | } 38 | } 39 | } 40 | 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /LZAlbum/ui/views/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/ui/views/test.png -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/Resources/MenuBackground@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/vendor/PathCover/Resources/MenuBackground@2x.png -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/Resources/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/vendor/PathCover/Resources/circle.png -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/Resources/circle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/vendor/PathCover/Resources/circle@2x.png -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/Resources/meicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/vendor/PathCover/Resources/meicon.png -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/Resources/pullrefresh.aif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzwjava/LZAlbum/33838f42510be45cb9b32f903ee52e022aa33c8d/LZAlbum/vendor/PathCover/Resources/pullrefresh.aif -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHPathCover.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHPathConver.h 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // user info key for Dictionary 12 | extern NSString *const XHUserNameKey; 13 | extern NSString *const XHBirthdayKey; 14 | 15 | @interface XHPathCover : UIView 16 | 17 | // parallax background 18 | @property (nonatomic, strong) UIImageView *bannerImageView; 19 | @property (nonatomic, strong) UIImageView *bannerImageViewWithImageEffects; 20 | 21 | // user info 22 | @property (nonatomic, strong) UIButton *avatarButton; 23 | @property (nonatomic, strong) UILabel *userNameLabel; 24 | @property (nonatomic, strong) UILabel *birthdayLabel; 25 | 26 | 27 | //scrollView call back 28 | @property (nonatomic) BOOL touching; 29 | @property (nonatomic) CGFloat offsetY; 30 | 31 | // parallax background origin Y for parallaxHeight 32 | @property (nonatomic, assign) CGFloat parallaxHeight; // default is 170, this height was not self heigth. 33 | 34 | @property (nonatomic, assign) BOOL isZoomingEffect; // default is NO, if isZoomingEffect is YES, will be dissmiss parallax effect 35 | @property (nonatomic, assign) BOOL isLightEffect; // default is YES 36 | @property (nonatomic, assign) CGFloat lightEffectPadding; // default is 80 37 | @property (nonatomic, assign) CGFloat lightEffectAlpha; // default is 1.12 (between 1 - 2) 38 | 39 | @property (nonatomic, copy) void(^handleRefreshEvent)(void); 40 | 41 | @property (nonatomic, copy) void(^handleTapBackgroundImageEvent)(void); 42 | 43 | // stop Refresh 44 | - (void)stopRefresh; 45 | 46 | // background image 47 | - (void)setBackgroundImage:(UIImage *)backgroundImage; 48 | // custom set url for subClass, There is not work 49 | - (void)setBackgroundImageUrlString:(NSString *)backgroundImageUrlString; 50 | 51 | // avatar image 52 | - (void)setAvatarImage:(UIImage *)avatarImage; 53 | // custom set url for subClass, There is not work 54 | - (void)setAvatarUrlString:(NSString *)avatarUrlString; 55 | 56 | // set info, Example : NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"Jack", @"userName", @"1990-10-19", @"birthday", nil]; 57 | - (void)setInfo:(NSDictionary *)info; 58 | 59 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; 60 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView; 61 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; 62 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 63 | @end 64 | -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHPathCover.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHPathConver.m 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHPathCover.h" 10 | #import "XHWaterDropRefresh.h" 11 | 12 | NSString *const XHUserNameKey = @"XHUserName"; 13 | NSString *const XHBirthdayKey = @"XHBirthday"; 14 | 15 | #import 16 | #import 17 | 18 | @interface UIImage (ImageEffects) 19 | - (UIImage *)applyLightEffect; 20 | @end 21 | 22 | @implementation UIImage (ImageEffects) 23 | 24 | - (UIImage *)applyLightEffect { 25 | UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3]; 26 | return [self applyBlurWithRadius:30 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; 27 | } 28 | 29 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage { 30 | // Check pre-conditions. 31 | if (self.size.width < 1 || self.size.height < 1) { 32 | NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self); 33 | return nil; 34 | } 35 | if (!self.CGImage) { 36 | NSLog (@"*** error: image must be backed by a CGImage: %@", self); 37 | return nil; 38 | } 39 | if (maskImage && !maskImage.CGImage) { 40 | NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage); 41 | return nil; 42 | } 43 | 44 | CGRect imageRect = { CGPointZero, self.size }; 45 | UIImage *effectImage = self; 46 | 47 | BOOL hasBlur = blurRadius > __FLT_EPSILON__; 48 | BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__; 49 | if (hasBlur || hasSaturationChange) { 50 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 51 | CGContextRef effectInContext = UIGraphicsGetCurrentContext(); 52 | CGContextScaleCTM(effectInContext, 1.0, -1.0); 53 | CGContextTranslateCTM(effectInContext, 0, -self.size.height); 54 | CGContextDrawImage(effectInContext, imageRect, self.CGImage); 55 | 56 | vImage_Buffer effectInBuffer; 57 | 58 | 59 | effectInBuffer.data = CGBitmapContextGetData(effectInContext); 60 | effectInBuffer.width = CGBitmapContextGetWidth(effectInContext); 61 | effectInBuffer.height = CGBitmapContextGetHeight(effectInContext); 62 | effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext); 63 | 64 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 65 | CGContextRef effectOutContext = UIGraphicsGetCurrentContext(); 66 | vImage_Buffer effectOutBuffer; 67 | effectOutBuffer.data = CGBitmapContextGetData(effectOutContext); 68 | effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext); 69 | effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext); 70 | effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext); 71 | 72 | if (hasBlur) { 73 | // A description of how to compute the box kernel width from the Gaussian 74 | // radius (aka standard deviation) appears in the SVG spec: 75 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement 76 | // 77 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three 78 | // successive box-blurs build a piece-wise quadratic convolution kernel, which 79 | // approximates the Gaussian kernel to within roughly 3%. 80 | // 81 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) 82 | // 83 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. 84 | // 85 | CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale]; 86 | NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); 87 | if (radius % 2 != 1) { 88 | radius += 1; // force radius to be odd so that the three box-blur methodology works. 89 | } 90 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (unsigned int)radius, (unsigned int)radius, 0, kvImageEdgeExtend); 91 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, (unsigned int)radius, (unsigned int)radius, 0, kvImageEdgeExtend); 92 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (unsigned int)radius, (unsigned int)radius, 0, kvImageEdgeExtend); 93 | } 94 | BOOL effectImageBuffersAreSwapped = NO; 95 | if (hasSaturationChange) { 96 | CGFloat s = saturationDeltaFactor; 97 | CGFloat floatingPointSaturationMatrix[] = { 98 | 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0, 99 | 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0, 100 | 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0, 101 | 0, 0, 0, 1, 102 | }; 103 | const int32_t divisor = 256; 104 | NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]); 105 | int16_t saturationMatrix[matrixSize]; 106 | for (NSUInteger i = 0; i < matrixSize; ++i) { 107 | saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor); 108 | } 109 | if (hasBlur) { 110 | vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); 111 | effectImageBuffersAreSwapped = YES; 112 | } 113 | else { 114 | vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); 115 | } 116 | } 117 | if (!effectImageBuffersAreSwapped) 118 | effectImage = UIGraphicsGetImageFromCurrentImageContext(); 119 | UIGraphicsEndImageContext(); 120 | 121 | if (effectImageBuffersAreSwapped) 122 | effectImage = UIGraphicsGetImageFromCurrentImageContext(); 123 | UIGraphicsEndImageContext(); 124 | } 125 | 126 | // Set up output context. 127 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 128 | CGContextRef outputContext = UIGraphicsGetCurrentContext(); 129 | CGContextScaleCTM(outputContext, 1.0, -1.0); 130 | CGContextTranslateCTM(outputContext, 0, -self.size.height); 131 | 132 | // Draw base image. 133 | CGContextDrawImage(outputContext, imageRect, self.CGImage); 134 | 135 | // Draw effect image. 136 | if (hasBlur) { 137 | CGContextSaveGState(outputContext); 138 | if (maskImage) { 139 | CGContextClipToMask(outputContext, imageRect, maskImage.CGImage); 140 | } 141 | CGContextDrawImage(outputContext, imageRect, effectImage.CGImage); 142 | CGContextRestoreGState(outputContext); 143 | } 144 | 145 | // Add in color tint. 146 | if (tintColor) { 147 | CGContextSaveGState(outputContext); 148 | CGContextSetFillColorWithColor(outputContext, tintColor.CGColor); 149 | CGContextFillRect(outputContext, imageRect); 150 | CGContextRestoreGState(outputContext); 151 | } 152 | 153 | // Output image is ready. 154 | UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); 155 | UIGraphicsEndImageContext(); 156 | 157 | return outputImage; 158 | } 159 | 160 | @end 161 | 162 | @interface XHPathCover () { 163 | BOOL normal, paste, hasStop; 164 | BOOL isrefreshed; 165 | } 166 | 167 | @property (nonatomic, strong) UIView *bannerView; 168 | 169 | @property (nonatomic, strong) UIView *showView; 170 | 171 | @property (nonatomic, strong) XHWaterDropRefresh *waterDropRefresh; 172 | 173 | @property (nonatomic, assign) CGFloat showUserInfoViewOffsetHeight; 174 | 175 | @end 176 | 177 | @implementation XHPathCover 178 | 179 | #pragma mark - Publish Api 180 | 181 | - (void)stopRefresh { 182 | [_waterDropRefresh stopRefresh]; 183 | if(_touching == NO) { 184 | [self resetTouch]; 185 | } else { 186 | hasStop = YES; 187 | } 188 | } 189 | 190 | // background 191 | - (void)setBackgroundImage:(UIImage *)backgroundImage { 192 | if (backgroundImage) { 193 | _bannerImageView.image = backgroundImage; 194 | _bannerImageViewWithImageEffects.image = [backgroundImage applyLightEffect]; 195 | } 196 | } 197 | 198 | - (void)setBackgroundImageUrlString:(NSString *)backgroundImageUrlString { 199 | if (backgroundImageUrlString) { 200 | 201 | } 202 | } 203 | 204 | // avatar 205 | - (void)setAvatarImage:(UIImage *)avatarImage { 206 | if (avatarImage) { 207 | [_avatarButton setImage:avatarImage forState:UIControlStateNormal]; 208 | } 209 | } 210 | 211 | - (void)setAvatarUrlString:(NSString *)avatarUrlString { 212 | if (avatarUrlString) { 213 | 214 | } 215 | } 216 | 217 | // set info 218 | - (void)setInfo:(NSDictionary *)info { 219 | NSString *userName = [info valueForKey:XHUserNameKey]; 220 | if (userName) { 221 | self.userNameLabel.text = userName; 222 | } 223 | 224 | NSString *birthday = [info valueForKey:XHBirthdayKey]; 225 | if (birthday) { 226 | self.birthdayLabel.text = birthday; 227 | } 228 | } 229 | 230 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 231 | self.touching = YES; 232 | } 233 | 234 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 235 | self.offsetY = scrollView.contentOffset.y; 236 | } 237 | 238 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 239 | if(decelerate == NO) { 240 | self.touching = NO; 241 | } 242 | } 243 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 244 | self.touching = NO; 245 | } 246 | 247 | #pragma mark - Propertys 248 | 249 | - (void)setTouching:(BOOL)touching { 250 | if(touching) { 251 | if(hasStop) { 252 | [self resetTouch]; 253 | } 254 | 255 | if(normal) { 256 | paste = YES; 257 | } else if (paste == NO && _waterDropRefresh.isRefreshing == NO) { 258 | normal = YES; 259 | } 260 | } else if(_waterDropRefresh.isRefreshing == NO) { 261 | [self resetTouch]; 262 | } 263 | _touching = touching; 264 | } 265 | 266 | - (void)setOffsetY:(CGFloat)y { 267 | CGFloat fixAdaptorPadding = 0; 268 | if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 7.0) { 269 | fixAdaptorPadding = 64; 270 | } 271 | y += fixAdaptorPadding; 272 | _offsetY = y; 273 | CGRect frame = _showView.frame; 274 | if(y < 0) { 275 | if((_waterDropRefresh.isRefreshing) || hasStop) { 276 | if(normal && paste == NO) { 277 | frame.origin.y = self.showUserInfoViewOffsetHeight + y; 278 | _showView.frame = frame; 279 | } else { 280 | if(frame.origin.y != self.showUserInfoViewOffsetHeight) { 281 | frame.origin.y = self.showUserInfoViewOffsetHeight; 282 | _showView.frame = frame; 283 | } 284 | } 285 | } else { 286 | frame.origin.y = self.showUserInfoViewOffsetHeight + y; 287 | _showView.frame = frame; 288 | } 289 | } else { 290 | if(normal && _touching && isrefreshed) { 291 | paste = YES; 292 | } 293 | if(frame.origin.y != self.showUserInfoViewOffsetHeight) { 294 | frame.origin.y = self.showUserInfoViewOffsetHeight; 295 | _showView.frame = frame; 296 | } 297 | } 298 | if (hasStop == NO) { 299 | _waterDropRefresh.currentOffset = y; 300 | } 301 | 302 | UIView *bannerSuper = _bannerImageView.superview; 303 | CGRect bframe = bannerSuper.frame; 304 | if(y < 0) { 305 | bframe.origin.y = y; 306 | bframe.size.height = -y + bannerSuper.superview.frame.size.height; 307 | bannerSuper.frame = bframe; 308 | 309 | CGPoint center = _bannerImageView.center; 310 | center.y = bannerSuper.frame.size.height / 2; 311 | _bannerImageView.center = center; 312 | 313 | if (self.isZoomingEffect) { 314 | _bannerImageView.center = center; 315 | CGFloat scale = fabsf(y) / self.parallaxHeight; 316 | _bannerImageView.transform = CGAffineTransformMakeScale(1+scale, 1+scale); 317 | } 318 | } else { 319 | if(bframe.origin.y != 0) { 320 | bframe.origin.y = 0; 321 | bframe.size.height = bannerSuper.superview.frame.size.height; 322 | bannerSuper.frame = bframe; 323 | } 324 | if(y < bframe.size.height) { 325 | CGPoint center = _bannerImageView.center; 326 | center.y = bannerSuper.frame.size.height/2 + 0.5 * y; 327 | _bannerImageView.center = center; 328 | } 329 | } 330 | 331 | if (self.isLightEffect) { 332 | if(y < 0 && y >= -self.lightEffectPadding) { 333 | float percent = (-y / (self.lightEffectPadding * self.lightEffectAlpha)); 334 | self.bannerImageViewWithImageEffects.alpha = percent; 335 | 336 | } else if (y <= -self.lightEffectPadding) { 337 | self.bannerImageViewWithImageEffects.alpha = self.lightEffectPadding / (self.lightEffectPadding * self.lightEffectAlpha); 338 | } else if (y > self.lightEffectPadding) { 339 | self.bannerImageViewWithImageEffects.alpha = 0; 340 | } 341 | } 342 | } 343 | 344 | #pragma mark - Life cycle 345 | 346 | - (id)initWithFrame:(CGRect)frame { 347 | self = [super initWithFrame:frame]; 348 | if (self) { 349 | // Initialization code 350 | [self _setup]; 351 | } 352 | return self; 353 | } 354 | 355 | - (id)initWithCoder:(NSCoder *)aDecoder { 356 | self = [super initWithCoder:aDecoder]; 357 | if(self) 358 | { 359 | [self _setup]; 360 | } 361 | return self; 362 | } 363 | 364 | - (void)_setup { 365 | self.parallaxHeight = 170; 366 | self.isLightEffect = YES; 367 | self.lightEffectPadding = 80; 368 | self.lightEffectAlpha = 1.15; 369 | 370 | _bannerView = [[UIView alloc] initWithFrame:self.bounds]; 371 | _bannerView.clipsToBounds = YES; 372 | UITapGestureRecognizer *tapGestureRecongnizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecongnizerHandle:)]; 373 | [_bannerView addGestureRecognizer:tapGestureRecongnizer]; 374 | 375 | _bannerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, -self.parallaxHeight, CGRectGetWidth(_bannerView.frame), CGRectGetHeight(_bannerView.frame) + self.parallaxHeight * 2)]; 376 | _bannerImageView.contentMode = UIViewContentModeScaleToFill; 377 | [_bannerView addSubview:self.bannerImageView]; 378 | 379 | _bannerImageViewWithImageEffects = [[UIImageView alloc] initWithFrame:_bannerImageView.frame]; 380 | _bannerImageViewWithImageEffects.alpha = 0.; 381 | [_bannerView addSubview:self.bannerImageViewWithImageEffects]; 382 | 383 | [self addSubview:self.bannerView]; 384 | 385 | CGFloat waterDropRefreshHeight = 100; 386 | CGFloat waterDropRefreshWidth = 20; 387 | _waterDropRefresh = [[XHWaterDropRefresh alloc] initWithFrame:CGRectMake(33, CGRectGetHeight(self.bounds) - waterDropRefreshHeight, waterDropRefreshWidth, waterDropRefreshHeight)]; 388 | _waterDropRefresh.refreshCircleImage = [UIImage imageNamed:@"circle"]; 389 | _waterDropRefresh.offsetHeight = 20; // 线条的长度 390 | [self addSubview:self.waterDropRefresh]; 391 | 392 | CGFloat avatarButtonHeight = 66; 393 | self.showUserInfoViewOffsetHeight = CGRectGetHeight(self.frame) - waterDropRefreshHeight / 3 - avatarButtonHeight; 394 | _showView = [[UIView alloc] initWithFrame:CGRectMake(0, self.showUserInfoViewOffsetHeight, CGRectGetWidth(self.bounds), waterDropRefreshHeight)]; 395 | _showView.backgroundColor = [UIColor clearColor]; 396 | 397 | _avatarButton = [[UIButton alloc] initWithFrame:CGRectMake(15, 0, avatarButtonHeight, avatarButtonHeight)]; 398 | 399 | _userNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(93, 0, 207, 34)]; 400 | _userNameLabel.textColor = [UIColor whiteColor]; 401 | _userNameLabel.backgroundColor = [UIColor clearColor]; 402 | _userNameLabel.shadowColor = [UIColor blackColor]; 403 | _userNameLabel.shadowOffset = CGSizeMake(0, 2); 404 | _userNameLabel.font = [UIFont boldSystemFontOfSize:28.0f]; 405 | 406 | 407 | _birthdayLabel = [[UILabel alloc] initWithFrame:CGRectMake(93, 42, 207, 24)]; 408 | _birthdayLabel.textColor = [UIColor whiteColor]; 409 | _birthdayLabel.backgroundColor = [UIColor clearColor]; 410 | _birthdayLabel.shadowColor = [UIColor blackColor]; 411 | _birthdayLabel.shadowOffset = CGSizeMake(0, 2); 412 | _birthdayLabel.font = [UIFont systemFontOfSize:14.0f]; 413 | 414 | [_showView addSubview:self.avatarButton]; 415 | [_showView addSubview:self.userNameLabel]; 416 | [_showView addSubview:self.birthdayLabel]; 417 | 418 | [self addSubview:self.showView]; 419 | } 420 | 421 | - (void)dealloc { 422 | self.bannerImageView = nil; 423 | self.bannerImageViewWithImageEffects = nil; 424 | 425 | self.avatarButton = nil; 426 | self.userNameLabel = nil; 427 | self.birthdayLabel = nil; 428 | 429 | self.bannerView = nil; 430 | self.showView = nil; 431 | 432 | self.waterDropRefresh = nil; 433 | } 434 | 435 | - (void)willMoveToSuperview:(UIView *)newSuperview { 436 | [super willMoveToSuperview:newSuperview]; 437 | if(newSuperview) { 438 | [self initWaterView]; 439 | } 440 | } 441 | 442 | - (void)initWaterView { 443 | __weak XHPathCover *wself =self; 444 | [_waterDropRefresh setHandleRefreshEvent:^{ 445 | [wself setIsRefreshed:YES]; 446 | if(wself.handleRefreshEvent) { 447 | wself.handleRefreshEvent(); 448 | } 449 | }]; 450 | } 451 | 452 | #pragma mark - previte method 453 | 454 | - (void)tapGestureRecongnizerHandle:(UITapGestureRecognizer *)tapGestureRecongnizer { 455 | if (self.handleTapBackgroundImageEvent) { 456 | self.handleTapBackgroundImageEvent(); 457 | } 458 | } 459 | 460 | - (void)setIsRefreshed:(BOOL)b { 461 | isrefreshed = b; 462 | } 463 | 464 | - (void)refresh { 465 | if(_waterDropRefresh.isRefreshing) { 466 | [_waterDropRefresh startRefreshAnimation]; 467 | } 468 | } 469 | 470 | - (void)resetTouch { 471 | normal = NO; 472 | paste = NO; 473 | hasStop = NO; 474 | isrefreshed = NO; 475 | } 476 | 477 | @end 478 | -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHSoundManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHSoundManager.h 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XHSoundManager : NSObject 12 | 13 | + (instancetype)sharedInstance; 14 | 15 | - (void)playRefreshSound; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHSoundManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHSoundManager.m 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHSoundManager.h" 10 | #import 11 | 12 | @interface XHSoundManager () { 13 | SystemSoundID refreshSound; 14 | } 15 | 16 | @end 17 | 18 | @implementation XHSoundManager 19 | 20 | + (instancetype)sharedInstance { 21 | static XHSoundManager *instance; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | instance = [[XHSoundManager alloc] init]; 25 | }); 26 | return instance; 27 | } 28 | 29 | - (id)init { 30 | self = [super init]; 31 | if (self) { 32 | NSURL *url = [[NSBundle mainBundle] URLForResource:@"pullrefresh" withExtension:@"aif"]; 33 | AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url) , &refreshSound); 34 | } 35 | return self; 36 | } 37 | 38 | - (void)playRefreshSound { 39 | AudioServicesPlaySystemSound(refreshSound); 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHWaterDropRefresh.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHWaterDropRefresh.h 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XHWaterDropRefresh : UIView 12 | 13 | @property (nonatomic, assign) CGFloat radius; // default is 5. 14 | @property (nonatomic, assign) CGFloat maxOffset; // default is 70 15 | @property (nonatomic, assign) CGFloat deformationLength; // default is 0.4 (between 0.1 -- 0.9) 16 | @property (nonatomic, assign) CGFloat offsetHeight; 17 | @property (nonatomic, strong) UIImage *refreshCircleImage; 18 | @property (nonatomic, readonly) BOOL isRefreshing; 19 | 20 | - (void)stopRefresh; 21 | - (void)startRefreshAnimation; 22 | 23 | @property (nonatomic, copy) void(^handleRefreshEvent)(void) ; 24 | @property (nonatomic) float currentOffset; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /LZAlbum/vendor/PathCover/XHWaterDropRefresh.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHWaterDropRefresh.m 3 | // XHPathCover 4 | // 5 | // Created by 曾 宪华 on 14-2-7. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHWaterDropRefresh.h" 10 | #import "XHSoundManager.h" 11 | 12 | @interface XHWaterDropRefresh () { 13 | BOOL _isRefresh; 14 | } 15 | 16 | @property (nonatomic, strong) CAShapeLayer *shapeLayer; 17 | @property (nonatomic, strong) CAShapeLayer *lineLayer; 18 | @property (nonatomic, strong) UIImageView *refreshView; 19 | 20 | 21 | @property (nonatomic, strong) NSTimer *timer; 22 | 23 | @end 24 | 25 | @implementation XHWaterDropRefresh 26 | 27 | #pragma mark - Propertys 28 | 29 | - (BOOL)isRefreshing { 30 | return _isRefresh; 31 | } 32 | 33 | 34 | #pragma mark - Life cycle 35 | 36 | - (id)initWithFrame:(CGRect)frame 37 | { 38 | self = [super initWithFrame:frame]; 39 | if (self) { 40 | // Initialization code 41 | [self _setup]; 42 | } 43 | return self; 44 | } 45 | 46 | - (id)initWithCoder:(NSCoder *)aDecoder 47 | { 48 | self = [super initWithCoder:aDecoder]; 49 | if(self) 50 | { 51 | [self _setup]; 52 | } 53 | return self; 54 | } 55 | 56 | - (void)dealloc { 57 | self.refreshCircleImage = nil; 58 | 59 | self.shapeLayer = nil; 60 | self.lineLayer = nil; 61 | self.refreshView = nil; 62 | } 63 | 64 | - (void)_setup { 65 | self.deformationLength = 0.4; 66 | self.maxOffset = 70; // 改变最大拉断距离 67 | self.radius = 5.; // 改变圆圈的半径 68 | self.offsetHeight = 20; // 改变线条的长度 69 | 70 | CGRect frame = self.frame; 71 | frame.size = CGSizeMake(30, 100); //固定 30 * 100 为什么要固定本身的大小呢? 72 | self.frame = frame; 73 | 74 | _lineLayer = [CAShapeLayer layer]; 75 | _lineLayer.fillColor = [UIColor colorWithRed:222./255. green:216./255. blue:211./255. alpha:0.5].CGColor; 76 | [self.layer addSublayer:_lineLayer]; 77 | 78 | 79 | _shapeLayer = [CAShapeLayer layer]; 80 | _shapeLayer.fillColor = [UIColor colorWithRed:222./255. green:216./255. blue:211./255. alpha:1].CGColor; 81 | _shapeLayer.strokeColor = [[UIColor whiteColor] CGColor]; 82 | _shapeLayer.lineWidth = 2; 83 | [self.layer addSublayer:_shapeLayer]; 84 | 85 | self.currentOffset = 0; 86 | } 87 | 88 | #pragma mark - Publish Api 89 | 90 | - (void)stopRefresh { 91 | _isRefresh = NO; 92 | 93 | CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 94 | anim.fromValue = @(1); 95 | anim.toValue = @(0); 96 | anim.duration = 0.2; 97 | anim.delegate = self; 98 | [_refreshView.layer addAnimation:anim forKey:nil]; 99 | _refreshView.layer.opacity = 0; 100 | 101 | 102 | anim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 103 | anim.fromValue = @(0); 104 | anim.toValue = @(1); 105 | anim.beginTime = 0.2; 106 | anim.duration = 0.2; 107 | anim.delegate = self; 108 | [_shapeLayer addAnimation:anim forKey:nil]; 109 | _shapeLayer.opacity = 0; 110 | } 111 | 112 | 113 | - (void)startRefreshAnimation { 114 | if(self.refreshView == nil) { 115 | _refreshView = [[UIImageView alloc] initWithImage:self.refreshCircleImage]; 116 | CGRect refreshViewFrame = _refreshView.frame; 117 | refreshViewFrame.size = CGSizeMake(18, 18); 118 | [self addSubview:_refreshView]; 119 | } 120 | _shapeLayer.opacity = 0; 121 | 122 | _refreshView.center = CGPointMake(15,self.frame.size.height - 20 - _radius); 123 | [_refreshView.layer removeAllAnimations]; 124 | _refreshView.layer.opacity = 1; 125 | 126 | CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 127 | animation.duration = 1; 128 | animation.fromValue = @0; 129 | animation.toValue = @(M_PI*2); 130 | animation.repeatCount = INT_MAX; 131 | 132 | [_refreshView.layer addAnimation:animation forKey:@"rotation"]; 133 | } 134 | 135 | #pragma mark - CGMutablePathRef Help Method 136 | 137 | - (CGMutablePathRef)createPathWithOffset:(float)currentOffset { 138 | CGMutablePathRef path = CGPathCreateMutable(); 139 | float top = self.frame.size.height - 20 - _radius*2 - currentOffset; 140 | float wdiff = currentOffset * self.deformationLength; // 改变拉断之前,水滴的长度 141 | 142 | if(currentOffset == 0) { 143 | CGPathAddEllipseInRect(path, NULL, CGRectMake(15-_radius, top, _radius*2, _radius*2)); 144 | } else { 145 | CGPathAddArc(path, NULL, 15, top+_radius, _radius, 0, M_PI, YES); 146 | float bottom = top + wdiff+_radius*2; 147 | if(currentOffset < 10) { 148 | CGPathAddCurveToPoint(path, NULL,15-_radius,bottom,15,bottom, 15,bottom); 149 | CGPathAddCurveToPoint(path, NULL, 15,bottom,15+_radius,bottom, 15+_radius, top+_radius); 150 | } else { 151 | CGPathAddCurveToPoint(path, NULL,15-_radius ,top +_radius, 15 - _radius ,bottom - _radius, 15, bottom); 152 | CGPathAddCurveToPoint(path,NULL, 15 + _radius, bottom - _radius, 15+_radius,top +_radius , 15+_radius, top+_radius); 153 | } 154 | } 155 | CGPathCloseSubpath(path); 156 | 157 | return path; 158 | } 159 | 160 | - (void)setCurrentOffset:(float)currentOffset { 161 | if(_isRefresh) 162 | return; 163 | 164 | [self privateSetCurrentOffset:currentOffset]; 165 | } 166 | 167 | - (void)privateSetCurrentOffset:(float)currentOffset { 168 | currentOffset = currentOffset>0?0:currentOffset; 169 | currentOffset = -currentOffset; 170 | _currentOffset = currentOffset; 171 | if(currentOffset < _maxOffset) { 172 | float wdiff = currentOffset* 0.2; 173 | float top = self.frame.size.height - 20 - _radius*2 - currentOffset; 174 | 175 | CGMutablePathRef path = [self createPathWithOffset:currentOffset]; 176 | _shapeLayer.path = path; 177 | CGPathRelease(path); 178 | 179 | 180 | CGMutablePathRef line = CGPathCreateMutable(); 181 | float w = ((_maxOffset - currentOffset)/_maxOffset) + 1; 182 | CGPathAddRect(line, NULL, CGRectMake(15-w/2, top + wdiff + _radius*2,w, currentOffset-wdiff + self.offsetHeight)); // 最好的+20就是线条的长度 183 | _lineLayer.path = line; 184 | 185 | self.transform = CGAffineTransformMakeScale(0.8+0.2*(w-1), 1); 186 | } else { 187 | if(self.timer == nil) 188 | { 189 | _isRefresh = YES; 190 | self.transform = CGAffineTransformIdentity; 191 | self.timer = [NSTimer timerWithTimeInterval:0.02 target:self selector:@selector(resetWater) userInfo:nil repeats:YES]; 192 | [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 193 | [_timer fire]; 194 | } 195 | } 196 | } 197 | 198 | - (void)resetWater { 199 | [self privateSetCurrentOffset:-(_currentOffset-(_maxOffset/8))]; 200 | if(_currentOffset == 0) { 201 | [self.timer invalidate]; 202 | self.timer = nil; 203 | 204 | // play refresh stop sound 205 | [[XHSoundManager sharedInstance] playRefreshSound]; 206 | 207 | if(self.handleRefreshEvent != nil) { 208 | self.handleRefreshEvent(); 209 | } 210 | [self startRefreshAnimation]; 211 | } 212 | } 213 | 214 | 215 | - (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag { 216 | if(anim.beginTime > 0) { 217 | _shapeLayer.opacity = 1; 218 | } else { 219 | [_refreshView.layer removeAllAnimations]; 220 | } 221 | } 222 | 223 | /* 224 | // Only override drawRect: if you perform custom drawing. 225 | // An empty implementation adversely affects performance during animation. 226 | - (void)drawRect:(CGRect)rect 227 | { 228 | // Drawing code 229 | } 230 | */ 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHImageViewer.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHImageViewer.h 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | @class XHImageViewer; 11 | @protocol XHImageViewerDelegate 12 | 13 | @optional 14 | 15 | - (void)imageViewer:(XHImageViewer *)imageViewer didShowImageView:(UIImageView*)selectedView atIndex:(NSInteger)index; 16 | - (void)imageViewer:(XHImageViewer *)imageViewer willDismissWithSelectedView:(UIImageView*)selectedView; 17 | 18 | @end 19 | 20 | @interface XHImageViewer : UIView 21 | 22 | @property (nonatomic, weak) id delegate; 23 | @property (nonatomic, assign) CGFloat backgroundScale; 24 | 25 | - (void)showWithImageViews:(NSArray*)views selectedView:(UIImageView*)selectedView; 26 | @end 27 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHImageViewer.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHImageViewer.m 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHImageViewer.h" 10 | #import "XHViewState.h" 11 | #import "XHZoomingImageView.h" 12 | 13 | @interface XHImageViewer () 14 | 15 | @property (nonatomic, strong) UIScrollView *scrollView; 16 | @property (nonatomic, strong) NSArray *imgViews; 17 | 18 | @end 19 | 20 | @implementation XHImageViewer 21 | 22 | - (id)init { 23 | self = [self initWithFrame:CGRectZero]; 24 | if (self) { 25 | [self _setup]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)_setup { 31 | self.backgroundColor = [UIColor colorWithWhite:0.1 alpha:1]; 32 | self.backgroundScale = 0.95; 33 | 34 | UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)]; 35 | pan.maximumNumberOfTouches = 1; 36 | [self addGestureRecognizer:pan]; 37 | } 38 | 39 | - (id)initWithFrame:(CGRect)frame { 40 | self = [super initWithFrame:[[UIScreen mainScreen] bounds]]; 41 | if (self) { 42 | [self _setup]; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)setImageViewsFromArray:(NSArray*)views { 48 | NSMutableArray *imgViews = [NSMutableArray array]; 49 | for(id obj in views) { 50 | if([obj isKindOfClass:[UIImageView class]]) { 51 | [imgViews addObject:obj]; 52 | 53 | UIImageView *view = obj; 54 | 55 | XHViewState *state = [XHViewState viewStateForView:view]; 56 | [state setStateWithView:view]; 57 | 58 | view.userInteractionEnabled = NO; 59 | } 60 | } 61 | _imgViews = [imgViews copy]; 62 | } 63 | 64 | - (void)showWithImageViews:(NSArray*)views selectedView:(UIImageView*)selectedView { 65 | [self setImageViewsFromArray:views]; 66 | 67 | if(_imgViews.count > 0) { 68 | if(![selectedView isKindOfClass:[UIImageView class]] || ![_imgViews containsObject:selectedView]){ 69 | selectedView = _imgViews[0]; 70 | } 71 | [self showWithSelectedView:selectedView]; 72 | } 73 | } 74 | 75 | #pragma mark- Properties 76 | 77 | - (void)setBackgroundColor:(UIColor *)backgroundColor { 78 | [super setBackgroundColor:[backgroundColor colorWithAlphaComponent:0]]; 79 | } 80 | 81 | - (NSInteger)pageIndex { 82 | return (_scrollView.contentOffset.x / _scrollView.frame.size.width + 0.5); 83 | } 84 | 85 | #pragma mark- View management 86 | 87 | - (UIImageView *)currentView { 88 | return [_imgViews objectAtIndex:self.pageIndex]; 89 | } 90 | 91 | - (void)showWithSelectedView:(UIImageView *)selectedView { 92 | 93 | [self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 94 | 95 | const NSInteger currentPage = [_imgViews indexOfObject:selectedView]; 96 | 97 | UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 98 | 99 | if(_scrollView == nil) { 100 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 101 | _scrollView.pagingEnabled = YES; 102 | _scrollView.showsHorizontalScrollIndicator = NO; 103 | _scrollView.showsVerticalScrollIndicator = NO; 104 | _scrollView.backgroundColor = [self.backgroundColor colorWithAlphaComponent:1]; 105 | _scrollView.alpha = 0; 106 | _scrollView.delegate = self; 107 | } 108 | 109 | [self addSubview:_scrollView]; 110 | [window addSubview:self]; 111 | 112 | const CGFloat fullW = window.frame.size.width; 113 | const CGFloat fullH = window.frame.size.height; 114 | 115 | selectedView.frame = [window convertRect:selectedView.frame fromView:selectedView.superview]; 116 | [window addSubview:selectedView]; 117 | 118 | [UIView animateWithDuration:0.3 119 | animations:^{ 120 | _scrollView.alpha = 1; 121 | window.rootViewController.view.transform = CGAffineTransformMakeScale(self.backgroundScale, self.backgroundScale); 122 | 123 | selectedView.transform = CGAffineTransformIdentity; 124 | 125 | CGSize size = (selectedView.image) ? selectedView.image.size : selectedView.frame.size; 126 | CGFloat ratio = MIN(fullW / size.width, fullH / size.height); 127 | CGFloat W = ratio * size.width; 128 | CGFloat H = ratio * size.height; 129 | selectedView.frame = CGRectMake((fullW-W)/2, (fullH-H)/2, W, H); 130 | } 131 | completion:^(BOOL finished) { 132 | _scrollView.contentSize = CGSizeMake(_imgViews.count * fullW, 0); 133 | _scrollView.contentOffset = CGPointMake(currentPage * fullW, 0); 134 | 135 | UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedScrollView:)]; 136 | [_scrollView addGestureRecognizer:gesture]; 137 | 138 | for(UIImageView *view in _imgViews){ 139 | view.transform = CGAffineTransformIdentity; 140 | 141 | CGSize size = (view.image) ? view.image.size : view.frame.size; 142 | CGFloat ratio = MIN(fullW / size.width, fullH / size.height); 143 | CGFloat W = ratio * size.width; 144 | CGFloat H = ratio * size.height; 145 | view.frame = CGRectMake((fullW-W)/2, (fullH-H)/2, W, H); 146 | 147 | XHZoomingImageView *tmp = [[XHZoomingImageView alloc] initWithFrame:CGRectMake([_imgViews indexOfObject:view] * fullW, 0, fullW, fullH)]; 148 | tmp.imageView = view; 149 | 150 | [_scrollView addSubview:tmp]; 151 | } 152 | if ([self.delegate respondsToSelector:@selector(imageViewer:didShowImageView:atIndex:)]) { 153 | [self.delegate imageViewer:self didShowImageView:selectedView atIndex:currentPage]; 154 | } 155 | } 156 | ]; 157 | } 158 | 159 | - (void)prepareToDismiss { 160 | UIImageView *currentView = [self currentView]; 161 | 162 | if([self.delegate respondsToSelector:@selector(imageViewer:willDismissWithSelectedView:)]) { 163 | [self.delegate imageViewer:self willDismissWithSelectedView:currentView]; 164 | } 165 | 166 | for(UIImageView *view in _imgViews) { 167 | if(view != currentView) { 168 | XHViewState *state = [XHViewState viewStateForView:view]; 169 | view.transform = CGAffineTransformIdentity; 170 | view.frame = state.frame; 171 | view.transform = state.transform; 172 | [state.superview addSubview:view]; 173 | } 174 | } 175 | } 176 | 177 | - (void)dismissWithAnimate { 178 | UIView *currentView = [self currentView]; 179 | UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 180 | 181 | CGRect rct = currentView.frame; 182 | currentView.transform = CGAffineTransformIdentity; 183 | currentView.frame = [window convertRect:rct fromView:currentView.superview]; 184 | [window addSubview:currentView]; 185 | 186 | [UIView animateWithDuration:0.3 187 | animations:^{ 188 | _scrollView.alpha = 0; 189 | window.rootViewController.view.transform = CGAffineTransformIdentity; 190 | 191 | XHViewState *state = [XHViewState viewStateForView:currentView]; 192 | currentView.frame = [window convertRect:state.frame fromView:state.superview]; 193 | currentView.transform = state.transform; 194 | } 195 | completion:^(BOOL finished) { 196 | XHViewState *state = [XHViewState viewStateForView:currentView]; 197 | currentView.transform = CGAffineTransformIdentity; 198 | currentView.frame = state.frame; 199 | currentView.transform = state.transform; 200 | [state.superview addSubview:currentView]; 201 | 202 | for (UIView *view in _imgViews) { 203 | XHViewState *_state = [XHViewState viewStateForView:view]; 204 | view.userInteractionEnabled = _state.userInteratctionEnabled; 205 | } 206 | 207 | [self removeFromSuperview]; 208 | } 209 | ]; 210 | } 211 | 212 | #pragma mark - UIScrollViewDelegate 213 | 214 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 215 | static NSInteger previousPage = 0; 216 | NSInteger page = [self pageIndex]; 217 | if (previousPage != page) { 218 | if ([self.delegate respondsToSelector:@selector(imageViewer:didShowImageView:atIndex:)]) { 219 | [self.delegate imageViewer:self didShowImageView:[self currentView] atIndex:page]; 220 | } 221 | previousPage = page; 222 | } 223 | } 224 | 225 | #pragma mark- Gesture events 226 | 227 | - (void)tappedScrollView:(UITapGestureRecognizer *)sender { 228 | [self prepareToDismiss]; 229 | [self dismissWithAnimate]; 230 | } 231 | 232 | - (void)didPan:(UIPanGestureRecognizer *)sender { 233 | static UIImageView *currentView = nil; 234 | 235 | if(sender.state == UIGestureRecognizerStateBegan) { 236 | currentView = [self currentView]; 237 | 238 | UIView *targetView = currentView.superview; 239 | while(![targetView isKindOfClass:[XHZoomingImageView class]]) { 240 | targetView = targetView.superview; 241 | } 242 | 243 | if (((XHZoomingImageView *)targetView).isViewing) { 244 | currentView = nil; 245 | } else { 246 | UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 247 | currentView.frame = [window convertRect:currentView.frame fromView:currentView.superview]; 248 | [window addSubview:currentView]; 249 | 250 | [self prepareToDismiss]; 251 | } 252 | } 253 | 254 | if(currentView) { 255 | if(sender.state == UIGestureRecognizerStateEnded) { 256 | if(_scrollView.alpha>0.5) { 257 | [self showWithSelectedView:currentView]; 258 | } else { 259 | [self dismissWithAnimate]; 260 | } 261 | currentView = nil; 262 | } else { 263 | CGPoint p = [sender translationInView:self]; 264 | 265 | CGAffineTransform transform = CGAffineTransformMakeTranslation(0, p.y); 266 | transform = CGAffineTransformScale(transform, 1 - fabs(p.y)/1000, 1 - fabs(p.y)/1000); 267 | currentView.transform = transform; 268 | 269 | CGFloat r = 1-fabs(p.y)/200; 270 | _scrollView.alpha = MAX(0, MIN(1, r)); 271 | } 272 | } 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHViewState.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHViewState.h 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XHViewState : UIView 12 | 13 | @property (nonatomic, strong) UIView *superview; 14 | @property (nonatomic, assign) CGRect frame; 15 | @property (nonatomic, assign) BOOL userInteratctionEnabled; 16 | @property (nonatomic, assign) CGAffineTransform transform; 17 | 18 | + (XHViewState *)viewStateForView:(UIView *)view; 19 | - (void)setStateWithView:(UIView *)view; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHViewState.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHViewState.m 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHViewState.h" 10 | 11 | @implementation XHViewState 12 | 13 | + (XHViewState *)viewStateForView:(UIView *)view { 14 | static NSMutableDictionary *dict = nil; 15 | if(dict == nil) { 16 | dict = [NSMutableDictionary dictionary]; 17 | } 18 | 19 | XHViewState *state = dict[@(view.hash)]; 20 | if(state == nil) { 21 | state = [[self alloc] init]; 22 | dict[@(view.hash)] = state; 23 | } 24 | return state; 25 | } 26 | 27 | - (void)setStateWithView:(UIView *)view { 28 | CGAffineTransform trans = view.transform; 29 | view.transform = CGAffineTransformIdentity; 30 | 31 | self.superview = view.superview; 32 | self.frame = view.frame; 33 | self.transform = trans; 34 | self.userInteratctionEnabled = view.userInteractionEnabled; 35 | 36 | view.transform = trans; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHZoomingImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHZoomingImageView.h 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XHZoomingImageView : UIView 12 | 13 | @property (nonatomic, readonly) UIScrollView *scrollView; 14 | @property (nonatomic, strong) UIImageView *imageView; 15 | @property (nonatomic, strong) UIImage *image; 16 | @property (nonatomic, readonly) BOOL isViewing; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHImageViewer/XHZoomingImageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHZoomingImageView.m 3 | // XHImageViewer 4 | // 5 | // Created by 曾 宪华 on 14-2-17. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHZoomingImageView.h" 10 | 11 | @interface XHZoomingImageView () 12 | 13 | @property (nonatomic, readwrite, strong) UIScrollView *scrollView; 14 | @property (nonatomic, strong) UIView *containerView; 15 | 16 | @end 17 | 18 | @implementation XHZoomingImageView 19 | 20 | - (void)_setup { 21 | self.clipsToBounds = YES; 22 | self.contentMode = UIViewContentModeScaleAspectFill; 23 | 24 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 25 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 26 | _scrollView.showsHorizontalScrollIndicator = NO; 27 | _scrollView.showsVerticalScrollIndicator = NO; 28 | _scrollView.delegate = self; 29 | 30 | _containerView = [[UIView alloc] initWithFrame:self.bounds]; 31 | [_scrollView addSubview:_containerView]; 32 | 33 | [self addSubview:_scrollView]; 34 | } 35 | 36 | - (void)awakeFromNib { 37 | [self _setup]; 38 | } 39 | 40 | - (id)initWithFrame:(CGRect)frame 41 | { 42 | self = [super initWithFrame:frame]; 43 | if (self) { 44 | // Initialization code 45 | [self _setup]; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)dealloc { 51 | [self.imageView removeObserver:self forKeyPath:@"image"]; 52 | } 53 | 54 | #pragma mark- Properties 55 | 56 | - (UIImage *)image { 57 | return _imageView.image; 58 | } 59 | 60 | - (void)setImage:(UIImage *)image { 61 | if(self.imageView == nil){ 62 | self.imageView = [UIImageView new]; 63 | self.imageView.clipsToBounds = YES; 64 | } 65 | self.imageView.image = image; 66 | } 67 | 68 | - (void)setImageView:(UIImageView *)imageView { 69 | if(imageView != _imageView) { 70 | [_imageView removeObserver:self forKeyPath:@"image"]; 71 | [_imageView removeFromSuperview]; 72 | 73 | _imageView = imageView; 74 | _imageView.frame = _imageView.bounds; 75 | 76 | [_imageView addObserver:self forKeyPath:@"image" options:0 context:nil]; 77 | 78 | [_containerView addSubview:_imageView]; 79 | 80 | _scrollView.zoomScale = 1; 81 | _scrollView.contentOffset = CGPointZero; 82 | _containerView.bounds = _imageView.bounds; 83 | 84 | [self resetZoomScale]; 85 | _scrollView.zoomScale = _scrollView.minimumZoomScale; 86 | [self scrollViewDidZoom:_scrollView]; 87 | } 88 | } 89 | 90 | - (BOOL)isViewing { 91 | return (_scrollView.zoomScale != _scrollView.minimumZoomScale); 92 | } 93 | 94 | #pragma mark- observe 95 | 96 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 97 | if(object == self.imageView) { 98 | [self imageDidChange]; 99 | } 100 | } 101 | 102 | - (void)imageDidChange { 103 | CGSize size = (self.imageView.image) ? self.imageView.image.size : self.bounds.size; 104 | CGFloat ratio = MIN(_scrollView.frame.size.width / size.width, _scrollView.frame.size.height / size.height); 105 | CGFloat W = ratio * size.width; 106 | CGFloat H = ratio * size.height; 107 | self.imageView.frame = CGRectMake(0, 0, W, H); 108 | 109 | _scrollView.zoomScale = 1; 110 | _scrollView.contentOffset = CGPointZero; 111 | _containerView.bounds = _imageView.bounds; 112 | 113 | [self resetZoomScale]; 114 | _scrollView.zoomScale = _scrollView.minimumZoomScale; 115 | [self scrollViewDidZoom:_scrollView]; 116 | } 117 | 118 | #pragma mark- Scrollview delegate 119 | 120 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 121 | return _containerView; 122 | } 123 | 124 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView { 125 | CGFloat Ws = _scrollView.frame.size.width - _scrollView.contentInset.left - _scrollView.contentInset.right; 126 | CGFloat Hs = _scrollView.frame.size.height - _scrollView.contentInset.top - _scrollView.contentInset.bottom; 127 | CGFloat W = _containerView.frame.size.width; 128 | CGFloat H = _containerView.frame.size.height; 129 | 130 | CGRect rct = _containerView.frame; 131 | rct.origin.x = MAX((Ws-W)/2, 0); 132 | rct.origin.y = MAX((Hs-H)/2, 0); 133 | _containerView.frame = rct; 134 | } 135 | 136 | - (void)resetZoomScale { 137 | CGFloat Rw = _scrollView.frame.size.width / self.imageView.frame.size.width; 138 | CGFloat Rh = _scrollView.frame.size.height / self.imageView.frame.size.height; 139 | 140 | CGFloat scale = 1; 141 | Rw = MAX(Rw, _imageView.image.size.width / (scale * _scrollView.frame.size.width)); 142 | Rh = MAX(Rh, _imageView.image.size.height / (scale * _scrollView.frame.size.height)); 143 | 144 | _scrollView.contentSize = _imageView.frame.size; 145 | _scrollView.minimumZoomScale = 1; 146 | _scrollView.maximumZoomScale = MAX(MAX(Rw, Rh), 1); 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHPhotographyHelper/XHPhotographyHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // XHPhotographyHelper.h 3 | // MessageDisplayExample 4 | // 5 | // Created by HUAJIE-1 on 14-5-3. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^DidFinishTakeMediaCompledBlock)(UIImage *image, NSDictionary *editingInfo); 12 | 13 | @interface XHPhotographyHelper : NSObject 14 | 15 | - (void)showOnPickerViewControllerSourceType:(UIImagePickerControllerSourceType)sourceType onViewController:(UIViewController *)viewController compled:(DidFinishTakeMediaCompledBlock)compled; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LZAlbum/vendor/XHPhotographyHelper/XHPhotographyHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // XHPhotographyHelper.m 3 | // MessageDisplayExample 4 | // 5 | // Created by HUAJIE-1 on 14-5-3. 6 | // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. 7 | // 8 | 9 | #import "XHPhotographyHelper.h" 10 | 11 | @interface XHPhotographyHelper () 12 | 13 | @property (nonatomic, copy) DidFinishTakeMediaCompledBlock didFinishTakeMediaCompled; 14 | 15 | @end 16 | 17 | @implementation XHPhotographyHelper 18 | 19 | - (instancetype)init { 20 | self = [super init]; 21 | if (self) { 22 | } 23 | return self; 24 | } 25 | 26 | - (void)dealloc { 27 | self.didFinishTakeMediaCompled = nil; 28 | } 29 | 30 | - (void)showOnPickerViewControllerSourceType:(UIImagePickerControllerSourceType)sourceType onViewController:(UIViewController *)viewController compled:(DidFinishTakeMediaCompledBlock)compled { 31 | if (![UIImagePickerController isSourceTypeAvailable:sourceType]) { 32 | compled(nil, nil); 33 | return; 34 | } 35 | self.didFinishTakeMediaCompled = [compled copy]; 36 | UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; 37 | imagePickerController.editing = YES; 38 | imagePickerController.delegate = self; 39 | imagePickerController.sourceType = sourceType; 40 | if (sourceType == UIImagePickerControllerSourceTypeCamera) { 41 | imagePickerController.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera]; 42 | } 43 | [viewController presentViewController:imagePickerController animated:YES completion:NULL]; 44 | } 45 | 46 | - (void)dismissPickerViewController:(UIImagePickerController *)picker { 47 | typeof(self) __weak weakSelf=self; 48 | [picker dismissViewControllerAnimated:YES completion:^{ 49 | weakSelf.didFinishTakeMediaCompled = nil; 50 | }]; 51 | } 52 | 53 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo { 54 | if (self.didFinishTakeMediaCompled) { 55 | self.didFinishTakeMediaCompled(image, editingInfo); 56 | } 57 | [self dismissPickerViewController:picker]; 58 | } 59 | 60 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 61 | if (self.didFinishTakeMediaCompled) { 62 | self.didFinishTakeMediaCompled(nil, info); 63 | } 64 | [self dismissPickerViewController:picker]; 65 | } 66 | 67 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { 68 | [self dismissPickerViewController:picker]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /LZAlbumTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LZAlbumTests/LZAlbumManagerTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumManagerTest.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/12/20. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LZBaseTest.h" 11 | 12 | @interface LZAlbumManagerTest : LZBaseTest 13 | 14 | @end 15 | 16 | @implementation LZAlbumManagerTest 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | - (void)testCommentAlbum { 41 | 42 | } 43 | 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LZAlbumTests/LZAlbumTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZAlbumTests.m 3 | // LZAlbumTests 4 | // 5 | // Created by lzw on 15/11/16. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseTest.h" 10 | #import "LZAlbumManager.h" 11 | #import "LZAlbum.h" 12 | 13 | @interface LZAlbumTest : LZBaseTest 14 | 15 | @end 16 | 17 | @implementation LZAlbumTest 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | } 22 | 23 | - (void)tearDown { 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testFindAlbums { 28 | [[LZAlbumManager manager] findAlbumWithBlock:^(NSArray *objects, NSError *error) { 29 | assertNil(error); 30 | assertTrue(objects.count > 0); 31 | NOTIFY 32 | }]; 33 | WAIT 34 | } 35 | 36 | // 因为以下测试会污染线上数据,先注释了 37 | 38 | //- (void)testCreateAlbum { 39 | // NSError *error; 40 | // [[LZAlbumManager manager] createAlbumWithText:@"hi everyone!!!!" photos:@[] error:&error]; 41 | // assertNil(error); 42 | //} 43 | // 44 | //- (void)testCreateAndFindAlbum { 45 | // NSError *error; 46 | // [[LZAlbumManager manager] createAlbumWithText:@"Hi!!!" photos:@[] error:&error]; 47 | // assertNil(error); 48 | // [[LZAlbumManager manager] findAlbumWithBlock:^(NSArray *objects, NSError *error) { 49 | // assertNil(error); 50 | // assertTrue(objects.count > 0); 51 | // LCAlbum *album = objects[0]; 52 | // assertEqualObjects(album.albumContent, @"Hi!!!"); 53 | // NOTIFY 54 | // }]; 55 | // WAIT 56 | //} 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /LZAlbumTests/LZBaseTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // LZBaseTest.h 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/12/15. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define assertTrue(expr) XCTAssertTrue((expr), @"") 12 | #define assertFalse(expr) XCTAssertFalse((expr), @"") 13 | #define assertNil(a1) XCTAssertNil((a1), @"") 14 | #define assertNotNil(a1) XCTAssertNotNil((a1), @"") 15 | #define assertEqual(a1, a2) XCTAssertEqual((a1), (a2), @"") 16 | #define assertEqualObjects(a1, a2) XCTAssertEqualObjects((a1), (a2), @"") 17 | #define assertNotEqual(a1, a2) XCTAssertNotEqual((a1), (a2), @"") 18 | #define assertNotEqualObjects(a1, a2) XCTAssertNotEqualObjects((a1), (a2), @"") 19 | #define assertAccuracy(a1, a2, acc) XCTAssertEqualWithAccuracy((a1),(a2),(acc)) 20 | 21 | #define WAIT \ 22 | do { \ 23 | [self expectationForNotification:@"LCUnitTest" object:nil handler:nil]; \ 24 | [self waitForExpectationsWithTimeout:60 handler:nil]; \ 25 | } while(0); 26 | 27 | #define NOTIFY \ 28 | do { \ 29 | [[NSNotificationCenter defaultCenter] postNotificationName:@"LCUnitTest" object:nil]; \ 30 | } while(0); 31 | 32 | @interface LZBaseTest : XCTestCase 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /LZAlbumTests/LZBaseTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // LZBaseTest.m 3 | // LZAlbum 4 | // 5 | // Created by lzw on 15/12/15. 6 | // Copyright © 2015年 lzw. All rights reserved. 7 | // 8 | 9 | #import "LZBaseTest.h" 10 | 11 | @implementation LZBaseTest 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '6.0' 3 | 4 | target 'LZAlbum' do 5 | pod 'AVOSCloud', '3.1.6.2' 6 | pod 'MBProgressHUD', '0.9.1' 7 | pod 'SDWebImage', '3.7.2' 8 | pod 'DateTools', '1.5.0' 9 | pod 'FXForms', '1.2.13' 10 | pod 'TTTAttributedLabel', '1.13.4' 11 | pod 'JDStatusBarNotification' 12 | end 13 | 14 | -------------------------------------------------------------------------------- /README-cn.md: -------------------------------------------------------------------------------- 1 | # LZAlbum 2 | 基于 LeanCloud 的朋友圈,展示了如何在 LeanCloud 上实现一对一和一对多的关系建模。 3 | 4 | ![album](https://cloud.githubusercontent.com/assets/5022872/9563818/dd9588ba-4ec3-11e5-940a-3d1e84b967f0.gif) 5 | 6 | 7 | # Support 8 | 9 | 如果在使用过程中有任何问题,请提 [issue](https://github.com/lzwjava/LZAlbum/issues) ,我会在 Github 上给予帮助。 10 | 11 | # Run 12 | ``` 13 | pod install --no-repo-update --verbose (报错说库找不到的话,去掉 --no-repo-update) 14 | open LZAlbum.xcworkspace 15 | ``` 16 | 17 | # Credit 18 | 19 | UI 界面大量借鉴了 [MessageDisplayKit](https://github.com/xhzengAIB/MessageDisplayKit),数据放在了 LeanCloud 上,一并表示感谢。 20 | 21 | # Backend 22 | 23 | ![image](https://cloud.githubusercontent.com/assets/5022872/7449102/2390131e-f260-11e4-8978-cead60e2f272.png) 24 | 25 | 用公共账号登录 https://leancloud.cn ,账号/密码:leancloud@163.com/Public123 ,选择应用 LCAlbum 即可查看表结构。 26 | **注意 请不要更改后台数据,查看就好了。否则可能造成客户端崩溃** 27 | **注意 上面的账号密码不是这个应用登录的账号,而是 LeanCloud 后台的账号。应用登录的账号,先在注册界面注册一个即可。** 28 | 29 | # Document 30 | 31 | [相关文档](https://leancloud.cn/docs/ios_os_x_guide.html) 32 | 33 | # License 34 | MIT 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LZAlbum 2 | 3 | Based on LeanCloud's Moments feature, it demonstrates how to model one-to-one and one-to-many relationships on LeanCloud. 4 | 5 | ![album](https://cloud.githubusercontent.com/assets/5022872/9563818/dd9588ba-4ec3-11e5-940a-3d1e84b967f0.gif) 6 | 7 | # Support 8 | 9 | If you encounter any issues during usage, please raise an [issue](https://github.com/lzwjava/LZAlbum/issues), and I will provide assistance on GitHub. 10 | 11 | # Run 12 | ``` 13 | pod install --no-repo-update --verbose (If you encounter errors regarding missing libraries, remove --no-repo-update) 14 | open LZAlbum.xcworkspace 15 | ``` 16 | 17 | # Credit 18 | 19 | The UI design drew heavily from [MessageDisplayKit](https://github.com/xhzengAIB/MessageDisplayKit), and the data is stored on LeanCloud. Gratitude is extended to both. 20 | 21 | # Backend 22 | 23 | ![image](https://cloud.githubusercontent.com/assets/5022872/7449102/2390131e-f260-11e4-8978-cead60e2f272.png) 24 | 25 | Login with the public account on https://leancloud.cn, credentials: leancloud@163.com/Public123, then select the LCAlbum app to view the table structure. 26 | **Note: Please refrain from modifying backend data, just view it. Otherwise, it may lead to client crashes.** 27 | **Note: The above-mentioned credentials are not for logging into this app but for the LeanCloud backend. For the app login, simply register a new account on the registration page.** 28 | 29 | # Document 30 | 31 | [Related documentation](https://leancloud.cn/docs/ios_os_x_guide.html) 32 | 33 | # License 34 | MIT -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | pod install --verbose --no-repo-update && \ 2 | xcodebuild -workspace LZAlbum.xcworkspace -scheme LZAlbum archive -archivePath ./build/LZAlbum.xcarchive && \ 3 | rm -rf ./build/LZAlbum.ipa && \ 4 | xcodebuild -exportArchive -exportFormat ipa -archivePath build/LZAlbum.xcarchive -exportPath build/LZAlbum.ipa && \ 5 | fir p build/LZAlbum.ipa -T eeca39297cfda6e31a67c9b66e1edd07 6 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | xcodebuild -workspace LZAlbum.xcworkspace -scheme LZAlbumTests -sdk iphonesimulator clean test 2 | --------------------------------------------------------------------------------