I have RSS, and the UITableView loads data from it, how can I show UIAlertView if the UITableView is empty, but not by count of an array because the array is different every time it is refreshed, is there some kind of function which will check if UITableView is empty after it completes drawing?
You can query the UITableView itself. So assuming you have a single section you could do the following ...
// Reload the tableview
[tableView reloadData];
// Test the number of rows in the first section
if ([tableView numberofRowsInSection:0] == 0) {
// Display UIAlertView here
}
EDIT; Based on the comments below ...
In your header file (.h) iVar declarations ...
int feedCount;
int feedsParsed;
In your implementation ...
- (void)refresh {
feedCount = 0;
feedsParsed = 0;
[feedParser stopParsing];
self.title = #"Refreshing...";
[parsedItems removeAllObjects];
for (NSString *imePredmeta in [Data variables].mojiPredmeti) {
... // I've removed these lines for brevity but they are still required
[feedParser parse];
feedCount += 1;
}
// Delete everything else after this line
}
- (void)feedParserDidFinish:(MWFeedParser *)parser {
feedsParsed += 1;
if (feedsParsed == feedCount) {
[self.tableView reloadData];
if ([self.tableView numberofRowsInSection:0] == 0) {
// Fade out tableview and display alert here as we now know for
// sure that there are no more feeds to parse and we definitely
// have nothing to display
}
}
}
You will need to loop round the array and check to determine if there are no results.
Please post your existing tableview code if you want a more explicit answer.
edit: micpringle answer above is better.
Related
I have one NSMutableArray that has number of records (suppose 52).
for (int i=0;i<=52;i++) {
[arrSavedCalculation addObject:[NSString stringWithFormat:#"Bianca lb,Red %d",i]];
}
I want to give a paging Prev and Next button at bottom of tableview. and each page display 6 record.How can i do this. i also see this document but not success
http://www.ke-cai.net/2011/04/pagination-with-uitableview-in-ios.html
Try with these. Tested and found working. Can be improved.
#interface ViewController (){
//pageNumber will hold the current page index
NSInteger _pageNumber;
//keeping the max page number for ease of calculation
NSUInteger _maxPageNumber;
//the batch size
NSUInteger _numberOfVisibleRows;
}
#end
#implementation ViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Main data array is formed
NSUInteger rows = 0;
NSMutableArray *tempArray = [NSMutableArray array];
while (rows<52) {
[tempArray addObject:[NSString stringWithFormat:#"Item %d",rows+1]];
rows++;
}
self.mainArray = [NSArray arrayWithArray:tempArray];
_pageNumber = 0;
_numberOfVisibleRows = 5;
_maxPageNumber = [self.mainArray count]/_numberOfVisibleRows;
self.subArray = [self subArrayForPageNumber:_pageNumber];
}
- (NSArray *)subArrayForPageNumber:(NSUInteger)pageNumber{
NSRange range = NSMakeRange(_pageNumber*_numberOfVisibleRows, _numberOfVisibleRows);
if (range.location+range.length>[self.mainArray count]) {
range.length = [self.mainArray count]-range.location;
}
return [self.mainArray subarrayWithRange:range];
}
- (IBAction)buttonPressed:(UIBarButtonItem *)button{
//Same method is used for calculating the page numbers
if (button.tag ==1) {
_pageNumber= MIN(_maxPageNumber, _pageNumber+1);
}else{
_pageNumber = MAX(0, _pageNumber-1);
}
self.subArray = [self subArrayForPageNumber:_pageNumber];
[self.tableView reloadData];
}
Source Code
you should take two array.
1) Your MAIN ARRAY containing all of objects.
2) A Temporary array contain only 6 objects.
While loading tableview use that Temporary Array.
keep a page counter which will count your current page.
According to your 52 objects you can have 52/6 = 9 page but last page contain only 4 object.
like.
#define kNumberOfObjectInOnePage 6
set self.page=0 in "viewDidLoad" //self.page is the page counter.
-(void)nextPage:(id)sender{
self.page++;
take 6 objects from main array into temp array
reload your table view.
}
-(void)previous:(id)sender{
self.page--;
take 6 previous object into temp array
reload your table view.
}
A simple solution is if you are accessing records from web service then ask to him provide the total number of records in initial hits as well as first 6 records to display. If you want to do paging you should keep page 0 or 1 at initial hit to service and as you got 6 records on first page and you find that total records which are given from service is more than the 6 then you have to show previous and next button otherwise no need of these button.
As you accessing the records you have to put into in an NSArray or in Coredata. When you press next buttonyou have to increase the page count and fetch next 6 records and save to DB or array and reload the table.
if you press previous button you have to remove last 6 object from array or you can delete or check some kind last 6 records to display and reload table.
Hope this helps.
I created a UITableView that contains custom section header views. Now, I want it to display a bit more data on the uppermost current visible section. I plan to use the event scrollViewDidEndDecelerating to update the section headers. Currently, the problem is that I cannot set the section header height for a specific section number.
I did try using heightForHeaderInSection beforehand, but the app just crashes with the following output:
'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
I was using the code:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (tableView == self.tableView)
{
NSArray *visibleCells = [self.tableView visibleCells];
NSMutableArray *visibleSections = [[NSMutableArray alloc] init];
for (NSInteger index = 0; index < [visibleCells count]; index++)
{
UITableViewCell *currentCell = [visibleCells objectAtIndex:index];
NSIndexPath *currentPath = (NSIndexPath *)[self.tableView indexPathForCell:currentCell];
if (![visibleSections containsObject:[NSNumber numberWithInt:currentPath.section]])
{
[visibleSections addObject:[NSNumber numberWithInt:currentPath.section]];
NSLog([NSString stringWithFormat:#"%ld", (long)[visibleSections count]]);
[visibleSections sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:nil ascending:YES]]];
}
}
if (visibleSections == nil)
{
return 42.0;
}
else if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
else
{
return 42.0;
}
}
}
I couldn't quite work out what went wrong in my heightForHeaderInSection method, but I knew it had something to do with the NSMutableArray, visibleSections.
Any hints or answers as to how I can go about changing the height for a specific section header view outside of heightForHeaderInSection and/or how I can fix my code above would be really helpful.
Edit:
Just to make the solution to my crashing problem a bit clearer, if (visibleSections == nil) should not be used in place of if ([visibleSections count] < 1) or if ([visibleSections count] == 0).
I think you could also do it like this, if you want the first section header to be taller when the table first appears (topSection is an NSInteger property):
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.topSection = ((NSIndexPath *)[self.tableView indexPathsForVisibleRows][0]).section;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.topSection] withRowAnimation:NO];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (self.topSection == section)
{
return 58.0;
}
else
{
return 42.0;
}
}
OK so it turns out this is a harder problem than it first seems. The best I have come up with so far is
Don't treat the header that is for the top section any different and populate them all with the extra data.
You can show and hide different parts by being clever with positioning the "additional" items so that they will be outside of the parent view's bounds when it is smaller and making the parent view clipToBounds.
Failing that you can make a custom UIView subclass and do some manipulation in layoutSubviews
The end implementation I was settling on was this
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
self.topSection = [indexPaths count] ? [indexPaths[0] section] : -1;
if (indexPaths.count > 1) {
self.topSection = [indexPaths[1] section];
}
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
{
if (section <= self.topSection) {
return 60;
} else {
return 20;
}
}
It's by no means perfect but it looked semi reasonable and could be tweaked.
Things to note:
You may need to assess if there is too much work going on in scrollViewDidScroll: but it didn't appear to cause any lag for me (I've not really tested properly)
I set the top section using the second indexPath if available as it looked slightly more pleasing/less clunky
I use section <= self.topSection because the header's before are all of screen so there is no point in reducing the size of them which causes really clunky animation.
So after trying this you may need to dig deeper or want to rethink your design a little
You cannot directly refer to the arrays first object by calling objectAtIndex:0, you gotta stay defensive so change this:
else if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
To
else if([visibleSections count]>0)
{
if ([[visibleSections objectAtIndex:0] integerValue] == section)
{
return 58.0;
}
}
Try changing this line:
NSMutableArray *visibleSections = [[NSMutableArray alloc] init]
to:
NSMutableArray *visibleSections = [NSMutableArray array];
Which initializes the array.
I am using grouped tableview for developing a contact list using database. I have to show the message "No Contacts" on tableview when there is no contact in list. how can I do it?
Share your ideas..
Thanks in Advance
supposing that you are using an array to store all the contacts then use the following delegate
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// You can also modify this condition according to a specific section
if([YOUR_ARRAY count] == 0)
{
return 1;
}
else
return [YOUR_ARRAY count];
}
Now adding data to table in following delegate
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialise your cell
if([YOUR_ARRAY count] > 0){
// add your array data to cells
}
if([YOUR_ARRAY count] == 0){
// this means no contacts in array and therfore you have only one cell to display NO CONTACTS
}
return cell;
}
For cases like this one we used table headers.
If the table had elements in his data source, the table header was clear and had a 1px height.
If the data source had no elements, then the table header view was set as big as the table's frame and contained a message, an image or whatever you might need.
The functions (table view delegate methods, actually) we used were height for header in section and view for header in section. We verified the data source inside the viewForHeader function
You can achieve the same effect using the table footers as well
you can add UILabel
ande set the text of the label
label.text = #"No results ";
and you make a test
if ([contacts count] == 0)
{
yourTableview.hidden = YES;
yourLabel.hidden = NO;
}
else
{
yourTableview.hidden = NO;
yourLabel.hidden = YES;
}`
I'm developing an application with a picker view that slide from the bottom (like an action sheet). I need to convert 3 units (litres, us gallons and imperial gallons). The value is taken from a text field but I don't know how to tell the compiler which was the unit to start from.
(ie...the user insert the value in the the text field but if he changes the units (usg or ig), the value in the text field changes accordingly.
this is the array:
NSMutableArray *weightArray = [[NSMutableArray alloc] initWithObjects:#"", #"Ltr",#"Usg",#"Ig",
nil];
self.weightPickerViewArray = weightArray;
[weightArray release];`
this is the picker:
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (row == 1) {
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}else if (row == 2){
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}else if (row == 3){
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}
how can I tell the compiler which was the value before the selection?
Add a text indicating the units at the end of the string.
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (row == 1) {
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}else if (row == 2){
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}else if (row == 3){
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}
When you select a row in picker, it call's its delegate method pickerView:didSelectRow:inComponent:
Here, you can can change the value of your text field depending on which row (liters, gallons, etc) user selected.
Please refer the apple's class reference for UIPickerView delegate
If I understand your problem. You want catch event when user change units in your pickerView, pass the information to the view that contains text field and execute conversion.
So you can use NSNotificationCenter (documentation here). In your pickerView when value change put this code :
[[NSNotificationCenter defaultCenter] postNotificationName:#"unitValueChange" object:#"litres"];
In the view that contains UITextField put this code :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMethodToExecute:) name:#"unitValueChange" object:self];
Now when your PickerView launch notification your view launch this method :
-(void)myMethodToExecute:(NSNotification *)notif{
NSString *myUnitSelected = notif.object
}
In the Contacts app's add/edit view, if you hit the 'Return' key in a field, you cycle through each of the UITextFields. These fields seem to be inside a UITableViewCell. What's a good way to do this?
When I have a series of UITextFields not inside a Table, I can invoke
[self.view viewWithTag:tag] to get a list of the views and the use that to cycle through them. But within a table, I only get one view and I'm not sure how to convert this over.
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
// Find the next entry field
for (UIView *view in [self entryFields]) {
if (view.tag == (textField.tag + 1)) {
[view becomeFirstResponder];
break;
}
}
return NO;
}
/*
Returns an array of all data entry fields in the view.
Fields are ordered by tag, and only fields with tag > 0 are included.
Returned fields are guaranteed to be a subclass of UIResponder.
From: http://iphoneincubator.com/blog/tag/uitextfield
*/
- (NSArray *)entryFields {
if (!entryFields) {
self.entryFields = [[NSMutableArray alloc] init];
NSInteger tag = 1;
UIView *aView;
while (aView = [self.view viewWithTag:tag]) {
if (aView && [[aView class] isSubclassOfClass:[UIResponder class]]) {
[entryFields addObject:aView];
}
tag++;
}
}
return entryFields;
}
Just discovered this while browsing for something myself - sorry for the delay.
If you haven't resolved the issue, you could create an array of uitextfield objects in the same order presented on screen. As you create the table, set the .tag property of the text field. Within the text field delegate method - read the tag, increment by 1 (to move to the next field), and issue the becomefirstresponder call.
You could also look at the textFieldDidEndEditing delegate method.