I have the following code using quickblox.
Unfortunately, when I accessthe view controller, I get an "unAuthorized" error from quickblox API.
What am I doing wrong?
#import "QChatViewController.h"
#include "ChatMessageTableViewCell.h"
#interface QChatViewController ()
#end
#implementation QChatViewController
#synthesize opponent;
#synthesize currentRoom;
#synthesize messages;
#synthesize toolBar;
#synthesize sendMessageField;
#synthesize sendMessageButton;
#synthesize tableView;
#pragma mark -
#pragma mark View controller's lifecycle
- (id) initWithStartup: (NSDictionary *) _startup investor: (NSDictionary *) _investor chat_id: (NSInteger) _chat_id chat_name: (NSString *) _name
{
self = [self initWithNibName: #"QChatViewController" bundle: nil];
if(self)
{
startup = _startup;
investor = _investor;
startup_id = 0;
investor_id = 0;
if ([startup objectForKey: #"id"] &&
[startup objectForKey: #"id"] != (id)[NSNull null])
{
startup_id = [[startup objectForKey: #"id"] intValue];
}
if ([investor objectForKey: #"id"] &&
[investor objectForKey: #"id"] != (id)[NSNull null])
{
investor_id = [[investor objectForKey: #"id"] intValue];
}
past = 0;
chat_id = _chat_id;
self.title = _name;
self.title = #"Chat";
UIButton * button4 = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage * btnImage = [UIImage imageNamed: #"chatrightbtn.png"];
[button4 setFrame:CGRectMake(-90.0f, 0.0f, btnImage.size.width, btnImage.size.height)];
[button4 addTarget:self action:#selector(showSheet:) forControlEvents:UIControlEventTouchUpInside];
[button4 setImage: btnImage forState:UIControlStateNormal];
UIBarButtonItem *random1 = [[UIBarButtonItem alloc] initWithCustomView:button4];
self.navigationItem.rightBarButtonItem = random1;
self.navigationItem.leftBarButtonItem.title = #"";
}
return self;
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
QBASessionCreationRequest *extendedAuthRequest = [QBASessionCreationRequest request];
extendedAuthRequest.userLogin = #"testjk";
extendedAuthRequest.userPassword = #"jerry";
[QBAuth createSessionWithExtendedRequest:extendedAuthRequest delegate:self];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBarHidden = NO;
if(chat_id >= 1) {
NSString * roomName = [NSString stringWithFormat: #"%d", chat_id];
[[QBChat instance] createOrJoinRoomWithName: roomName membersOnly:YES persistent:NO];
}
messages = [[NSMutableArray alloc] init];
}
-(void) chatDidLogin{
// You have successfully signed in to QuickBlox Chat
}
- (void)completedWithResult:(Result *)result{
// Create session result
if(result.success && [result isKindOfClass:QBAAuthSessionCreationResult.class]){
// You have successfully created the session
QBAAuthSessionCreationResult *res = (QBAAuthSessionCreationResult *)result;
// Sign In to QuickBlox Chat
QBUUser *currentUser = [QBUUser user];
currentUser.ID = res.session.userID; // your current user's ID
currentUser.password = #"jerry"; // your current user's password
// set Chat delegate
[QBChat instance].delegate = self;
// login to Chat
[[QBChat instance] loginWithUser:currentUser];
}
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// leave room
if(self.currentRoom){
if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
// back button was pressed.
[[QBChat instance] leaveRoom:self.currentRoom];
[[DataManager shared].rooms removeObject:self.currentRoom];
}
}
}
- (void)viewDidUnload{
[self setToolBar:nil];
[self setSendMessageField:nil];
[self setSendMessageButton:nil];
[self setTableView:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
}
- (IBAction)sendMessage:(id)sender {
if(self.sendMessageField.text.length == 0){
return;
}
if(chat_id == 0)
{
NSString * sid = [NSString stringWithFormat: #"%d", startup_id];
NSString * iid = [NSString stringWithFormat: #"%d", investor_id];
NSString * pasts = [NSString stringWithFormat: #"%d", past];
NSString * chat_ids = [NSString stringWithFormat: #"%d", chat_id];
NSString * path_str = [NSString stringWithFormat: #"chats/?format=json"];
NSMutableDictionary* params =[NSMutableDictionary dictionaryWithObjectsAndKeys:
sid, #"startup",
iid, #"investor",
pasts, #"past",
chat_ids, #"conversation_id",
#"avv7ejtaegxxk2wzgnymsj8xtm2tk9s4xgp6854r6dqn8bk6jjwux4g9dh9b", #"apikey",
nil];
[[API sharedInstance] postcommandWithParams:params
path: path_str
onCompletion:^(NSDictionary *json)
{
if(chat_id == 0)
{
if ([json objectForKey: #"id"] &&
[json objectForKey: #"id"] != (id)[NSNull null])
{
chat_id = [[json objectForKey: #"id"] intValue];
if(chat_id >= 1) {
NSString * roomName = [NSString stringWithFormat: #"%d", chat_id];
[[QBChat instance] createOrJoinRoomWithName: roomName membersOnly:YES persistent:NO];
}
[[QBChat instance] sendMessage:self.sendMessageField.text toRoom:self.currentRoom];
// reload table
[self.tableView reloadData];
// hide keyboard & clean text field
[self.sendMessageField resignFirstResponder];
[self.sendMessageField setText:nil];
}
}
}];
}
else
{
[[QBChat instance] sendMessage:self.sendMessageField.text toRoom:self.currentRoom];
// reload table
[self.tableView reloadData];
// hide keyboard & clean text field
[self.sendMessageField resignFirstResponder];
[self.sendMessageField setText:nil];
}
}
-(void)keyboardShow{
CGRect rectFild = self.sendMessageField.frame;
rectFild.origin.y -= 215;
CGRect rectButton = self.sendMessageButton.frame;
rectButton.origin.y -= 215;
[UIView animateWithDuration:0.25f
animations:^{
[self.sendMessageField setFrame:rectFild];
[self.sendMessageButton setFrame:rectButton];
}
];
}
-(void)keyboardHide{
CGRect rectFild = self.sendMessageField.frame;
rectFild.origin.y += 215;
CGRect rectButton = self.sendMessageButton.frame;
rectButton.origin.y += 215;
[UIView animateWithDuration:0.25f
animations:^{
[self.sendMessageField setFrame:rectFild];
[self.sendMessageButton setFrame:rectButton];
}
];
}
#pragma mark -
#pragma mark TextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField{
[self keyboardShow];
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
[self keyboardHide];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField setText:nil];
[textField resignFirstResponder];
return YES;
}
#pragma mark -
#pragma mark TableViewDataSource & TableViewDelegate
static CGFloat padding = 20.0;
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"MessageCellIdentifier";
// Create cell
ChatMessageTableViewCell *cell = (ChatMessageTableViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ChatMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.userInteractionEnabled = NO;
// Message
QBChatMessage *messageBody = [messages objectAtIndex:[indexPath row]];
// set message's text
NSString *message = [messageBody text];
cell.message.text = message;
// message's datetime
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat: #"yyyy-mm-dd HH:mm:ss"];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:#"..."]];
NSString *time = [formatter stringFromDate:messageBody.datetime];
CGSize textSize = { 260.0, 10000.0 };
CGSize size = [message sizeWithFont:[UIFont boldSystemFontOfSize:13]
constrainedToSize:textSize
lineBreakMode:UILineBreakModeWordWrap];
size.width += (padding/2);
// Left/Right bubble
UIImage *bgImage = nil;
if ([[[DataManager shared] currentUser] ID] == messageBody.senderID || self.currentRoom) {
bgImage = [[UIImage imageNamed:#"orange.png"] stretchableImageWithLeftCapWidth:24 topCapHeight:15];
[cell.message setFrame:CGRectMake(padding, padding*2, size.width+padding, size.height+padding)];
[cell.backgroundImageView setFrame:CGRectMake( cell.message.frame.origin.x - padding/2,
cell.message.frame.origin.y - padding/2,
size.width+padding,
size.height+padding)];
cell.date.textAlignment = UITextAlignmentLeft;
cell.backgroundImageView.image = bgImage;
if(self.currentRoom){
cell.date.text = [NSString stringWithFormat:#"%d %#", messageBody.senderID, time];
}else{
cell.date.text = [NSString stringWithFormat:#"%# %#", [[[DataManager shared] currentUser] login], time];
}
} else {
bgImage = [[UIImage imageNamed:#"aqua.png"] stretchableImageWithLeftCapWidth:24 topCapHeight:15];
[cell.message setFrame:CGRectMake(320 - size.width - padding,
padding*2,
size.width+padding,
size.height+padding)];
[cell.backgroundImageView setFrame:CGRectMake(cell.message.frame.origin.x - padding/2,
cell.message.frame.origin.y - padding/2,
size.width+padding,
size.height+padding)];
cell.date.textAlignment = UITextAlignmentRight;
cell.backgroundImageView.image = bgImage;
cell.date.text = [NSString stringWithFormat:#"%# %#", self.opponent.login, time];
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.messages count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
QBChatMessage *chatMessage = (QBChatMessage *)[messages objectAtIndex:indexPath.row];
NSString *text = chatMessage.text;
CGSize textSize = { 260.0, 10000.0 };
CGSize size = [text sizeWithFont:[UIFont boldSystemFontOfSize:13]
constrainedToSize:textSize
lineBreakMode:UILineBreakModeWordWrap];
size.height += padding;
return size.height+padding+5;
}
#pragma mark -
#pragma mark QBChatDelegate
// Did receive 1-1 message
- (void)chatDidReceiveMessage:(QBChatMessage *)message{
[self.messages addObject:message];
// save message to cache if this 1-1 chat
if (self.opponent) {
[[DataManager shared] saveMessage:[NSKeyedArchiver archivedDataWithRootObject:messages]
toHistoryWithOpponentID:self.opponent.ID];
}
// reload table
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[messages count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
// Did receive message in room
- (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromRoom:(NSString *)roomName{
// save message
[self.messages addObject:message];
// reload table
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[messages count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
// Fired when you did leave room
- (void)chatRoomDidLeave:(NSString *)roomName{
NSLog(#"Chat Controller chatRoomDidLeave");
}
// Called in case changing occupant
- (void)chatRoomDidChangeOnlineUsers:(NSArray *)onlineUsers room:(NSString *)roomName{
NSLog(#"chatRoomDidChangeOnlineUsers %#, %#",roomName, onlineUsers);
}
- (void)chatRoomDidEnter:(QBChatRoom *)room{
NSLog(#"Private room %# was created", room.name);
// You have to retain created room if this is temporary room. In other cases room will be destroyed and all occupants will be disconnected from room
self.currentRoom = room;
// Add users to this room
NSInteger user_id = [[[[API sharedInstance] user] objectForKey: #"id"] intValue];
NSNumber *me = [NSNumber numberWithInt: user_id];
NSArray *users = [NSArray arrayWithObjects: me, nil];
[[QBChat instance] addUsers:users toRoom:room];
}
#end
I'm not sure that you have user with these credentials
extendedAuthRequest.userLogin = #"testjk";
extendedAuthRequest.userPassword = #"jerry";
I first check user exist or not modified your code like this...
//your code
.....
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
//Create extended session request with user authorization
QBASessionCreationRequest *extendedAuthRequest = [QBASessionCreationRequest request];
//Check user exist or not
if(self.currentQBUser){
extendedAuthRequest.userLogin = #"testjk";
extendedAuthRequest.userPassword = #"testjk";
}
[QBAuth createSessionWithExtendedRequest:extendedAuthRequest delegate:self];
}
........
- (void)completedWithResult:(Result *)result{
// Create session result
if(result.success && [result isKindOfClass:QBAAuthSessionCreationResult.class]){
// Register new user
self.currentQBUser = [[QBUUser alloc] init];
user.fullName = #"Your Name";
user.login = #"testjk";
user.password = #"testjk";
user.tags = [NSMutableArray arrayWithObject:#"Chat"];
// Create user
[QBUsers signUp:user delegate:self];
// You have successfully created the session
QBAAuthSessionCreationResult *res = (QBAAuthSessionCreationResult *)result;
} else if([result isKindOfClass:[QBUUserLogInResult class]]){
if(result.success){
QBUUserLogInResult *res = (QBUUserLogInResult *)result;
//Now login to chat
// Sign In to QuickBlox Chat
QBUUser *currentUser = res.user;
currentUser.ID = #"testjk; // your current user's ID
currentUser.password = #"testjk"; // same as user id
// set Chat delegate
[QBChat instance].delegate = self;
// login to Chat
[[QBChat instance] loginWithUser:currentUser];
}
}
if (result.errors.count && (401 != result.status))
{
NSLog(#"QBErrors: %#",result.errors);
}
}
I met the same problem here. And i gave the way i made it.
error unauthorized is the user.login or user.password.
user.login can not be email address but your login username. these i do not know why.
user.password is the email address/username password.
check these informations in your User list at QuickBlox Dashboard
Hope these instructions will help.
use quickblox.min.js
var QBApp = {
appId: 123,
authKey: 'dfdgfd44444',
authSecret: 'dffdgfdg455445'
};
$(document).ready(function () {
QB.init(QBApp.appId, QBApp.authKey, QBApp.authSecret);
QB.createSession(function (err, result) {
console.log('Session create callback', err, result);
});
})
function addUser() {
var pwd, ctr, data;
ctr = document.getElementById(myForm.password);
pwd = ctr.value;
var params = { 'login': 'Rajesh', 'password': 'Pass#123' };
alert(params)
QB.users.create(params, function (err, user) {
debugger;
if (user) {
alert('Done')
//$('#output_place').val(JSON.stringify(user));
} else {
alert('Error')
//$('#output_place').val(JSON.stringify(err));
}
})}
Related
This below code is fired when I press delete button after selecting messages I want to delete in chat room.
- (void)deleteButtonPressed:(id)sender {
if (arrayToDelete.count) {
for (NSString *str in arrayToDelete) {
NSLog(#"msgID --> %#",str);
[self.chatModel.dataSource removeObject:str]; //??? Remove data from the screen
[[FMDBManager sharedInstance] deleteMessageByMessageId:str]; //??? Delete data from database
}
[arrayToDelete removeAllObjects];
[self.chatTableView reloadData];
}
}
This line successfully removes selected messages from the chat room.
[self.chatModel.dataSource removeObject:str]; //??? Remove data from the screen
When I go out the chat room and re-enter, those messages still exist, so I have this line below.
[[FMDBManager sharedInstance] deleteMessageByMessageId:str]; //??? Delete data from database
I think the above line should delete those selected messages from the database but when I re-enter the chat room I still see those messages. Here below are related code to that.
- (void)deleteMessageByMessageId:(NSString *)messageId {
FMDatabase *db = [self getterDataBase];
[db open];
NSString *sqlString = [NSString stringWithFormat:#"DELETE FROM message WHERE messageId = '%#'",messageId];
BOOL status = [db executeUpdate:sqlString];
NSLog(#"Delete MessageById:%# Status:%d",messageId,status);
[db close];
}
I've found that when chat room calls viewDidLoad it will eventually call the method callBackGetChannelLogNew where server will sync-up data with chat room tableview and local database.
- (void)callBackGetChannelLogNew:(NSDictionary *)resultDataDic status:(enumAPI_STATUS)eAPI_STATUS {
if (isFirstTimeUpdate) {
}
if (eAPI_STATUS == API_STATUS_SUCCEE) {
NSString *readString=[NSString stringWithFormat:#"%#",resultDataDic[#"read_arr"]];
if ([readString isEqualToString:#""]) {
// NSLog(#"read_arr is empty");
}
else {
NSArray *read_arr=resultDataDic[#"read_arr"];
// Copy read_arr
self.readArray=[read_arr mutableCopy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
[self dealWithReadArray:read_arr];
});
}
NSArray *data = [resultDataDic objectForKey:#"msg"];
if (data.count > 0) {
apiHaveData = YES;
} else {
apiHaveData = NO;
self.loadIngView.hidden = YES;
isLoadingData = NO;
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
// Reverse order of data
NSArray* reversedArray = [[data reverseObjectEnumerator] allObjects];
NSMutableArray *messageFromOtherArray = [NSMutableArray new];
NSMutableArray *messageAllArray = [NSMutableArray new];
for (int i = 0; i < reversedArray.count; i++) {
NSDictionary *_dic = reversedArray[i];
NSString *fromId = [_dic objectForKey:#"fid"];
NSString *message = [NSString stringWithFormat:#"%#",[_dic objectForKey:#"say"]];
if ([ObjectManager getChatMessageKindWithString:message] == MessageTypeText) {
message = [ObjectManager decryptWithString:message];
}
NSString *messageId = [_dic objectForKey:#"mid"];
NSString *toId = [_dic objectForKey:#"tid"];
NSDateFormatter *_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = #"yyyy-MM-dd HH:mm:ss.SSS";
NSDate *date_t = [NSDate dateWithTimeIntervalSince1970:[[_dic objectForKey:#"t"] doubleValue]/1000.0]; //換算成日期
NSString *stringDate = [_formatter stringFromDate:date_t];
NSString *sendDate = stringDate;
NSString *lid = _dic[#"lid"];
NSMutableDictionary *myDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fromId,#"fromId",
message,#"message",
messageId,#"messageId",
sendDate,#"sendDate",
toId,#"toId",
lid,#"lid",
nil];
NSString *isRead;
if (_chatRoomType == ChatRoomTypePrivate) {
if ([_dic[#"r"] intValue]) {
isRead = #"1";
myDic[#"isRead"] = isRead;
lastReadMessageId = [NSString stringWithFormat:#"%#",messageId];
}
}
if (i == 0) {
if (lidForAPI != [_dic[#"lid"] intValue]) {
lidForAPI = [_dic[#"lid"] intValue];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
apiHaveData = NO;
self.loadIngView.hidden = YES;
isLoadingData = NO;
});
return ;
}
}
if (![myDic[#"fromId"] isEqualToString:[User sharedUser].account]) {
[messageFromOtherArray addObject:myDic];
}
if (_chatRoomType == ChatRoomTypeGroup) {
[myDic setObject:#"1" forKey:#"isGroupMessage"];
}
[myDic setObject:#"1" forKey:#"did_I_Read"];
[messageAllArray addObject:myDic];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self setupViewWithMessageArray:messageAllArray]; //???? Here server sync-up data with tableview
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
if (_chatRoomType == ChatRoomTypePrivate) {
if (messageFromOtherArray.count > 0 && isUplaodLastRead == NO) {
isUplaodLastRead = YES;
NSDictionary *lastReadMsgDic = messageFromOtherArray.lastObject;
[self callMsgReadAPI:lastReadMsgDic];
}
} else {
if (messageAllArray.count > 0 && isUplaodLastRead == NO) {
isUplaodLastRead = YES;
NSDictionary *lastReadMsgDic = messageAllArray.lastObject;
[self callMsgReadAPI:lastReadMsgDic];
}
}
self.chatModel.channelTopic = _topic;
NSArray *read_arr=resultDataDic[#"read_arr"];
[self dealMySendMessageReadedWithReadArray:read_arr AndMessageArray:messageAllArray];
[self saveMessageWithArray:messageAllArray]; //???? Here server sync-up data with local db
});
});
}
}
This lines will sync-up data from server to tableview
dispatch_async(dispatch_get_main_queue(), ^{
[self setupViewWithMessageArray:messageAllArray]; //???? Here server sync-up data with tableview
});
Here below is the method setupViewWithMessageArray
- (void)setupViewWithMessageArray:(NSArray *)messageAllArray {
if (!isFirstTimeUpdate) {
isFirstTimeUpdate = YES;
self.chatModel.dataSource = nil;
[self.chatTableView reloadData];
self.chatModel.dataSource = [[NSMutableArray alloc] init];
[self addMessageWithArray:messageAllArray];
[self.chatTableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.chatModel.dataSource.count-1 inSection:0];
[self.chatTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
} else {
[self addMessageWithArray:messageAllArray];
[self reloadTableViewWithoutMove];
}
self.loadIngView.hidden = YES;
isLoadingData = NO;
if (_chatRoomType == ChatRoomTypePrivate) {
if (lastReadMessageId) {
[self.chatModel setPrivateChatListAllReadFormMessageId:lastReadMessageId];
}
}
}
This line will sync-up data from server to local db
[self saveMessageWithArray:messageAllArray]; //???? Here server sync-up data with local db
Here below is the method saveMessageWithArray
- (void)saveMessageWithArray:(NSArray *)messageArray {
for (NSDictionary *myDic in messageArray) {
if (![[FMDBManager sharedInstance] didMessageExistWithMessageID:[myDic objectForKey:#"messageId"]]) {
[[FMDBManager sharedInstance] SaveMessage:myDic];
}
else {
NSString *mid=[NSString stringWithFormat:#"%#",myDic[#"messageId"]];
NSString *isRead = myDic[#"isReaed"];
if (isRead) {
[[FMDBManager sharedInstance] UpdateisReadWithMessageID:mid];
}
}
}
}
So I think now my question is how I can update messageAllArray with arrayToDelete before server sync-up?
I can't forever read and try to understand the whole codes of MWPhotoBrowser's Example Project. I can't figure out where does this project get the Photos data. I've been trying to understand the project since last week.
So here's the thing, I'm trying to make an app that uses this MWPhotoBrowser (https://github.com/mwaterfall/MWPhotoBrowser/blob/master/README.md) open source project/library. An app that can browse all the albums in my phone but using the MWPhotoBrowser.
In the example proejct of the MWPhotoBrowser, there's a sample code how to browse the local photos of the phone. There are lots of examples but I managed to delete some of them and retain just the last one option. - Library Photos and Videos (Case:9 if you're going to look at the code).
What I have done so far:
Implement the open source project (Gallery Viewer App) from developer.apple.com - successfully implemented, but I'm not satisfied because MWPhotoBrowser is better and cooler.
Edit the Sample Project of MWPhotoBrowser.
CODE:
//
// Menu.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 21/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import <Photos/Photos.h>
#import "Menu.h"
#import "SDImageCache.h"
#import "MWCommon.h"
#implementation Menu
#pragma mark -
#pragma mark View
- (void)viewDidLoad {
[super viewDidLoad];
// Test toolbar hiding
// [self setToolbarItems: #[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:nil action:nil]]];
// [[self navigationController] setToolbarHidden:NO animated:NO];
NSLog(#"view did load....");
self.title = #"MWPhotoBrowser";
// Clear cache for testing
[[SDImageCache sharedImageCache] clearDisk];
[[SDImageCache sharedImageCache] clearMemory];
[self loadAssets];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// self.navigationController.navigationBar.barTintColor = [UIColor greenColor];
// self.navigationController.navigationBar.translucent = NO;
// [self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// [self.navigationController setNavigationBarHidden:NO animated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationNone;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger rows = 1;
#synchronized(_assets) {
if (_assets.count) rows++;
}
return rows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.accessoryType = _segmentedControl.selectedSegmentIndex == 0 ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
// Configure
switch (indexPath.row) {
case 0: {
cell.textLabel.text = #"Library photos and videos";
cell.detailTextLabel.text = #"media from device library";
break;
}
default: break;
}
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Did Select...");
// Browser
NSMutableArray *photos = [[NSMutableArray alloc] init];
NSMutableArray *thumbs = [[NSMutableArray alloc] init];
MWPhoto *photo, *thumb;
BOOL displayActionButton = YES;
BOOL displaySelectionButtons = NO;
BOOL displayNavArrows = NO;
BOOL enableGrid = YES;
BOOL startOnGrid = NO;
BOOL autoPlayOnAppear = NO;
//#synchronized(_assets) {
NSMutableArray *copy = [_assets copy];
if (NSClassFromString(#"PHAsset")) {
// Photos library
UIScreen *screen = [UIScreen mainScreen];
CGFloat scale = screen.scale;
// Sizing is very rough... more thought required in a real implementation
CGFloat imageSize = MAX(screen.bounds.size.width, screen.bounds.size.height) * 1.5;
CGSize imageTargetSize = CGSizeMake(imageSize * scale, imageSize * scale);
CGSize thumbTargetSize = CGSizeMake(imageSize / 3.0 * scale, imageSize / 3.0 * scale);
for (PHAsset *asset in copy) {
[photos addObject:[MWPhoto photoWithAsset:asset targetSize:imageTargetSize]];
[thumbs addObject:[MWPhoto photoWithAsset:asset targetSize:thumbTargetSize]];
}
}
else {
// Assets library
for (ALAsset *asset in copy) {
MWPhoto *photo = [MWPhoto photoWithURL:asset.defaultRepresentation.url];
[photos addObject:photo];
MWPhoto *thumb = [MWPhoto photoWithImage:[UIImage imageWithCGImage:asset.thumbnail]];
[thumbs addObject:thumb];
if ([asset valueForProperty:ALAssetPropertyType] == ALAssetTypeVideo) {
photo.videoURL = asset.defaultRepresentation.url;
thumb.isVideo = true;
}
}
}
//}
self.photos = photos;
self.thumbs = thumbs;
// Create browser
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
browser.displayActionButton = displayActionButton;
browser.displayNavArrows = displayNavArrows;
browser.displaySelectionButtons = displaySelectionButtons;
browser.alwaysShowControls = displaySelectionButtons;
browser.zoomPhotosToFill = YES;
browser.enableGrid = enableGrid;
browser.startOnGrid = startOnGrid;
browser.enableSwipeToDismiss = NO;
browser.autoPlayOnAppear = autoPlayOnAppear;
[browser setCurrentPhotoIndex:0];
// Test custom selection images
// browser.customImageSelectedIconName = #"ImageSelected.png";
// browser.customImageSelectedSmallIconName = #"ImageSelectedSmall.png";
// Reset selections
if (displaySelectionButtons) {
_selections = [NSMutableArray new];
for (int i = 0; i < photos.count; i++) {
[_selections addObject:[NSNumber numberWithBool:NO]];
}
}
// Show
[self.navigationController pushViewController:browser animated:YES];
}
#pragma mark - MWPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser {
return _photos.count;
}
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
if (index < _photos.count)
return [_photos objectAtIndex:index];
return nil;
}
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser thumbPhotoAtIndex:(NSUInteger)index {
if (index < _thumbs.count)
return [_thumbs objectAtIndex:index];
return nil;
}
//- (MWCaptionView *)photoBrowser:(MWPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index {
// MWPhoto *photo = [self.photos objectAtIndex:index];
// MWCaptionView *captionView = [[MWCaptionView alloc] initWithPhoto:photo];
// return [captionView autorelease];
//}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser actionButtonPressedForPhotoAtIndex:(NSUInteger)index {
NSLog(#"ACTION!");
}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser didDisplayPhotoAtIndex:(NSUInteger)index {
NSLog(#"Did start viewing photo at index %lu", (unsigned long)index);
}
- (BOOL)photoBrowser:(MWPhotoBrowser *)photoBrowser isPhotoSelectedAtIndex:(NSUInteger)index {
return [[_selections objectAtIndex:index] boolValue];
}
//- (NSString *)photoBrowser:(MWPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index {
// return [NSString stringWithFormat:#"Photo %lu", (unsigned long)index+1];
//}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index selectedChanged:(BOOL)selected {
[_selections replaceObjectAtIndex:index withObject:[NSNumber numberWithBool:selected]];
NSLog(#"Photo at index %lu selected %#", (unsigned long)index, selected ? #"YES" : #"NO");
}
//////////////
#pragma mark - Load Assets
- (void)loadAssets {
if (NSClassFromString(#"PHAsset")) {
// Check library permissions
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusNotDetermined) {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
[self performLoadAssets];
}
}];
} else if (status == PHAuthorizationStatusAuthorized) {
[self performLoadAssets];
}
} else {
// Assets library
[self performLoadAssets];
}
}
- (void)performLoadAssets {
// Initialise
_assets = [NSMutableArray new];
// Load
if (NSClassFromString(#"PHAsset")) {
// Photos library iOS >= 8
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PHFetchOptions *options = [PHFetchOptions new];
options.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
PHFetchResult *fetchResults = [PHAsset fetchAssetsWithOptions:options];
[fetchResults enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_assets addObject:obj];
}];
if (fetchResults.count > 0) {
// [self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
});
} else {
// Assets Library iOS < 8
_ALAssetsLibrary = [[ALAssetsLibrary alloc] init];
// Run in the background as it takes a while to get all assets from the library
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableArray *assetGroups = [[NSMutableArray alloc] init];
NSMutableArray *assetURLDictionaries = [[NSMutableArray alloc] init];
// Process assets
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result != nil) {
NSString *assetType = [result valueForProperty:ALAssetPropertyType];
if ([assetType isEqualToString:ALAssetTypePhoto] || [assetType isEqualToString:ALAssetTypeVideo]) {
[assetURLDictionaries addObject:[result valueForProperty:ALAssetPropertyURLs]];
NSURL *url = result.defaultRepresentation.url;
[_ALAssetsLibrary assetForURL:url
resultBlock:^(ALAsset *asset) {
if (asset) {
#synchronized(_assets) {
[_assets addObject:asset];
if (_assets.count == 1) {
// Added first asset so reload data
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
}
}
}
failureBlock:^(NSError *error){
NSLog(#"operation was not successfull!");
}];
}
}
};
// Process groups
void (^ assetGroupEnumerator) (ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if (group != nil) {
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:assetEnumerator];
[assetGroups addObject:group];
}
};
// Process!
[_ALAssetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {
NSLog(#"There is an error");
}];
});
}
}
#end
Next, I tried to make a ViewController inside the Storyboard of the sample project, disconnected the connection between the navigation controller and the Table View controller. I Connected the Navigation Controller to ViewController. I then assigned my newly made class named: MainViewController.m to ViewController in the storyboard.
I copied all the codes, or rather, copy all the implementation of codes in the Main.m (the class connected to Table View of the sample project), to my MainViewController.m. So here's the code of mine so far:
//
// MainViewController.m
// MWPhotoBrowser
//
// Created by Glenn on 9/28/15.
// Copyright (
c) 2015 Michael Waterfall. All rights reserved.
//
#import "MainViewController.h"
#import <Photos/Photos.h>
// #import "Menu.h"
#import "SDImageCache.h"
#import "MWCommon.h"
#interface MainViewController ()
{
MWPhotoBrowser *browser;
}
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self mwSetup];
self.title = #"MWPhotoBrowser";
// Clear cache for testing
[[SDImageCache sharedImageCache] clearDisk];
[[SDImageCache sharedImageCache] clearMemory];
[self loadAssets];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// self.navigationController.navigationBar.barTintColor = [UIColor greenColor];
// self.navigationController.navigationBar.translucent = NO;
// [self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// [self.navigationController setNavigationBarHidden:NO animated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationNone;
}
- (void)mwSetup
{
NSLog(#"Did Select...");
// Browser
NSMutableArray *photos = [[NSMutableArray alloc] init];
NSMutableArray *thumbs = [[NSMutableArray alloc] init];
MWPhoto *photo, *thumb;
BOOL displayActionButton = YES;
BOOL displaySelectionButtons = NO;
BOOL displayNavArrows = NO;
BOOL enableGrid = YES;
BOOL startOnGrid = NO;
BOOL autoPlayOnAppear = NO;
//#synchronized(_assets) {
NSMutableArray *copy = [_assets copy];
if (NSClassFromString(#"PHAsset")) {
// Photos library
UIScreen *screen = [UIScreen mainScreen];
CGFloat scale = screen.scale;
// Sizing is very rough... more thought required in a real implementation
CGFloat imageSize = MAX(screen.bounds.size.width, screen.bounds.size.height) * 1.5;
CGSize imageTargetSize = CGSizeMake(imageSize * scale, imageSize * scale);
CGSize thumbTargetSize = CGSizeMake(imageSize / 3.0 * scale, imageSize / 3.0 * scale);
for (PHAsset *asset in copy) {
[photos addObject:[MWPhoto photoWithAsset:asset targetSize:imageTargetSize]];
[thumbs addObject:[MWPhoto photoWithAsset:asset targetSize:thumbTargetSize]];
}
}
else {
// Assets library
for (ALAsset *asset in copy) {
MWPhoto *photo = [MWPhoto photoWithURL:asset.defaultRepresentation.url];
[photos addObject:photo];
MWPhoto *thumb = [MWPhoto photoWithImage:[UIImage imageWithCGImage:asset.thumbnail]];
[thumbs addObject:thumb];
if ([asset valueForProperty:ALAssetPropertyType] == ALAssetTypeVideo) {
photo.videoURL = asset.defaultRepresentation.url;
thumb.isVideo = true;
}
}
}
//}
self.photos = photos;
self.thumbs = thumbs;
// Create browser
browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
browser.displayActionButton = displayActionButton;
browser.displayNavArrows = displayNavArrows;
browser.displaySelectionButtons = displaySelectionButtons;
browser.alwaysShowControls = displaySelectionButtons;
browser.zoomPhotosToFill = YES;
browser.enableGrid = enableGrid;
browser.startOnGrid = startOnGrid;
browser.enableSwipeToDismiss = NO;
browser.autoPlayOnAppear = autoPlayOnAppear;
[browser setCurrentPhotoIndex:0];
// Test custom selection images
// browser.customImageSelectedIconName = #"ImageSelected.png";
// browser.customImageSelectedSmallIconName = #"ImageSelectedSmall.png";
// Reset selections
if (displaySelectionButtons) {
_selections = [NSMutableArray new];
for (int i = 0; i < photos.count; i++) {
[_selections addObject:[NSNumber numberWithBool:NO]];
}
}
// Show
[self.navigationController pushViewController:browser animated:YES];
//[self.view addSubview:browser.view];
}
#pragma mark - MWPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser {
return _photos.count;
}
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
if (index < _photos.count)
return [_photos objectAtIndex:index];
return nil;
}
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser thumbPhotoAtIndex:(NSUInteger)index {
if (index < _thumbs.count)
return [_thumbs objectAtIndex:index];
return nil;
}
//- (MWCaptionView *)photoBrowser:(MWPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index {
// MWPhoto *photo = [self.photos objectAtIndex:index];
// MWCaptionView *captionView = [[MWCaptionView alloc] initWithPhoto:photo];
// return [captionView autorelease];
//}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser actionButtonPressedForPhotoAtIndex:(NSUInteger)index {
NSLog(#"ACTION!");
}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser didDisplayPhotoAtIndex:(NSUInteger)index {
NSLog(#"Did start viewing photo at index %lu", (unsigned long)index);
}
- (BOOL)photoBrowser:(MWPhotoBrowser *)photoBrowser isPhotoSelectedAtIndex:(NSUInteger)index {
return [[_selections objectAtIndex:index] boolValue];
}
//- (NSString *)photoBrowser:(MWPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index {
// return [NSString stringWithFormat:#"Photo %lu", (unsigned long)index+1];
//}
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index selectedChanged:(BOOL)selected {
[_selections replaceObjectAtIndex:index withObject:[NSNumber numberWithBool:selected]];
NSLog(#"Photo at index %lu selected %#", (unsigned long)index, selected ? #"YES" : #"NO");
}
#pragma mark - Load Assets
- (void)loadAssets {
if (NSClassFromString(#"PHAsset")) {
// Check library permissions
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusNotDetermined) {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
[self performLoadAssets];
}
}];
} else if (status == PHAuthorizationStatusAuthorized) {
[self performLoadAssets];
}
} else {
// Assets library
[self performLoadAssets];
}
}
- (void)performLoadAssets {
// Initialise
_assets = [NSMutableArray new];
// Load
if (NSClassFromString(#"PHAsset")) {
// Photos library iOS >= 8
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
PHFetchOptions *options = [PHFetchOptions new];
options.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
PHFetchResult *fetchResults = [PHAsset fetchAssetsWithOptions:options];
[fetchResults enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_assets addObject:obj];
}];
if (fetchResults.count > 0) {
[browser reloadData]; }
});
} else {
// Assets Library iOS < 8
_ALAssetsLibrary = [[ALAssetsLibrary alloc] init];
// Run in the background as it takes a while to get all assets from the library
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableArray *assetGroups = [[NSMutableArray alloc] init];
NSMutableArray *assetURLDictionaries = [[NSMutableArray alloc] init];
// Process assets
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result != nil) {
NSString *assetType = [result valueForProperty:ALAssetPropertyType];
if ([assetType isEqualToString:ALAssetTypePhoto] || [assetType isEqualToString:ALAssetTypeVideo]) {
[assetURLDictionaries addObject:[result valueForProperty:ALAssetPropertyURLs]];
NSURL *url = result.defaultRepresentation.url;
[_ALAssetsLibrary assetForURL:url
resultBlock:^(ALAsset *asset) {
if (asset) {
#synchronized(_assets) {
[_assets addObject:asset];
if (_assets.count == 1) {
// Added first asset so reload data
[browser reloadData];
}
}
}
}
failureBlock:^(NSError *error){
NSLog(#"operation was not successfull!");
}];
}
}
};
// Process groups
void (^ assetGroupEnumerator) (ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if (group != nil) {
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:assetEnumerator];
[assetGroups addObject:group];
}
};
// Process!
[_ALAssetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {
NSLog(#"There is an error");
}];
});
}
}
#end
Please help. And can someone help me edit the format of my 2nd code? Thanks.
Okay, so I got it working. Basically, the MWPhotoBrowser needs a first view controller before loading itself.
For instance, in the sample project, the first screen is the Table View. Then you will select between the 9 rows, after that, the MWPhotoBrowser viewcontroller will be showed.
In my implementation, I just made a first view controller, and then added a button. When you click the button, the MWPhotoBrowser will be showed.
So that's how MWPhotoBrowser works. It doesn't load the photos and videos if you push the MWPhotoBrowser inside the ViewWillAppear, ViewDidLoad, ViewDidAppear.
Now, I have to learn or know how to load videos and photos from different album.
I've already asked the same question about saving pressed button state and I thought that I should do in the same way to save progress state on cells but my tries are unsuccessful.
What I'm doing now: I select some UICollectionViewCell's and then press "download" button and then downolad action starts. Every cell I selected shows UIProgressView and everything is ok untill I scroll my UICollectionView up or down. When I do it another cells have progress view too but they mustn't! I know that I must save indexPath of selected cells in NSMutableArray and then in cellForItemAtIndexPath check if current cell indexPath is in my array and then show or hide my cell's subviews. I do that but it only works with cell selection! What should I do to save progress view state on each cell this progress really there?
Here is my code:
In cellForItemAtIndexPath:
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if ([selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
cell.selectedBG.hidden = NO;
cell.selectedImg.hidden = NO;
}
else
{
cell.selectedBG.hidden = YES;
cell.selectedImg.hidden = YES;
cell.progressView.hidden = YES;
}
In didSelectItemAtIndexPath:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
...
// Add the selected item into the array
[selectedIds addObject:selectedId];
[selectedVideos addObject:selectedVideo];
AVMVideoCell *collectionCell = (AVMVideoCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if ( ![selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
[selecedCellsArray addObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
collectionCell.selectedBG.hidden = NO;
collectionCell.selectedImg.hidden = NO;
[collectionCell setSelected:YES];
}
}
}
In didDeselectItemAtIndexPath:
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
...
// Delete the selected item from the array
[selectedIds removeObject:selectedId];
[selectedVideos removeObject:selectedVideo];
AVMVideoCell *collectionCell = (AVMVideoCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if ( [selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
[selecedCellsArray removeObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
collectionCell.selectedBG.hidden = YES;
collectionCell.selectedImg.hidden = YES;
[collectionCell setSelected:NO];
}
}
And this is how I show my progress:
-(NSString *)progress:(long long )val1 : (long long )val2 : (AVMVideoCell *)cell : (NSString *)name : (NSIndexPath *)path{
float progress = ((float)val1) / val2;
NSString *prog = [[NSNumber numberWithFloat:progress*100] stringValue];
if (prog != nil){
if(cell.isSelected){
cell.selectedImg.hidden = YES;
cell.progressView.hidden = NO;
}
}
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)path.row];
if ( [selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]])
{
[cell.progressView setProgress:progress animated:YES];
}
if([prog intValue]==100){
cell.progressView.hidden = YES;
}
return prog;
}
EDIT: AVMVideoCell.m
#import "AVMVideoCell.h"
#implementation AVMVideoCell
{
NSString *fullUrl;
}
#synthesize imageView;
#synthesize selectedBG;
#synthesize progressLabel;
#synthesize progressView;
#synthesize selectedImg;
#synthesize progLayer;
-(void) setVideo:(AVMDataStore *)video {
if(_video != video) {
_video = video;
}
NSString *durString = [NSString stringWithFormat:#"%#",[self timeFormatted:_video.duration]];
if((_video.filePreview != nil) && ![_video.filePreview isEqualToString:#""]){
fullUrl = [NSString stringWithFormat:#"http://example.com%#",_video.filePreview];
}
NSURL *imgURL = [NSURL URLWithString:fullUrl];
[self.imageView setImageWithURL:imgURL
placeholderImage:[UIImage imageNamed:#"yesterday.png"] options:0 usingActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
self.duration.text = durString;
}
- (NSString *)timeFormatted:(int)totalSeconds
{
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
return [NSString stringWithFormat:#"%02d:%02d:%02d",hours, minutes, seconds];
}
#end
EDIT 2: Explanation about progress
My progressView is not an IBOutlet it's a #property (nonatomic,strong) UIProgressView *progressView; (AVMVideoCell.h)
I allocate and initialize it in cellForItemAtIndexPath:
cell.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0,0, 150.0f, 1.0f)];
cell.progressView.trackTintColor = [UIColor colorWithWhite:255 alpha:0.5f];
cell.progressView.progressTintColor = [UIColor whiteColor];
[cell.progLayer addSubview:cell.progressView];
cell.progressView.hidden = YES;
cell.progressView.tag = indexPath.row+500;
This is where I call progress change showing:
-(void)downloadStart:(NSString*)fileUrl : (NSString*)name : (AVMVideoCell *) cell : (NSIndexPath *)path{
NSURL *URL = [NSURL URLWithString:fileUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSString *fileName = [NSString stringWithFormat:#"%#.mp4",name]; //set full file name to save
AFHTTPRequestOperation *downloadRequest = [[AFHTTPRequestOperation alloc] initWithRequest:request]; //create download request
[downloadRequest setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData *data = [[NSData alloc] initWithData:responseObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *pathToFile = [NSString stringWithFormat:#"%#/%#", [paths firstObject],fileName]; // path to 'Documents'
NSString *pathOfFile = [[paths objectAtIndex:0] stringByAppendingPathComponent:fileName];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:pathOfFile append:NO];
BOOL success = [data writeToFile:pathToFile atomically:YES];
if(success){
[self checkIfExists:name : cell :path];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"file downloading error : %#", [error localizedDescription]);
UIAlertView * alert=[[UIAlertView alloc]initWithTitle:#"Error" message:[NSString stringWithFormat:#"%#",error] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil ];
[alert show];
cell.progressView.hidden = YES;
}];
// Step 5: begin asynchronous download
[downloadRequest start];
[downloadRequest setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
[self progress:totalBytesRead :totalBytesExpectedToRead :cell :name : path]; //here I pass val1 and val 2
}];
}
When I select an items in collection view, I gather their model's objects, get each id and make array of urls, then in for..in loop i pass urls one by one and then start async download. You can see how I download and call progress method above.
I need to know if you have a reuse issue or a model issue.
So first try this :
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
...
if (![selectedIds.containsObject:selectedId])
{
// Add the selected item into the array
[selectedIds addObject:selectedId];
[selectedVideos addObject:selectedVideo];
AVMVideoCell *collectionCell = (AVMVideoCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if ( ![selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
[selecedCellsArray addObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
collectionCell.selectedBG.hidden = NO;
collectionCell.selectedImg.hidden = NO;
[collectionCell setSelected:YES];
}
}
else {
// Delete the selected item from the array
[selectedIds removeObject:selectedId];
[selectedVideos removeObject:selectedVideo];
AVMVideoCell *collectionCell = (AVMVideoCell*)[collectionView cellForItemAtIndexPath:indexPath];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if ( [selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
[selecedCellsArray removeObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
collectionCell.selectedBG.hidden = YES;
collectionCell.selectedImg.hidden = YES;
[collectionCell setSelected:NO];
}
}
and remove the didDeselect delegate.
Let me know what happens then.
EDIT :
Ok try this now:
// Lazy instantiation of the progressView
- (UIProgressView *)progressView
{
if (!_progressView)
{
_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0,0, 150.0f, 1.0f)];
_progressView.trackTintColor = [UIColor colorWithWhite:255 alpha:0.5f];
_progressView.progressTintColor = [UIColor whiteColor];
_progressView.hidden = YES;
[self.contentView addSubview:_progressView];
}
return _progressView;
}
// Here we remove the progressView on reuse
-(void)prepareForReuse
{
[super prepareForReuse];
[self.progressView removeFromSuperview];
self.progressView = nil;
}
Also remove what you did with the progressView with in the cellForItemAtIndexPath method.
You can create class for cell's models, store these models in array in your viewController and use them to get/set all states from/for cells.
Something like this:
#interface CellModel : NSObject
#property(nonatomic) BOOL selected;
#property(nonatomic) NSUInteger progress;
#end
In viewController:
#interface MyViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic) NSArray* models;
#end
I wish to send an array of say stationNames in a SOAP call .
Please help.
This code for manual input to array
- (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField
possibleCompletionsForString:(NSString *)string
completionHandler:(void (^)(NSArray *))handler
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
if(self.simulateLatency){
CGFloat seconds = arc4random_uniform(4)+arc4random_uniform(4); //normal distribution
NSLog(#"sleeping fetch of completions for %f", seconds);
sleep(seconds);
}
NSArray *completions;
if(self.testWithAutoCompleteObjectsInsteadOfStrings){
completions = [self allCountryObjects];
} else {
completions = [self allCountries];
}
handler(completions);
});
}
- (NSArray *)allCountryObjects
{
if(!self.countryObjects){
NSArray *countryNames = [self allCountries];
NSMutableArray *mutableCountries = [NSMutableArray new];
for(NSString *countryName in countryNames){
DEMOCustomAutoCompleteObject *country = [[DEMOCustomAutoCompleteObject alloc] initWithCountry:countryName];
[mutableCountries addObject:country];
}
[self setCountryObjects:[NSArray arrayWithArray:mutableCountries]];
}
return self.countryObjects;
}
- (NSArray *)allCountries
{
NSArray *countries =
#[
#"Item1",
#"Item2",
#"Item3",
#"Item4",
#"Item5",
#"Item6",
#"Item7",
#"Item8",
#"Item9",
];
return countries;
}
- (BOOL)autoCompleteTextField:(MLPAutoCompleteTextField *)textField
shouldConfigureCell:(UITableViewCell *)cell
withAutoCompleteString:(NSString *)autocompleteString
withAttributedString:(NSAttributedString *)boldedString
forAutoCompleteObject:(id<MLPAutoCompletionObject>)autocompleteObject
forRowAtIndexPath:(NSIndexPath *)indexPath;
//This is your chance to customize an autocomplete tableview cell before it appears
in the autocomplete tableview
NSString *filename = [autocompleteString stringByAppendingString:#".png"];
filename = [filename stringByReplacingOccurrencesOfString:#" " withString:#"-"];
filename = [filename stringByReplacingOccurrencesOfString:#"&" withString:#"and"];
[cell.imageView setImage:[UIImage imageNamed:filename]];
return YES;
}
- (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField
didSelectAutoCompleteString:(NSString *)selectedString
withAutoCompleteObject:(id<MLPAutoCompletionObject>)selectedObject
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedObject){
NSLog(#"selected object from autocomplete menu %# with string %#", selectedObject,
[selectedObject autocompleteString]);
} else {
NSLog(#"selected string '%#' from autocomplete menu", selectedString);
}
}
I am putting a twitter feed in my app and no matter what I have tried to do to get the tweets to display in their entirety, I have not been entirely successful. Most tweets show up fine but it's the really long ones that have given me a headache. I thought it was because I was somehow not getting enough lines in my textLabel, but I noticed that if a user has elongated their tweet by hitting enter multiple times the tweets would show up fine. Which leads me to believe somehow it is truncating after a certain amount of characters. I don't know if I'm doing something wrong or if this is just an issue with twitter. If anyone can see anything in my code that is wrong, or could be changed to fix this, please let me know. Thank you
#import "TwitterFeedTVC.h"
#import "TweetVC.h"
#import <QuartzCore/QuartzCore.h>
#import "GTMNSString+HTML.h"
#define REFRESH_HEADER_HEIGHT 52.0f
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f
#implementation TwitterFeedTVC
#synthesize textPull, textRelease, textLoading, refreshHeaderView, refreshLabel, refreshArrow, refreshSpinner, twitterFeedName, twitterFeedTitle;
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self != nil)
{
[self setupStrings];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self != nil)
{
[self setupStrings];
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self != nil)
{
[self setupStrings];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.twitterFeedTitle.text = self.twitterFeedName;
self.navigationItem.title = self.twitterFeedName;
[self fetchTweets];
[self addPullToRefreshHeader];
}
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://api.twitter.com/1/statuses/user_timeline.json?include_rts=true&screen_name=johnnelm9r&count=100"]];
if (data == nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Twitter Is Not Responding. Please Try Again Later!"
delegate:self
cancelButtonTitle:#"Kali Baby"
otherButtonTitles: nil];
[alert show];
}
else
{
NSError *error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (void)viewDidUnload
{
[super viewDidUnload];
textPull = nil;
textRelease = nil;
textLoading = nil;
refreshHeaderView = nil;
refreshLabel = nil;
refreshArrow = nil;
refreshSpinner = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:#"text"];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), CGFLOAT_MAX);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height, 44.0f);
return height + ((CELL_CONTENT_MARGIN * 2) + 9);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[dateFormatter setDateFormat:#"EEE MMM dd HH:mm:ss +0000 yyyy"];
NSDate *currentDate = [dateFormatter dateFromString:[tweet objectForKey:#"created_at"]];
NSDate *todayDate = [NSDate date];
NSString *date = [dateFormatter stringFromDate:currentDate];
double timeInterval = [currentDate timeIntervalSinceDate:todayDate];
timeInterval = timeInterval * -1;
if (timeInterval < 1)
{
date = #"never";
}
else if (timeInterval <60)
{
date = #"less than a minute ago";
}
else if (timeInterval <3600)
{
int diff = round(timeInterval / 60);
date = [NSString stringWithFormat:#"%d minutes ago", diff];
}
else if (timeInterval < 86400)
{
int diff = round(timeInterval / 60 / 60);
date = [NSString stringWithFormat:#"%d hours ago", diff];
}
else if (timeInterval < 2629743)
{
int diff = round(timeInterval / 60 / 60 / 24);
date = [NSString stringWithFormat:#"%d days ago", diff];
}
else
{
date = #"never";
}
cell.textLabel.text = [[tweet objectForKey:#"text"] gtm_stringByUnescapingFromHTML];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", date];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageUrl = [[tweet objectForKey:#"user"] objectForKey:#"profile_image_url"];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:data];
[cell addSubview:cell.imageView];
});
});
return cell;
}
- (void)setupStrings
{
textPull = #"Pull Down To Be Fresh...";
textRelease = #"Release To Be Fresh...";
textLoading = #"Getting Loaded...";
}
- (void)addPullToRefreshHeader
{
refreshHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0 - REFRESH_HEADER_HEIGHT, 320, REFRESH_HEADER_HEIGHT)];
refreshHeaderView.backgroundColor = [UIColor clearColor];
refreshLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, REFRESH_HEADER_HEIGHT)];
refreshLabel.backgroundColor = [UIColor clearColor];
refreshLabel.font = [UIFont boldSystemFontOfSize:12.0];
refreshLabel.textAlignment = UITextAlignmentCenter;
refreshArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"KrizzOpener.png"]];
refreshArrow.frame = CGRectMake(floorf((REFRESH_HEADER_HEIGHT - 27) / 2),
(floorf(REFRESH_HEADER_HEIGHT - 44) / 2),
27, 44);
refreshSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
refreshSpinner.frame = CGRectMake(floorf(floorf(REFRESH_HEADER_HEIGHT - 20) / 2), floorf((REFRESH_HEADER_HEIGHT - 20) / 2), 20, 20);
refreshSpinner.hidesWhenStopped = YES;
[refreshHeaderView addSubview:refreshLabel];
[refreshHeaderView addSubview:refreshArrow];
[refreshHeaderView addSubview:refreshSpinner];
[self.tableView addSubview:refreshHeaderView];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (isLoading) return;
isDragging = YES;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (isLoading) {
if (scrollView.contentOffset.y > 0)
self.tableView.contentInset = UIEdgeInsetsZero;
else if (scrollView.contentOffset.y >= -REFRESH_HEADER_HEIGHT)
self.tableView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
} else if (isDragging && scrollView.contentOffset.y < 0) {
[UIView beginAnimations:nil context:NULL];
if (scrollView.contentOffset.y < -REFRESH_HEADER_HEIGHT) {
refreshLabel.text = self.textRelease;
} else {
refreshLabel.text = self.textPull;
}
[UIView commitAnimations];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (isLoading) return;
isDragging = NO;
if (scrollView.contentOffset.y <= -REFRESH_HEADER_HEIGHT)
{
[self startLoading];
}
}
- (void)startLoading {
isLoading = YES;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
self.tableView.contentInset = UIEdgeInsetsMake(REFRESH_HEADER_HEIGHT, 0, 0, 0);
refreshLabel.text = self.textLoading;
refreshArrow.hidden = YES;
[refreshSpinner startAnimating];
[UIView commitAnimations];
[self refresh];
}
- (void)stopLoading {
isLoading = NO;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDidStopSelector:#selector(stopLoadingComplete:finished:context:)];
self.tableView.contentInset = UIEdgeInsetsZero;
UIEdgeInsets tableContentInset = self.tableView.contentInset;
tableContentInset.top = 0.0;
self.tableView.contentInset = tableContentInset;
[UIView commitAnimations];
}
- (void)stopLoadingComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
refreshLabel.text = self.textPull;
refreshArrow.hidden = NO;
[refreshSpinner stopAnimating];
}
- (void)refresh {
[self fetchTweets];
[self performSelector:#selector(stopLoading) withObject:nil afterDelay:2.7];
}
#pragma mark - Table view delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"tweetVC"])
{
NSInteger row = [[self tableView].indexPathForSelectedRow row];
NSDictionary *tweet = [tweets objectAtIndex:row];
TweetVC *tweetVC = segue.destinationViewController;
tweetVC.detailItem = tweet;
}
}
After a lot of investigation I found an article somewhere that said there is a difference between the text key and the retweeted_status.text key. The tweets being truncated were, in fact, retweets from the user using the text key. However, the retweeted_status.text key is only the tweet that was retweeted, without the user who originally tweeted it. That sucks because the retweeted_status.text key is NOT truncated. Ahhh, the joys of programming. Hopefully the new twitter API will address this. I hope this answer helps somebody.
Here is the link to what I found in case anyone is interested: http://code.google.com/p/twitter-api/issues/detail?id=2261
I haven't gone through your code, but I thought of mentioning this point that Twitter only allows tweet 140 characters long! This is the basic rule of a tweet. So no matter what you do tweet with mode than 140 characters will be truncated.
I hope this is not the issue?