I know this question has been asked again and again. This post summarized some common causes, but none applies to me:
Every answer I've seen when searching has been a variation of: 1) The tableView is nil 2) numberOfRowsInSection is 0 3) tableView's delegate/data source not set 4) calling reloadTable on the wrong uiTableView.
The answer to that post was the tableView was not displayed before another call to reloadData, which is not my case either. My actual code is a bit lengthy, so I would just paste the parts that I think is related. Feel free to ask me to paste more. Note that competitorsTable has been added to the view in the story board.
#interface CartItemViewController : TrackedUIViewController <UITableViewDataSource, UITableViewDelegate>
//...
#end
#interface CartItemViewController ()
//...
#property (weak, nonatomic) IBOutlet UITableView *competitorsTable;
#end
#implementation CartItemViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
NSAssert(self.competitorsTable, #"Competitor table should not be nil");
self.competitorsTable.dataSource = self;
self.competitorsTable.delegate = self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self updateCompetitors];
}
- (void)updateCompetitors
{
MBProgressHUD *indicator = [MBProgressHUD showHUDAddedTo:self.hostView animated:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (![self.product isLoaded]) {
[NSThread sleepForTimeInterval:1];
}
[self.product loadCompetitorsPriceForConditionValue:self.conditionValue];
NSDictionary *competitors = self.product.competitors[#(self.conditionValue)];
dispatch_async(dispatch_get_main_queue(), ^{
if (competitors) {
if (competitors.count > 1) {
self.hostView.hidden = NO;
self.hostView.hostedGraph = [[CompetitorGraph alloc]initWithFrame:self.hostView.bounds Competitors:competitors];
NSAssert(!self.competitorsTable.hidden, #"Competitor table should not be hidden");
[self.competitorsTable reloadData];
} else {
self.hostView.hidden = YES;
}
} else {
self.hostView.hostedGraph = nil;
}
[indicator hide:YES];
});
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSDictionary *competitors = self.product.competitors[#(self.conditionValue)];
if (competitors.count == 0) {
NSLog(#"WARNING: %s returning 0", __PRETTY_FUNCTION__);
} else {
NSLog(#"Number of rows: %d", competitors.count);
}
return [self.product.competitors[#(self.conditionValue)] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Sort names according to its price
NSDictionary *competitors = self.product.competitors[#(self.conditionValue)];
NSArray *names = [competitors.allKeys sortedArrayUsingComparator:^NSComparisonResult(id name1, id name2) {
if ([competitors[name1] floatValue] > [competitors[name2] floatValue]) {
return (NSComparisonResult)NSOrderedAscending;
} else if ([competitors[name1] floatValue] < [competitors[name2] floatValue]) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"vendor"];
NSString *vendorName = names[indexPath.row];
cell.textLabel.text = vendorName;
cell.detailTextLabel.text = competitors[vendorName];
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"Competitors' Offer";
}
#end
When the view is opened, there are two calls to numberOfRowsInSection, one returns 0, which is normal because the competitors information hasn't been loaded, the other returns a number greater than 0. But cellForRowAtIndexPath is called in none of the cases.
After hours of debugging, I finally found the problem: I didn't set the struts and springs right, making the table be squeezed to 0 height. Because it is not shown, cellForRowAtIndexPath was not called
Related
I am encountering an issue with my UITableView.
The animations for the delete swipe gesture does not work properly.
The thing is, in the new template project "Master Detailed" it works well. But not in the project I am currently working in.
I had an other issue before with the animations that was not working after finishing animate the first time. I fixed it by replacing this in my code.
/// New code
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key || !obj) {
return;
}
[self gl_setObject:obj forKeyedSubscript:key];
}
/// Old code
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key) {
return;
}
if (!obj) {
obj = [NSNull null];
}
[self gl_setObject:obj forKeyedSubscript:key];
}
And this is the code of the current TableView that is working in the Xcode base project but not in mine.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface TempViewController: UIViewController
#end
NS_ASSUME_NONNULL_END
#import "TempViewController.h"
#import "TempTableViewCell.h"
#interface TempViewController () <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property NSMutableArray *objects;
#end
#implementation TempViewController
- (void)viewDidLoad {
[super viewDidLoad];
// TableView
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.rowHeight = 80;
[self.tableView registerNib:[UINib nibWithNibName:#"TempTableViewCell" bundle:nil] forCellReuseIdentifier:#"TempTableViewCell"];
// Add button to add element when deleting too much
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
// Add some data to make the bug work
for (int i = 0; i < 100; i++) {
[self insertNewObject:0];
}
}
- (void)insertNewObject:(id)sender {
if (!self.objects) {
self.objects = [[NSMutableArray alloc] init];
}
[self.objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TempTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TempTableViewCell" forIndexPath:indexPath];
NSDate *object = self.objects[indexPath.row];
cell.tempLabel.text = [object description];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.objects removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
#end
So, I am wondering guys if you have any clue about where to look at? Or already experienced this issue before?
Thank you for your future help!!
Happy building!
Well,
I went deeper in the direction on our swizzling methods.
I removed this code completely. We were using it to avoid inserting nil value in dictionary. Without it, the bug disappeared. I am not sure why, but if I found the answer someday, I will update this post.
Hope it can save some time for some people!
#import "NSDictionary+NilSafe.h"
#import <objc/runtime.h>
#implementation NSObject (Swizzling)
+ (BOOL)gl_swizzleMethod:(SEL)origSel withMethod:(SEL)altSel {
Method origMethod = class_getInstanceMethod(self, origSel);
Method altMethod = class_getInstanceMethod(self, altSel);
if (!origMethod || !altMethod) {
return NO;
}
class_addMethod(self,
origSel,
class_getMethodImplementation(self, origSel),
method_getTypeEncoding(origMethod));
class_addMethod(self,
altSel,
class_getMethodImplementation(self, altSel),
method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getInstanceMethod(self, origSel),
class_getInstanceMethod(self, altSel));
return YES;
}
+ (BOOL)gl_swizzleClassMethod:(SEL)origSel withMethod:(SEL)altSel {
return [object_getClass((id)self) gl_swizzleMethod:origSel withMethod:altSel];
}
#end
#implementation NSDictionary (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self gl_swizzleMethod:#selector(initWithObjects:forKeys:count:) withMethod:#selector(gl_initWithObjects:forKeys:count:)];
[self gl_swizzleClassMethod:#selector(dictionaryWithObjects:forKeys:count:) withMethod:#selector(gl_dictionaryWithObjects:forKeys:count:)];
});
}
+ (instancetype)gl_dictionaryWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt {
id safeObjects[cnt];
id safeKeys[cnt];
NSUInteger j = 0;
for (NSUInteger i = 0; i < cnt; i++) {
id key = keys[i];
id obj = objects[i];
if (!key) {
continue;
}
if (!obj) {
obj = [NSNull null];
}
safeKeys[j] = key;
safeObjects[j] = obj;
j++;
}
return [self gl_dictionaryWithObjects:safeObjects forKeys:safeKeys count:j];
}
- (instancetype)gl_initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt {
id safeObjects[cnt];
id safeKeys[cnt];
NSUInteger j = 0;
for (NSUInteger i = 0; i < cnt; i++) {
id key = keys[i];
id obj = objects[i];
if (!key) {
continue;
}
if (!obj) {
obj = [NSNull null];
}
safeKeys[j] = key;
safeObjects[j] = obj;
j++;
}
return [self gl_initWithObjects:safeObjects forKeys:safeKeys count:j];
}
#end
#implementation NSMutableDictionary (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = NSClassFromString(#"__NSDictionaryM");
[class gl_swizzleMethod:#selector(setObject:forKey:) withMethod:#selector(gl_setObject:forKey:)];
[class gl_swizzleMethod:#selector(setObject:forKeyedSubscript:) withMethod:#selector(gl_setObject:forKeyedSubscript:)];
});
}
- (void)gl_setObject:(id)anObject forKey:(id<NSCopying>)aKey {
if (!aKey) {
return;
}
if (!anObject) {
anObject = [NSNull null];
}
[self gl_setObject:anObject forKey:aKey];
}
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key) {
return;
}
if (!obj) {
obj = [NSNull null];
}
[self gl_setObject:obj forKeyedSubscript:key];
}
#end
#implementation NSNull (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self gl_swizzleMethod:#selector(methodSignatureForSelector:) withMethod:#selector(gl_methodSignatureForSelector:)];
[self gl_swizzleMethod:#selector(forwardInvocation:) withMethod:#selector(gl_forwardInvocation:)];
});
}
- (NSMethodSignature *)gl_methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig = [self gl_methodSignatureForSelector:aSelector];
if (sig) {
return sig;
}
return [NSMethodSignature signatureWithObjCTypes:#encode(void)];
}
- (void)gl_forwardInvocation:(NSInvocation *)anInvocation {
NSUInteger returnLength = [[anInvocation methodSignature] methodReturnLength];
if (!returnLength) {
// nothing to do
return;
}
// set return value to all zero bits
char buffer[returnLength];
memset(buffer, 0, returnLength);
[anInvocation setReturnValue:buffer];
}
#end
EDIT 1: Well, at the end, the problem was this nil check of obj
if (!key || !obj) {
return;
}
/// Replaced by
if (!key) {
return;
}
I kept the rest of the code, it was producing a crash in case of inserting trying to insert nil value in a NSDictionary.
how to reload the data in the table while scrolling?
If i have a url "http:.......&start=0&limit=50".
How to reload the data in table with incrementing the start and limit value with 50.
Can anyone find a solution for this?
You need to implement the load more functionality in the table view for this.In order to do so, you need to track the table view's index while it is scrolling once table reaches the last cell you can call the api with increased page number and add the new records to the array once you get the positive response from the api.
See the below code for more understanding.
Variable initialization
#interface ExampleListVC (){
BOOL hasNextPage,isLoadingNextPage;
int currentPage;
NSMutableArray *arrTableData;
}
#end
In viewDidLoad method
arrTableData = [[NSMutableArray alloc]init];
currentPage=-1;
hasNextPage = YES;
In tableView numberOfRowsInSection method
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int count = (int)arrTableData.count;
if (hasNextPage)
{
count++;
}
return count;
}
In cellForRowAtIndexPath method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
if (indexPath.row<arrTableData.count)
{
//Load TableviewCell you intend to show
}
else
{
//Show loading cell
cell = [self loadingCell:indexPath];
if (!isLoadingNextPage)
{
[self fetchData];
}
}
return cell;
}
Loading Cell Code
-(UITableViewCell *) loadingCell :(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:nil];
cell.backgroundColor = [UIColor clearColor];
UIActivityIndicatorView *activityIndicatorView =[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicatorView.color = [UIColor blackColor];
activityIndicatorView.center = CGPointMake(self.tblView.center.x, cell.center.y);
[cell.contentView addSubview:activityIndicatorView];
[activityIndicatorView startAnimating];
cell.userInteractionEnabled = NO;
return cell;
}
Api call implementation
-(void)fetchData
{
isLoadingNextPage = YES;
if (currentPage==-1)
currentPage=0;
//API Call
{
DLog(#"API Call Response = %#",response);
isLoadingNextPage = NO;
if (response == nil)
{
hasNextPage=NO;
return;
}
if (success)
{
if (hasNextPage)
{
currentPage++;
}
[arrTableData addObjectsFromArray:response];
}
else
{
hasNextPage=NO;
}
//Reload tableview
}];
}
Alternative solution for this is
Use the SVPullToRefresh library in integrate infinite scrolling of it.
https://github.com/samvermette/SVPullToRefresh
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(self.comptabl.contentOffset.y >= (self.comptabl.contentSize.height - self.comptabl.bounds.size.height))
{
if(isPageRefreshing==NO){
isPageRefreshing=YES;
[appDelegate showIndicator:nil view1:self.view];
start=start+50;
[self makeRequest:start];
[self.comptabl reloadData];
NSLog(#"called %d",start);
}
}
}
I am using this method but at all time the value is increment with 50 ..how can i set limit to the value
Problem:
I am trying to create a custom UITextField class "UIValidatedTextField" which allows one to set certain rules as to whether input is valid. For example, you can set a regex parameter to ensure that input is of a specific format, i.e. a password, email address, etc...
Another ability of this is to specify and set a parameter that references another UITextField and ensures that the input matches the input from that other UITextField.
The issue I am having here, is that I am setting this reference to another UITextField. However, when I access its "text" field I find that there is nothing in the text field even when I type something into it.
I have provided related code below:
#import "UIRegisterViewController.h"
#import "UIRegisterViewCell.h"
#import "UIValidatedTextField.h"
#import "NSConstants.h"
#interface UIRegisterViewController ()
#end
#implementation UIRegisterViewController
- (void)viewDidLoad {
[super viewDidLoad];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableItems = #[#"name", #"email", #"netId", #"username", #"password", #"confirmPassword"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_tableItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = [_tableItems objectAtIndex:indexPath.row];
UIRegisterViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if ([cellIdentifier isEqualToString:#"email"]) {
[cell.textField setRegex:VALID_DUKE_EMAIL_REGEX];
} else if ([cellIdentifier isEqualToString:#"netId"]) {
//Validation?
} else if ([cellIdentifier isEqualToString:#"username"]) {
//Validation?
//THIS IS THE CELL THAT I WANT TO COMPARE INPUT TO
} else if ([cellIdentifier isEqualToString:#"password"]) {
[cell.textField setRegex:VALID_PASSWORD_REGEX];
//SETTING THE TEXT FIELD IN QUESTION HERE...
} else if ([cellIdentifier isEqualToString:#"confirmPassword"]) {
[cell.textField setRegex:VALID_PASSWORD_REGEX];
NSIndexPath *index = [NSIndexPath indexPathForRow:4 inSection:0];
UIRegisterViewCell *confirm =(UIRegisterViewCell *)[self tableView:_tableView cellForRowAtIndexPath:index];
[cell.textField setConfirm:confirm.textField];
}
cell.textField.delegate = self;
return cell;
}
#pragma mark - Text Field Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
#end
Note that the textFields are UIValidatedTextFields - a custom class provided below:
#import "UIValidatedTextField.h"
#import "NSArgumentValidator.h"
#implementation UIValidatedTextField
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialize];
}
return self;
}
- (id)initialize {
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification object:self];
[self validate]; //Validate in case editing began before observer was set.
}
return self;
}
- (void) setOptional:(BOOL)isOptional {
_isOptional = isOptional;
}
- (BOOL) isOptional {
return _isOptional;
}
- (void) setRegex:(NSString *)regex {
_regex = regex;
}
//SET THE TEXT FIELD TO COMPARE INPUT AGAINST HERE.
- (void) setConfirm:(UITextField *)confirm {
_confirm = confirm;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification object:_confirm];
[self validate]; //Validate in case editing on confirm began before observer was set.
}
- (void) setQuery:(NSString *)query {
_query = query;
}
- (void) textFieldDidChange:(NSNotification *)notification {
NSLog(#"UPDATE");
_isValid = [self validate];
[self showInputValidation];
}
- (BOOL) validateRegex {
if (_regex.length == 0) {
return true;
}
return [NSArgumentValidator isValid:self.text withRegex:_regex];
}
- (BOOL) validateConfirm {
// NSLog(#"%# : %#", [_confirm text], self.text);
if (_confirm == NULL) {
//NSLog(#"IS NULL");
return true;
}
return [self.text isEqualToString:_confirm.text];
}
- (BOOL) validateQuery {
return true;
}
- (BOOL) validate {
_isValid = (self.text == 0 && _isOptional) || ((self.text != 0) && [self validateRegex] && [self validateConfirm] && [self validateQuery]);
return _isValid;
}
//IF ANYONE HAS A SOLUTION AS TO HOW TO MAKE CHANGING BORDER COLOR CHANGE THE COLOR ALONG THE ROUNDED BORDER THAT IS PRESENT AS OPPOSED TO A RECTANGULAR BORDER LET ME KNOW.
- (void) showInputValidation {
self.layer.borderWidth = 1.0;
if (self.text.length == 0) {
self.layer.borderColor = [[UIColor blackColor] CGColor];
} else if (_isValid) {
self.layer.borderColor = [[UIColor greenColor] CGColor];
} else {
self.layer.borderColor = [[UIColor redColor] CGColor];
}
}
- (void) finalize {
[super finalize];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UITextFieldTextDidChangeNotification object:self];
if (_confirm != NULL) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UITextFieldTextDidChangeNotification object:_confirm];
}
}
#end
Thanks for the help!
One glaring bug in this code is that you're setting the regex in cellForRowAtIndexPath:, even though all cell are reusing the same cell object. cellForRowAtIndexPath: should be used only to set cell content, like text and color. Instead, create an IBOutlet to the validating text fields and add their regexes in viewDidLoad. Better yet, scrap the custom subclass entirely and instead run your regex validation whenever one of the relevant text fields fires off an event when editing is finished.
I am trying to add Search to Todo demo application. I have below code so far with Search working. The issue with below code is that the Pagination doesn't work anymore. There is simlilar question about pagination but when "number of rows in section + 1" returned, the app crashes with [__NSArrayM objectAtIndex:] error. How do I get the Pagination working?
// MyTableController.h
#import <Parse/Parse.h>
#interface MyTableController : PFQueryTableViewController
#end
// MyTableController.m
#import "MyTableController.h"
#interface MyTableController() <UISearchDisplayDelegate> {
}
#property (nonatomic, strong) UISearchBar *searchBar;
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation MyTableController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
self.className = #"Todo";
self.keyToDisplay = #"text";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = YES;
self.objectsPerPage = 5;
}
return self;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = self.searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar
contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
CGPoint offset = CGPointMake(0, self.searchBar.frame.size.height);
self.tableView.contentOffset = offset;
self.searchResults = [NSMutableArray array];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName: self.className];
[query whereKey:#"text" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count ;
}
}
#pragma mark - Parse
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
}
- (void)objectsWillLoad {
[super objectsWillLoad];
}
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.className];
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByAscending:#"priority"];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [object objectForKey:#"text"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Priority: %#", [object objectForKey:#"priority"]];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
#end
In your own implementation of numberOfRowsInSection you must return numberOfRows + 1. But then you must write your code considering you have more cells than query returning. Check out your heightForRowAtIndexPath and others methods: there can be calls to [self.objects objectAtIndex:self.objects.count]. I mean, if indexPath.row == self.objects.count, its a "Load More" cell.
If you overriding willSelectRowAtIndexPath or didSelectRowAtIndexPath, you must do this in the top of method
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
or add this (previous case is prefered)
if (indexPath.row == self.objects.count)
{
[self loadNextPage];
}
Customization of Load More cell placed in -(PFTableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath
And im using
-(PFTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath
instead of
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
I am trying to load my dropbox files into a UITableView but they are not showing up. I did every step from registering my app on dropbox.com and implementing the session delegate in my app. Here is my code can anyone tell me whats wrong with it. Im pretty sure i declared everything correctly but i cant seem to find the problem. Also i know its not a connection issue because i added an NSLog and it logs the files.
#import <UIKit/UIKit.h>
#import <DropboxSDK/DropboxSDK.h>
#interface testViewController : UITableViewController <DBRestClientDelegate>
{
DBRestClient *restClient;
NSMutableArray *dropboxURLs;
}
#end
#import "testViewController.h"
#interface testViewController ()
#end
#implementation testViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (DBRestClient *)restClient {
if (!restClient) {
restClient =
[[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
restClient.delegate = self;
}
return restClient;
}
- (void)viewDidLoad
{
[super viewDidLoad];
dropboxURLs = [[NSMutableArray alloc] init];
[[self restClient] loadMetadata:#"/"];
}
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if (metadata.isDirectory) {
for (DBMetadata *file in metadata.contents) {
if (!file.isDirectory)
{
NSLog(#"%#", file.filename);
[dropboxURLs addObject:file.filename];
}
}
}
}
- (void)restClient:(DBRestClient *)client
loadMetadataFailedWithError:(NSError *)error {
NSLog(#"Error loading metadata: %#", error);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return dropboxURLs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"FileCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [dropboxURLs objectAtIndex:indexPath.row];
return cell;
}
#end
Reload the table view after you add things to the dropboxURLs array.
[self.tableView reloadData];
Try something like this immediately after your addObject call:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
-(void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
if (metadata.isDirectory) {
for (DBMetadata *file in metadata.contents) {
if (!file.isDirectory)
{
NSLog(#"%#", file.filename);
[dropboxURLs addObject:file.filename];
}
}
}
[self. tableView reloadData];
}
Don't forget to set delegate and datasource of UITableView to self.
we have to load data from DropBoxRoot
After MetaData is loaded ,reload the tableview
(void)viewDidLoad{
[super viewDidLoad];
dropboxURLs = [[NSMutableArray alloc] init];
[self loadData];
}
-(void) loadData{
if ([DBSession sharedSession].root == kDBRootDropbox) {
photosRoot = #"/";//can specify any folder like /Photos,/Public etc
}
[self.restClient loadMetadata:photosRoot withHash:photosHash];
}
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
photosHash = metadata.hash;
NSArray* validExtensions = [NSArray arrayWithObjects:#"jpg", #"jpeg",#"png",#"txt",#"pdf",#"doc",#"mp3",#"mp4", nil];
for (DBMetadata* child in metadata.contents) {
NSString* extension = [[child.path pathExtension] lowercaseString];
if (!child.isDirectory && [validExtensions indexOfObject:extension] != NSNotFound) {
[directory addObject:child.filename];
}else{
[allFileAndDirectory addObject:child.filename];
}
[myTableView reloadData];
}