How to recognize File or Folder from dropbox APi - iphone

Hello I am using dropbox api and displaying meta data from dropbox account..
I want to differentiate files and folders from loaded data..because I want to show next level if there is folder and if there is file I don't want to show next View
my code to load data
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
[self.metaArray release];
self.metaArray = [[NSMutableArray alloc]init ];
for (DBMetadata *child in metadata.contents) {
NSString *folderName = [[child.path pathComponents] lastObject];
[self.metaArray addObject:folderName];
}
[self.tableView reloadData];
[self.activityIndicator stopAnimating];
}

According to the Dropbox Developer Docs the metadata includes a property called is_dir which should allow you to determine whether the particular item is a directory or not.
Looking at the header of DBMetaData it is indeed exposed as a property
#property (nonatomic, readonly) BOOL isDirectory;
So you can just do a simple test like so
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata
{
if (metadata.isDirectory) {
// handle directory here
} else {
// handle file here
}
}
With regards pushing views based on whether or not an entry is a directory, you could subclass UITableViewCell and add an isDirectory property. Instead of adding just the name to self.metaArray you could add a dictionary containing both the name and the value of isDirectory. Then in your table view datasource where you populate the cells you'd set the isDirectory property of the UITableViewCell based on the same property in the appropriate dictionary from the array. Finally, in the table view delegate method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
you can get the selected cell using the indexPath and then test the isDirectory property and based on it's value take the appropriate action.
Hope this helps.

