I have a little bit problem with -[__NSArrayM objectAtIndex:]: index 6 beyond bounds [0 .. 4]' How can I show (null) If I don't have data.
Here's code
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if ([[bookList objectAtIndex:indexPath.item]quiz_url]==[[bookList objectAtIndex:6]quiz_url]) {// This one is problem, I have `objectAtIndex:` only 0 - 5 .
// quiz_all = [[bookList objectAtIndex:indexPath.item]quiz_url];
_getQuiz6 = [[bookList objectAtIndex:6]quiz_url];
NSLog(#" test quiz6 = %#",_getQuiz6); //It's possible? Can I print `(null)` if no data.
}
...
and
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [bookList count];
}
I don't have data [[bookList objectAtIndex:6]quiz_url] on webservice.
There's plenty of ways you could go around this. One way to deal with it, is using "placeholder" objects to pad the content, for example, NSNull. Or you could modify your Book object and add a property like isLoaded.
Whatever method you choose, then (sticking with the NSNull);
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// Whatever code you have
Book *book = [bookList objectAtIndex:indexPath.item];
// NSNull is a singleton, you can safely do pointer comparison
if (book == [NSNull null])
{
// Show loading or something in the cell
cell.loadingOrSomething = YES;
// Return the cell here
return cell;
}
// book wasn't NSNull, then proceed with the rest of stuff
NSURL *url = [book quiz_url];
cell.loadingOrSomething = NO;
return cell;
}
You can use a category if you want. Somewhere in a header include this:
#interface NSArray (custom)
-(id)quizURLAtIndex:(int)index;
#end
and in a .m file you add this:
#implementation NSArray (custom)
-(id)quizURLAtIndex:(int)index{
if(index >= self.count) return nil;
else return [[self objectAtIndex:index] quiz_url];
}
#end
And from now on, whenever you want, you can call [bookList quizURLAtIndex:indexPath.item] instead of [[bookList objectAtIndex:indexPath.row] quiz_url]
Related
I'm having an issue with running two TableViews in the one ViewController.
EDIT:
To make the question a bit more understandable, I've placed the snippet below of the code.
Things that may be relevant:
Both use custom cells.
From what I have done with the debugger, it looks at though it doesn't pass through the following if statement:
if (tableView == self.tblFriendsList){...}
3.Initially I was trying to use a Containment View to add the second UITableViewController, but now I have opted to try using a UITableView.
The containment view (which includes the two tableViews) comes from a NIB.
I have tried both adding the delegate and datasource manually, and through the file's owner.
I have changed the code so that if it doesn't execute the if statement, then it will execute what is required for the other table (which is setting up successfully), but previously it was also working for the first table when I used the example that I have written below. It is the other table that fails to load when both when used as a tableView or when I try adding an entire ViewController to a subview (I've been interchanging between both to try to solve this).
Spelling has been checked on the actual code many-a-time, so I'm more thinking that it may have to do with the fact that it isn't entering the if statement for the one mentioned above.
Most examples I have seen from videos/Stack Exchange have mainly been prior to this year, so I am wondering if there has been an update to practical coding that I have missed.
Sadly I am only doing this for a job for someone wanting an exact replica of their design, or I would have thought of a nicer alternative!
I have attempted all answers so far, but have come up short... although I have seen too many tutorials on this that have succeeded, so I am very unsure about something funny going on...
EDIT AGAIN 11. Even when setting the tableView tag and running an if statement, the debugger still shows the code skipping past the if statement. Even when the reload data method is called, the debugger does not have another break when it should.
Currently, the code sits as this:
For viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.m_arrVenueList=[[NSMutableArray alloc]init];
self.m_dictCells = [[NSMutableDictionary alloc] init];
[self setNavigationProperties];
[self loadFriendByLocation];
[self setRefreshController];
self.m_dictFriendListCells = [[NSMutableDictionary alloc] init];
self.tblFriendLocation.delegate = self;
self.tblFriendLocation.dataSource = self;
self.tblFriendsList.delegate = self;
self.tblFriendsList.dataSource = self;
[self.tblFriendsList reloadData];
}
For numberOfRowsInSection:
Note: Just one section for each.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tblFriendsList) {
return [self.arrFriendsList count];
}
// Return the number of rows in the section.
return [self.m_arrVenueList count];
}
For cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.tblFriendsList) {
NSDictionary *friends=[self.arrFriendsList objectAtIndex:indexPath.row];
NSString *friendID = [Utility formattedValue:[friends objectForKey:#"user_id"]];
// Configure the cell using friend object...
NSString *sIdentifier = [NSString stringWithFormat:#"TableCell_%ld",(long)friendID];
FriendListVenueCell *cell = (FriendListVenueCell *)[self.m_dictCells objectForKey:sIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (cell == nil)
{
cell = (FriendListVenueCell*)[Utility getViewFromXib: #"FriendListVenueCell" classname:[FriendListVenueCell class] owner:tableView];
if (indexPath.row%2 != 0)
{
cell.contentView.backgroundColor = UIColorWithRGB(11,21,39);
}
else
{
cell.contentView.backgroundColor = UIColorWithRGB(12,23,32);
}
}
[cell setCellDataForFriend:friends];
[self.m_dictCells setObject:cell forKey:sIdentifier];
return cell;
}
// Configure the cell using friend object...
NSString *sIdentifier = [NSString stringWithFormat:#"TableCell_%ld",(long)indexPath.row];
FriendsAtVenueCell *cell = (FriendsAtVenueCell*)[self.m_dictCells objectForKey:sIdentifier];
if (cell == nil)
{
cell = (FriendsAtVenueCell *)[Utility getViewFromXib:#"FriendsAtVenueCell" classname:[FriendsAtVenueCell class] owner:tableView];
cell.m_FriendViewContrller = self;
[self.m_dictCells setObject:cell forKey:sIdentifier];
if (indexPath.row%2 != 0)
{
cell.contentView.backgroundColor = UIColorWithRGB(11,21,39);
}
else
{
cell.contentView.backgroundColor = UIColorWithRGB(12,23,32);
}
}
cell.delegate=self;
Venue *tempDic=(Venue*)[self.m_arrVenueList objectAtIndex:indexPath.row];
[cell setCellData:tempDic];
if (tempDic.nVenueId == 0)
{
[cell.m_imgLogo setHidden:YES];
[cell.m_imgLogobg setHidden:YES];
[cell.m_btnFollow setHidden:YES];
}
cell.btnFifthImg.tag = indexPath.row;
cell.m_btnFollow.tag = tempDic.nVenueId;
cell.m_RowId= indexPath.row;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
So far, what I have attempted to do - from what I have read from StackExchange and seen on tutorials - is to have the method cellForRowAtIndexPath have separate cases for each cell and then return that cell.
Examples include
if (tableView == oneTableViewOutlet) {
//load cell
return cell;
} else if (tableView == anotherTableViewOutlet) {
//load cell
return cell;
}
Hopefully this hasn't made the question too long, I am trying to condense it!
EDIT FINISHED
I've watched it with success on a few videos where they have added the delegate and datasource to self for each respective TableView outlet and it has worked from there, but I am having the issue of one outlet loading and not the other.
The above image has me debugging the issue, where I have Stepped Through the execution of the function and it has successfully initiated the first case, but then returns from the function and does not execute for the other TableView.
The code is tricky as I have custom cases in it, but if there are any other thoughts on debugs or references to help that would be appreciated.
In summary:
2 TableViews
Both hooked up to delegate and datasource
2 Custom NIB Cells used for each table
Only one executing
Add
NSLog(#"Table:%#", tableView) at Line 204.
See if it prints two different tableViews.
and Try replacing Line 205 with if([tableView isEqual:self.tblFriendLocation])
Set the tag for your tableview and compare with tag
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TablevIewCell1 *cell1=[tableView dequeueReusableCellWithIdentifier:#"CellId1"];
TablevIewCell2 *cell2=[tableView dequeueReusableCellWithIdentifier:#"CellId2"];
switch (tableView.tag) {
case 1:
[cell1.textLabel1 setText:[self.arrForTableView1 objectAtIndex:indexPath.row]];
return cell1;
break;
case 2:
[cell2.textLabel2 setText:[self.arrForTableView2 objectAtIndex:indexPath.row]];
return cell2;
break;
default:
break;
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (tableView ==self.tableView1)
return self.arrForTableView1.count;
else
return self.arrForTableView2.count;
}
or else
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TablevIewCell1 *cell1=[tableView dequeueReusableCellWithIdentifier:#"CellId1"];
TablevIewCell2 *cell2=[tableView dequeueReusableCellWithIdentifier:#"CellId2"];
if ([tableView isEqual:self.tableView1]) {
[cell1.textLabel1 setText:[self.arrForTableView1 objectAtIndex:indexPath.row]];
return cell1;
} if ([tableView isEqual:self.tableView2]){
[cell2.textLabel2 setText:[self.arrForTableView2 objectAtIndex:indexPath.row]];
return cell2;
}
return nil;
}
Hope this will help you.
if([tableview isEqual:tblName])
First of all this is how you must check equality. Secondly post all methods so that one can see exactly what is wrong with your code.
I was using this example for searching through a UICollectionView: https://github.com/ihomam/CollectionViewWithSearchBar/blob/master/collevtionViewWithSearchBar/CollectionViewController.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
if (self.searchBarActive) {
cell.laName.text = self.dataSourceForSearchResult[indexPath.row];
} else {
cell.laName.text = self.dataSource[indexPath.row];
}
return cell;
}
...only my app is setup a little differently:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PlaceCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
Place *p = [_entries objectAtIndex:indexPath.item];
if (self.searchBarActive) {
cell.placeName.text = self.dataSourceForSearchResult[indexPath.item];
} else {
cell.placeName.text = p.PName;
cell.placeImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:p.PImage]]];
}
return cell;
}
So if I just run it the way it is then self.dataSourceForSearchResult[indexpath.item] will return EVERYTHING and I get an error because it's not returning a string...
'Can't use in/contains operator with collection <Place: 0x17d8ad70> (not a collection)'
but what I want it to do is search through the p.PName results. Something like:
self.dataSourceForSearchResult[p.PName indexpath.item]
Having looked through the github code I think that the core of your problem lies somewhere else.
The problem lies here :
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"self contains[c] %#", searchText];
self.dataSourceForSearchResult = [self.dataSource filteredArrayUsingPredicate:resultPredicate];
}
What this method does is it filters matching items from self.datasource (which I assume you renamed to self.entries, and I hope you also did it here) based on the predicate. The predicate is a little tricky to understand, especially if you have no previous experience with them. Let's break it down :
self - in this context it means the object that will be checked against the condition. This object will be an element of the array.
contains[c] - this is the condition. The self will be checked if it contains something. The [c] means that the check will be case insensitive.
%# - this is the 'something'. It will be replaced with searchText.
This worked in the example because the array contained NSString objects and NSPredicate knows how to check if they contain something (a substring). Since your array consists of Place objects the NSPredicate doesn't know how to check if they contain something (a subplace? :P).
To fix this replace the predicate with this one self.PName contains[c] %#. This one will acces the PName property of each object of array and check whether it contains the substring.
You can read more on NSPredicate on NSHipster and in the docs
As to the second problem, self.dataSourceForSearchResult[indexpath.item] will not in fact return an NSString but a Place object, though not EVERYTHING, but one of the filtered ones. It was actually never reached in your app, as it crashed earlier. No offence, but I think that you don't entirely understand the whole order of things in this code. To break it down :
The view controller is shown and the searchbar is empty so the cells are drawn, based on data in self.datasource (or self.entries as you renamed it). collectionView:cellForItemAtIndexPath: is being called.
User inputs something in the search bar. (this triggers searchBar:textDidChange:).
The matching objects are filtered into self.dataSourceForSearchResult array.
The collection view gets reloaded.
The cells are redrawn (collectionView:cellForItemAtIndexPath: is being called again), but since the searchbar is not empty, we now use only objects from self.dataSourceForSearchResult.
So to make it work your - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; should look like this :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PlaceCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
Place *p;
// retrieve item from appropriate array
if (self.searchBarActive) {
p = self.dataSourceForSearchResult[indexPath.item];
} else {
p = self.entries[indexPath.item];
}
// populate the cell - we do this regardless of whether the searchbar is active or not
cell.placeName.text = p.PName;
cell.placeImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:p.PImage]]];
return cell;
}
I’m trying to add in a new cell into my collection view, only if it has more than one item already in it. I have no worked much with collection views, and research in the docs and this site had not helped solve this issue yet. So, on my cellForItemAtIndexPath method, I do a check to see if it is populated. If not, I add the cell, like so:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (self.myArray.count != 0) {
return self.myArray.count + 1;
}
else {
return self.myArray.count;
}
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyNormalCollectionViewCellS *cells = (MyNormalCollectionViewCells *) [collectionView dequeueReusableCellWithReuseIdentifier:#"MyNormalCollectionViewCells” forIndexPath:indexPath];
cell.clipsToBounds = NO;
DataClass *data = [self.myArray objectAtIndex:indexPath.row];
[cells configureMyNormalCellsWith:data];
if (0 < self.myArray.count) {
UICollectionViewCell *deleteCell = [UICollectionViewCell new];
deleteCell.backgroundColor = [UIColor yellowColor];
NSArray *newData = [[NSArray alloc] initWithObjects:deleteCell, nil];
[self.myArray addObjectsFromArray:newData];
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
[self.myCollectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
return deleteCell;
}
return cell;
}
For some reason, I have an assertion being thrown, stating:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of items in section 0. The number of items contained in an
existing section after the update (7) must be equal to the number of
items contained in that section before the update (6), plus or minus
the number of items inserted or deleted from that section (0 inserted,
0 deleted) and plus or minus the number of items moved into or out of
that section (0 moved in, 0 moved out).'
Of course, the number usually varies, but it's always angry about that extra cell. Everything is fine up until I try and add it in. Now, being unfamiliar with collection views and after browsing related questions upon this site, I decided it was time to ask the pros.
Does anyone know how I should change this code in order to accomplish what I'm trying to do?
Don't modify data source in collectionView:cellForItemAtIndexPath:. Return different number of items in - collectionView:numberOfItemsInSection: instead:
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
if (self.dataArray.count > 0) {
return self.dataArray.count + 1;
}
else {
return 0;
}
}
In collectionView:cellForItemAtIndexPath: you should return your "normal" cell for "normal" item, and "extra" cell for that extra one, depending on the value of indexPath.row. For example:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < self.dataArray.count) { // if indexPath.row is within data array bounds
// dequeue, setup and return "normal" cell
} else { // this is your "+1" cell
// dequeue, setup and return "extra" cell
}
}
Please can anyone help me to achieve something like this-
(developing for iPad iOS7) In UIViewController i have UITableView & UICollectionView(like splitViewController).I just want to change image in UICollectionView when i tap on UITableViewCell.(I am using button over tableViewCell and collecting index in NSInteger).
Please help me to achieve this,I am trying from so many days,but can't achieve till now. :(
Here is the code what i have done till now.
In my viewDidLoad:
self.collectionViewImages=[[NSMutableArray alloc]initWithObjects:#"1.jpg",#"2.jpg",#"3.jpg",nil] ;
Then in CollectionViewMethods:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [_collectionViewImages count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell"
forIndexPath:indexPath];
NSString *collectionViewImageName=[self.collectionViewImages objectAtIndex:indexPath.row];
UIImageView * collectionImage = (UIImageView*)[myCell viewWithTag:11];
collectionImage.image =[UIImage imageNamed:collectionViewImageName];
return myCell;
}
Then on ButtonClick(ofTableViewCell)--
(i am collecting tag of each button in NSInteger and using that property in numberOfItemsInSection like this,
-(void)btnTapped:(id)sender{
int tag= [(UIButton *)sender tag];
NSLog(#"Button Pressed%d",tag);
_buttonIndex=tag;
//_buttonIndex is NSInteger property
//If i add object in NSMutableArray and call reloadData my UI hangs here only.
//If I empty my NSMutableArray and add new objects in it and then call reloadData, I am getting NSRangeException.
}
And now i am using this property in numberOfItemsInSection as...
if (_buttonIndex==1){
[collectionImage setImage:[UIImage imageNamed:[_array1 objectAtIndex:indexPath.row]]];
//My _array1 has 4 objects and my collectionViewImages array has 7 objects.
}
i am getting error message as
*** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]'
Its crashing because you are trying to get an item from the array at index 4. But there are only indexes: 0,1,2,3.
Double check the contents of array1 on line (if it crashes here):
[collectionImage setImage:[UIImage imageNamed:[_array1 objectAtIndex:indexPath.row]]];
Otherwise find the other place where you call objectAtIndexand check if it crashes there.
I am trying to change UICollectionViewCell data on button click(in iPad)?
How to achieve this?
How to send different array count to numberOfItemsInSection on button Click?
In my viewDidLoad:
self.collectionViewImages=[[NSMutableArray alloc]initWithObjects:#"1.jpg",#"2.jpg",#"3.jpg",nil] ;
Then in CollectionViewMethods:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [_collectionViewImages count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell"
forIndexPath:indexPath];
NSString *collectionViewImageName=[self.collectionViewImages objectAtIndex:indexPath.row];
UIImageView * collectionImage = (UIImageView*)[myCell viewWithTag:11];
collectionImage.image =[UIImage imageNamed:collectionViewImageName];
return myCell;
}
Then on ButtonClick--
(As there are 4-5 buttons i am collecting tag of each button in NSInteger and using that property in numberOfItemsInSection like this,
-(void)btnTapped:(id)sender{
int tag= [(UIButton *)sender tag];
NSLog(#"Button Pressed%d",tag);
_buttonIndex=tag;
//_buttonIndex is NSInteger property
//If i add object in NSMutableArray and call reloadData my UI hangs here only.
//If I empty my NSMutableArray and add new objects in it and then call reloadData, I am getting NSRangeException.
}
And now i am using this property in numberOfItemsInSection as...
if (_buttonIndex==1){
[collectionImage setImage:[UIImage imageNamed:[_array1 objectAtIndex:indexPath.row]]];
//My _array1 has 4 objects and my collectionViewImages array has 7 objects.
}
i am getting error message as
*** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]'
well you keep two array with a BOOL and based on taps you relload the UicollectionView first keep two array
Declare BOOL
BOOL isChangeData;
Ex :
self.collectionViewImages=[[NSMutableArray alloc]initWithObjects:#"1.jpg",#"2.jpg",#"3.jpg",nil] ;
self.collectionViewImages1=[[NSMutableArray alloc]initWithObjects:#"4.jpg",#"5.jpg",#"6.jpg",nil] ;
isChangeData = TRUE;
First time set original Data as Datasource
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
if(isChangeData){
isChangeData = false;
return [_collectionViewImages1 count];
}
else{
isChangeData = TRUE;
return [_collectionViewImages count];
}
}
in button action Make BOOL true and reload UICOllectionView
-(IBAction)changeData:(id)sender {
[self.CollectionView reloadData];
}
Hope this Works if i did any mistake pls correct me.
I believe you're just simply asking how to add/remove data from a UICollectionView through an IBAction. You would just add an object to your UICollectionView's datasource and reload. Here is an example of adding something:
-(IBAction)addObject:(id)sender {
[self.objectArray addObject:#"new object"];
[self.myCollectionView reloadData];
}