Using the API V2 of Dropbox with the Dropbox SDK is:
DropboxClient *client = [DropboxClientsManager authorizedClient];
[[client.filesRoutes listFolder:path]
response:^(DBFILESListFolderResult *result, DBFILESListFolderError *routeError, DBRequestError *error) {
if (result) {
for (DBFILESMetadata *entry in result.entries) {
if ([entry isKindOfClass:[DBFILESFileMetadata class]]) {
DBFILESFileMetadata *fileMetadata = (DBFILESFileMetadata *)entry;
NSLog(#"File: %#", fileMetadata.name);
} else if ([entry isKindOfClass:[DBFILESFolderMetadata class]]) {
DBFILESFolderMetadata *folderMetadata = (DBFILESFolderMetadata *)entry;
NSLog(#"Folder: %#", folderMetadata.name);
}
}
}

Related

add additional cells to the bottom ofUICollectionView

Im using UICollectionView to get a set of images from instagram however i want to load past images when i get to the bottom of the page. I do however recieve the past images because they are logged into the console but dont appear on the screen.
Here is my code to retrieve the images:
- (void)nextInstagramPage:(NSIndexPath *)indexPath{
NSDictionary *page = self.timelineResponse[#"pagination"];
NSString *nextPage = page[#"next_url"];
[[InstagramClient sharedClient] getPath:[NSString stringWithFormat:#"%#",nextPage] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.timelineResponse = responseObject;
[self.collectionView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
}
Here is my code to detect when the user gets to the bottom of the screen:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
[self nextInstagramPage:indexPath];
}
Edit: I have found out that the collectionview is a subclass of uiscrollview so how would i correctly implement a method which has an indexpath to detect the bottom of the scrollview!
With in your code i am making 2 assumptions, that you have an array of data to run parallel with your ui collection view and the delegate/datasource methods. you would notice at least these two methods in your code.
– collectionView:numberOfItemsInSection:
– collectionView:cellForItemAtIndexPath:
the first one tells you how many items/cells/squares you see in your ui collection view, the second makes you create what the item/cell/square will look like.
In the first method you told the uicolelction view how many there were, with in the cellforitem you should test "is this the last image" if it is the last image then download more images, if it is not the last image then continue as if nothing happened
Edit:
[[self entries] count]
you had that, now with in cell for row at index path do
if ( indexpath.item == ([[self entries] count] - 1) ) {
// download more data and add it to the 'entries' array
}

Multiple Threads Using GCD while Fetching Photos

I have to fetch contacts from the Address Book and show photo beside each if found in a UITableView.
I fetch all contacts using ABContactsHelper library and then asynchronously fetch photos for visible rows in the UITableView using GCD blocks.
I referred to an Apple Sample code which waits for the UITableView to finish scrolling, get Visible NSIndexPaths & created threads to fetch photos. My problem so far is two fold.
First, if user scrolls, stops, scrolls & stops and does it quite a few times, too many threads are generated for fetching photos which slows down the app.
Secondly, when the thread returns to set photo in cache as well as the UITableViewCell however, the reference to UIImageView is now being reused for another record in UITableViewCell, hence the photo is placed on wrong record which eventually gets replace by correct one, when thread for that particular record returns.
Here is the code I is used both in cellForRowAtIndexPath as well as when UITableView stops scrolling.
- (void)loadImagesLazilyForIndexPath:(NSIndexPath *)indexPath photo:(UIImageView *)photo contact:(ContactModel *)contact
{
if (!self.tableView.isDragging && !self.tableView.isDecelerating) {
UIImage *thePhoto = [self.imagesForContacts objectForKey:indexPath];
if (!thePhoto) {
// NSLog(#"Photo Not Found - Now Fetching %#", indexPath);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
#autoreleasepool {
UIImage *image = [[JMContactsManager sharedContactsManager] photoForContact:contact];
if (!image)
image = self.noProfilePhoto;
[self.imagesForContacts setObject:image forKey:indexPath];
dispatch_async(dispatch_get_main_queue(), ^{
// NSLog(#"Photo Fetched %#", indexPath);
#autoreleasepool {
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
BOOL visible = [visiblePaths indexOfObjectPassingTest:^BOOL(NSIndexPath * ip, NSUInteger idx, BOOL *stop) {
if (ip.row == indexPath.row && ip.section == indexPath.section) {
*stop = YES;
return 1;
}
return 0;
}];
if (visible)
photo.image = [self.imagesForContacts objectForKey:indexPath];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
});
}
});
} else {
// NSLog(#"Photo Was Found %#", indexPath);
#autoreleasepool {
photo.image = [self.imagesForContacts objectForKey:indexPath];
}
}
}
}
For this kind of functionality I would go with an NSOperation and an NSOperationQueue, they are build on top of GCD, but it gives you the opportunity to cancel operations. You could check which operation aren't visible anymore and cancel them. In thi s way you can control reference "away".
I see also another issue that could lead into a "problem" it seems that you are caching images in an NSMutableDictionary, aren't you? Or are you using an NSCache? If it is an NScache is fine, but most of mutable object aren't thread safe "naturally"
Boost up the priority of the queue :-)
As mentioned by #Andrea, you should be using an NSOperationQueue, which gives you the ability to cancel queued tasks.
Indexing your image cache by indexPath into your table is not robust as an index path for a given element could change (although maybe not in your specific case). You might consider indexing your image cache by ABRecord.uniqueId instead.
In any case it will not solve the problem of your images being set twice or more for the same cell. This happens because UITableView does not assign a view for each item but manages a pool of UITableCellViews, which it re-uses each time. What you could do is something along the following lines:
// Assuming your "ContactCellView" inherits from UITableCellView and has a contact property
// declared as follows: #property (retain) ABRecord *contact.
- (void) setContact:(ABRecord*)contact
{
_contact = contact;
__block UIImage *thePhoto = [self.imagesForContacts objectForKey:contact.uniqueId];
if (thePhoto == nil) {
_loadImageOp = [NSBlockOperation blockOperationWithBlock:^(void) {
// Keep a local reference to the contact because it might change on us at any time.
ABRecord *fetchContact = contact;
// Fetch the photo as you normally would
thePhoto = [[JMContactsManager sharedContactsManager] photoForContact:fetchContact];
if (thePhoto == nil)
thePhoto = self.noProfilePhoto;
// Only assign the photo if the contact has not changed in the mean time.
if (fetchContact == _contact)
_contactPhotoView.image = thePhoto;
}];
} else {
_contactPhotoView.image = thePhoto;
}
}

Add Image and Caption Text to Three20 Table section using TTSectionedDataSource

I am trying to create table using TTTableViewController. I want to display an image in the section header along with some caption text, something similar to what instagram and many other application does. I tried using the sample from TTNavigatorDemo to display data from TTSectionedDatasource, (I am not sure if this is the right way to do it, please suggest some better way if you know any). It contains section name/headers as regular strings and data as TTTableViewItem. I tried implementing <UITableViewDelegate> protocol and achieved it using viewForHeaderInSection method, now the data is separated from section, Is there any better way using Three20 through which I can pass on my Header/Section view and TableItemView along in a datasource as it would allow me to implement the TTTableViewDragRefreshDelegate, which I was not able to implement when implementing the <UITableViewDelegate> protocol in my class.
My class declaration right now, looks something like
#interface ImageTable : TTTableViewController <UITableViewDelegate> { }
I would like to show section headers with image and text like:
:
I am currently using the TTNavigatorCode to populate the data in my
Table which is:
self.dataSource = [TTSectionedDataSource dataSourceWithObjects:
#"Food"
[TTTableViewItem itemWithText:#"Porridge" URL:#"tt://food/porridge"],
[TTTableTextItem itemWithText:#"Bacon & Eggs" URL:#"tt://food/baconeggs"],
[TTTableTextItem itemWithText:#"French Toast" URL:#"tt://food/frenchtoast"],
#"Drinks",
[TTTableTextItem itemWithText:#"Coffee" URL:#"tt://food/coffee"],
[TTTableTextItem itemWithText:#"Orange Juice" URL:#"tt://food/oj"],
#"Other",
[TTTableTextItem itemWithText:#"Just Desserts" URL:#"tt://menu/4"],
[TTTableTextItem itemWithText:#"Complaints" URL:#"tt://about/complaints"],
#"Other",
[TTTableTextItem itemWithText:#"Just Desserts" URL:#"tt://menu/4"],
[TTTableTextItem itemWithText:#"Complaints" URL:#"tt://about/complaints"],
nil];
I was able to make something similar by implementing the method:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return myCustomView;
}
But since my data is coming from the Model Class and I want my generate views and add them to datasource in a formatted way. I would like to know if there is a way using Three20 through which I specify an Array of my data for sections and another array for corresponding data under the sections?
Your help is highly appreciated
Thanks
Create Delegate FOOTableDelegate.h
#interface FOOTableDelegate : TTTableViewDragRefreshDelegate
{
}
- (UIView*) createHeaderView:(FOODataSource *)fDataSource forSectionID:(NSString *)secID;
#end
In FOOTableDelegate.m file
- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (tableView.style == UITableViewStylePlain)
{
if ([tableView.dataSource respondsToSelector:#selector(tableView:titleForHeaderInSection:)])
{
NSString* title = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
if (title.length > 0)
{
UIView* header = [_headers objectForKey:title];
if (nil != header)
{
header.alpha = 1;
}
else
{
if (nil == _headers)
{
_headers = [[NSMutableDictionary alloc] init];
}
header = [self createHeaderView:tableView.dataSource forSectionID:title];
[_headers setObject:header forKey:title];
}
return header;
}
}
}
return nil;
}
- (UIView*) createHeaderView:(FeedDataSource *)fDataSource forSectionID:(NSString *)secID
{
//do some code for header View
return View;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
//return height for the Section Header
return height;
}
This will help you to solve your problem

Three20 : how to pass a class of objects between 2 views

I have a TTableView. The items in this table a mapped to an url, so that when I click on an item, another view appear with informations about this item.
All these informations are attributes of a class. So, how can I build my TTableTextItem URL in order to transmit the class containing informations to the view responsible for the display of these informations ?
Thanks in advance.
One way of doing it is to use a TTURLAction. When the user selects a row in your table, which will call your didSelectObject (of TTTableViewController) method, extract the object or set of objects you want to pass and build a TTURLAction like this:
TTURLAction *action = [[[TTURLAction actionWithURLPath:#"tt://showUser"]
applyQuery:[NSDictionary dictionaryWithObject:user forKey:#"kParameterUser"]]
applyAnimated:YES];
Then open the action:
[[TTNavigator navigator] openURLAction:action];
The controller you want to open as a result of this action should be registered in your TTURLMap and should have a constructor thus:
- (id) initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query {
self = [super init];
if (self != nil) {
self.user = [query objectForKey:kParameterUser];
}
return self;
}
I tend to create categories on classes for objects I want to be able to open another controller and display themselves.
The big problem with directly using TTURLAction is that you can't really use them with TTTableViewItem. The only way to really do it is override -didSelectObject:atIndexPath: and build your custom TTURLAction with your desired object in the query dictionary. But this breaks the nice separation of Model and View Controller, and gets complicated once you have multiple objects to pass.
Instead of this, I've been using a small category which automatically takes the userInfo property of the table item (which I set to whatever I need), and automatically adds it as a URL parameter.
And then you use this to retrieve it in your mapped view controller.
- (id)initWithNavigatorURL:(NSURL *)URL query:(NSDictionary *)query {
if (self = [self initWithNibName:nil bundle:nil]) {
id myPassedObject = [query objectForKey:#"__userInfo__"];
// do the rest of your initlization
}
return self;
}
You can download it as a GitHub Gist here. The code is also below. We're considering merging this into the main branch at some point.
TTTableViewDelegate+URLAdditions.h
#interface TTTableViewDelegate(URLAdditions)
#end
TTTableViewDelegate+URLAdditions.m
#import "TTTableViewDelegate+URLAdditions.h"
#implementation TTTableViewDelegate(URLAdditions)
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
id<TTTableViewDataSource> dataSource = (id<TTTableViewDataSource>)tableView.dataSource;
id object = [dataSource tableView:tableView objectForRowAtIndexPath:indexPath];
// Added section to automatically wrap up any TTTableItem userInfo objects. If it is a dictionary, it gets sent directly
// If it is not, it is put in a dictionary and sent as they __userInfo__ key
if( [object isKindOfClass:[TTTableLinkedItem class]] ) {
TTTableLinkedItem* item = object;
if( item.URL && [_controller shouldOpenURL:item.URL] ) {
// If the TTTableItem has userInfo, wrap it up and send it along to the URL
if( item.userInfo ) {
NSDictionary *userInfoDict;
// If userInfo is a dictionary, pass it along else create a dictionary
if( [item.userInfo isKindOfClass:[NSDictionary class]] ) {
userInfoDict = item.userInfo;
} else {
userInfoDict = [NSDictionary dictionaryWithObject:item.userInfo forKey:#"__userInfo__"];
}
[[TTNavigator navigator] openURLAction:[[[TTURLAction actionWithURLPath:item.URL]
applyQuery:userInfoDict]
applyAnimated:YES]];
} else {
TTOpenURL( item.URL );
}
}
if( [object isKindOfClass:[TTTableButton class]] ) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
else if( [object isKindOfClass:[TTTableMoreButton class]] ) {
TTTableMoreButton* moreLink = (TTTableMoreButton*)object;
moreLink.isLoading = YES;
TTTableMoreButtonCell* cell
= (TTTableMoreButtonCell*)[tableView cellForRowAtIndexPath:indexPath];
cell.animating = YES;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if( moreLink.model )
[moreLink.model load:TTURLRequestCachePolicyDefault more:YES];
else
[_controller.model load:TTURLRequestCachePolicyDefault more:YES];
}
}
[_controller didSelectObject:object atIndexPath:indexPath];
}
#end
I think the documentation on the official website does describe very clearly the navigation scheme for Three20. Your question is the very common task of any application, and Three20 provides powerful support for that.
I have little fix this awesome code :)
as posted version strip callback from TTTableButton.
Correction is:
if( [object isKindOfClass:[TTTableButton class]] ) {
if (item.delegate && item.selector) {
[item.delegate performSelector:item.selector withObject:object];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

Possible View Caching problem?

I'm building an iphone app and I've got a table view with some textfields inside the cells, the content of the fields is set in viewWillAppear (its a grouped TableView w/ 3 fields that are always the same). The content of the text fields is retrieved from getter methods that return values from various class variables.
The problem I'm having is the getter seems to be returning the original value, not the value that is modified by the setter method. The class variable is an NSMutableString. Is it possible the view is caching the method call?
//header file
#implementation ManageWorkoutViewController : UIViewController {
NSMutableString *workoutDifficulty;
}
-(void)setWorkoutDifficulty:(NSString *)value;
-(NSString *)getWorkoutDifficulty;
#end
//implementation file
-(NSString *)getWorkoutDifficulty {
if (nil == workoutDifficulty) {
workoutDifficulty = [NSMutableString stringWithString:#"Easy"];
}
NSLog(#"getter: Returning workoutDifficulty as: %#", workoutDifficulty);
return workoutDifficulty;
} //end getWorkoutDifficulty
-(void)setWorkoutDifficulty:(NSString *)value {
workoutDifficulty = [NSString stringWithFormat:#"%d", value];
NSLog(#"setter: workoutDifficulty set as: %#", workoutDifficulty);
}//end setWorkoutDifficulty
//elsewhere in the implementation another table view is
//pushed onto the nav controller to allow the user to pick
//the difficulty. The initial value comes from the getter
workoutDifficultyController.title = #"Workout Difficulty";
[workoutDifficultyController setOriginalDifficulty:[self getWorkoutDifficulty]];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[(UINavigationController *)self.parentViewController pushViewController:workoutDifficultyController
animated:YES];
//then in that workoutDifficultyController it calls back into the first controller to set the selected value:
[manageWorkoutController setWorkoutDifficulty:selectedDifficulty];
You've got many issues here. First, you're creating your accessors incorrectly. The problem that's particularly causing you trouble is this line:
workoutDifficulty = [NSString stringWithFormat:#"%d", value];
value is an NSString here. You should be receiving a warning about this. I believe "Typecheck Calls to printf/scanf" is turned on by default, and should catch this. workoutDifficulty is being set to some random number (probably taken from the first 4 bytes of value).
Here is what you probably meant. I would probably switch workoutDifficulty to an enum, but I'm keeping it an NSString for consistency with your code. I'm also doing this without properties because you did, but I would use a property here.
//header file
#implementation ManageWorkoutViewController : UIViewController {
NSString *_workoutDifficulty;
}
-(void)setWorkoutDifficulty:(NSString *)value;
-(NSString *)workoutDifficulty; // NOTE: Name change. "getWorkoutDifficulty" is incorrect.
#end
//implementation file
-(NSString *)workoutDifficulty {
if (nil == workoutDifficulty) {
_workoutDifficulty = [#"Easy" retain];
}
NSLog(#"getter: Returning workoutDifficulty as: %#", _workoutDifficulty);
return _workoutDifficulty;
} //end workoutDifficulty
-(void)setWorkoutDifficulty:(NSString *)value {
[value retain];
[_workoutDifficulty release];
_workoutDifficulty = value;
NSLog(#"setter: workoutDifficulty set as: %#", _workoutDifficulty);
}//end setWorkoutDifficulty
You have to retain workoutDifficulty whenever you set it to a new value (and release the old value).