UITableView: `tableView:cellForRowAtIndexPath:` not called for all rows - ios

I have created a UITableView with 4 Sections and 3 Rows so totally 12 Rows. But
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
The above method only gets called for 9 times instead of 12 times. Why this happening?
My 4th section is not getting constructed but my 1st section gets duplicated as 4th section.
Please refer my code below
#interface MainViewController : UITableViewController<UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource>
{
}
#end
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
CGRect frameRect = CGRectMake(0,0,320,460);
UITableView *tableView = [[UITableView alloc] initWithFrame:frameRect
style:UITableViewStyleGrouped];
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor purpleColor];
tableView.scrollEnabled = YES;
self.view = tableView;
[tableView release];
[super viewDidLoad];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 3;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 4;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CELL IS NIL %i", indexPath.section);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
if (indexPath.section == 0)
{
if(indexPath.row == 0)
{
cell.text = #"Tmail";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else if ( indexPath.row == 1 )
{
cell.text = #"English";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else
{
cell.text = #"Hindi";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
}
else if (indexPath.section == 1)
{
if(indexPath.row == 0)
{
cell.text = #"Street";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else if ( indexPath.row == 1 )
{
cell.text = #"City";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else
{
cell.text = #"State";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
}
else if (indexPath.section == 2)
{
if(indexPath.row == 0)
{
cell.text = #"Salem";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else if ( indexPath.row == 1 )
{
cell.text = #"Samalpatti";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else
{
cell.text = #"Chennai";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
}
else if (indexPath.section == 3)
{
if(indexPath.row == 0)
{
cell.text = #"NOKIA";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else if ( indexPath.row == 1)
{
cell.text = #"SAMSUNG";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
else
{
cell.text = #"SONY";
UITextField *aField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
[cell addSubview:aField];
[aField release];
}
}
}
return cell;
}

actually when you scroll then other rows are created in case you having more rows,so think you need to write this code like this,
Edited one
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CELL IS NIL %i", indexPath.section);
static NSString *CellIdentifier = #"Cell";
if (cell == nil)
{
CGRect CellFrame = CGRectMake(0, 0, 300, 70);
cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:CellIdentifier] autorelease];
CGRect labLabelFrame = CGRectMake(5, 5, 250, 40);
UILabel *lblTemp;
lblTemp = [[UILabel alloc] initWithFrame:labLabelFrame];
lblTemp.tag = 1;
[cell.contentView addSubview:lblTemp];
[lblTemp release];
CGRect textFieldFrame= CGRectMake(200, 5, 120, 30);
UITextField *txt=[[UITextField alloc] initWithFrame:textFieldFrame];
text.tag=2;
[cell.contentView addSubview:txt];
[txt release];
}
UILabel *cellLabel = (UILabel *)[cell viewWithTag:1];
UITextField *aField=(UITextField *)[cell viewWithTag:2];
aField.text=#"";
aField.placeholder=#"";
if (indexPath.section == 0)
{
if(indexPath.row == 0)
{
cellLabel.text = #"Tmail";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else if ( indexPath.row == 1 )
{
cellLabel.text = #"English";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else
{
cellLabel.text = #"Hindi";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
}
else if (indexPath.section == 1)
{
if(indexPath.row == 0)
{
cellLabel.text = #"Street";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else if ( indexPath.row == 1 )
{
cellLabel.text = #"City";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else
{
cellLabel.text = #"State";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
}
else if (indexPath.section == 2)
{
if(indexPath.row == 0)
{
cellLabel.text = #"Salem";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else if ( indexPath.row == 1 )
{
cellLabel.text = #"Samalpatti";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else
{
cellLabel.text = #"Chennai";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
}
else if (indexPath.section == 3)
{
if(indexPath.row == 0)
{
cellLabel.text = #"NOKIA";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else if ( indexPath.row == 1)
{
cellLabel.text = #"SAMSUNG";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
else
{
cellLabel.text = #"SONY";
aField.placeholder = #"Mandatory";
aField.delegate = self;
aField.textColor = [UIColor blackColor];
}
}
return cell;
}

Problem with your code is:
1) Cell is being reused. but the textfield what you added as subview is not removed!
2) Whenever your cellforRowAtIndex gets called, you are adding textfield each time. But you forgot that you have already added one textfield but haven't removed it.
This is the reason for overlapping.
Solution:
Subclass the UITableViewCell and add textfield in its initWithFrame method and pass the data to that cell. This will solve your problem and also this will reduce your cellForRowAtIndex method length.
Sample code:
CustomCell.h:
#import <UIKit/UIKit.h>
#class contactr;
#interface CustomTableCell : UITableViewCell
{
UITextField *mTextField;
}
#property(nonatomic , retain) UITextField *textField;
#end
CustomCell.m:
#import "CustomTableCell.h"
#synthesize textField = mTextField;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(100,10,200,40)];
self.textField = textField;
[textField release];
[self addSubview:self.textField];
}
return self;
}
- (void)dealloc
{
self.textfield = nil;
[super dealloc];
}
#end
Use this class as your tableViewCell.
And your cellForRowAtIndex must be:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CELL IS NIL %i", indexPath.section);
static NSString *CellIdentifier = #"Cell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textField.placeholder = #"";
cell.textField.text = #"";
if (indexPath.section == 0)
{
if(indexPath.row == 0)
{
cell.text = #"Tmail";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
else if ( indexPath.row == 1 )
{
cell.text = #"English";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
else
{
cell.text = #"Hindi";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
}
else if (indexPath.section == 1)
{
if(indexPath.row == 0)
{
cell.text = #"Street";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
else if ( indexPath.row == 1 )
{
cell.text = #"City";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
else
{
cell.text = #"State";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
}
else if (indexPath.section == 2)
{
if(indexPath.row == 0)
{
cell.text = #"Salem";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
}
else if ( indexPath.row == 1 )
{
cell.text = #"Samalpatti";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
cell.textField.textColor = [UIColor blackColor];
}
else
{
cell.text = #"Chennai";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
cell.textField.textColor = [UIColor blackColor];
}
}
else if (indexPath.section == 3)
{
if(indexPath.row == 0)
{
cell.text = #"NOKIA";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
cell.textField.textColor = [UIColor blackColor];
}
else if ( indexPath.row == 1)
{
cell.text = #"SAMSUNG";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
cell.textField.textColor = [UIColor blackColor];
}
else
{
cell.text = #"SONY";
cell.textField.placeholder = #"Mandatory";
cell.textField.delegate = self;
cell.textField.textColor = [UIColor blackColor];
}
}
return cell;
}

From your initial code, the tableview is behaving normally.
You create cells for the first 3 sections because they are visible and there are no extra cell in the table view which you can reuse.
But, whenever you get scroll to the 4th section (which is not visible at first), you start reusing cells (which happen to be the ones from the 1st section that is not out of the screen).
In order not to lose the data the user has entered in each cell you need to store each UITextField(or at least the text from it) in a separate array. When the table needs a certain cell, you look for the textfield in your array. If it's not there, create and add a new UITextField.
PS: I hope the code doesn't have any typos. I'm at work and I don't have a mac here.
#interface MainViewController : UITableViewController
{
NSMutableArray* textFields;
}
#end
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
CGRect frameRect = CGRectMake(0,0,320,460);
UITableView *tableView = [[UITableView alloc] initWithFrame:frameRect
style:UITableViewStyleGrouped];
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor purpleColor];
tableView.scrollEnabled = YES;
self.view = tableView;
[tableView release];
[super viewDidLoad];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 3;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 4;
}
- (UITextField*)getTextFieldAtIndexPath:(NSIndexPath*)indexPath
{
if (textFields == nil)
textFields = [[NSMutableArray alloc] init];
// Add padding, just in case UITableView doesn't need the cells in order
while (indexPath.section >= [textFields count])
{
[textFields addObject:[NSNull null]];
}
// Get the section list
NSMutableArray* sectionList = [textFields objectAtIndex:indexPath.section];
if (sectionList == [NSNull null])
{
sectionList = [[[NSMutableArray alloc] init] autorelease];
[textFields replaceObjectAtIndex:indexPath.section withObject:sectionList];
}
// Now that we have the section list, we need to do the same for the row
while (indexPath.row >= [sectionList count])
{
[sectionList addObject:[NSNull null]];
}
UITextField* afield = [sectionList objectAtIndex:indexPath.row];
if (afield == [NSNull null])
{
// If there was no afield stored there, just create a new one
afield = [[UITextField alloc] initWithFrame:CGRectMake(100,10,200,40)];
afield.placeholder = #"Mandatory";
afield.delegate = self;
afield.textColor = [UIColor blackColor];
[sectionList replaceObjectAtIndex:indexPath.row withObject:aField];
[afield release];
}
return afield;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CELL IS NIL %i", indexPath.section);
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITextField *aField;
// If we can't reuse the cell, just create a new one
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
else
{
// if we are reusing the cell, just remove the old textField to make room for the new one
[[cell.contentView viewForTag:1234] removeFromSuperview];
}
// Get the text field for this cell. The method below will create a new one if it doesn't exist, or just return the existing one if found.
aField = [self getTextFieldAtIndexPath:indexPath];
// Just add this textfield to the cell
[cell.contentView addSubView:aField];
// Now that we added the textfield, we only need to set the title of the cell. The content of the textField will be cached and moved around.
if (indexPath.section == 0)
{
if(indexPath.row == 0)
cell.text = #"Tmail";
else if ( indexPath.row == 1 )
cell.text = #"English";
else
cell.text = #"Hindi";
}
else if (indexPath.section == 1)
{
if(indexPath.row == 0)
cell.text = #"Street";
else if ( indexPath.row == 1 )
cell.text = #"City";
else
cell.text = #"State";
}
else if (indexPath.section == 2)
{
if(indexPath.row == 0)
cell.text = #"Salem";
else if ( indexPath.row == 1 )
cell.text = #"Samalpatti";
else
cell.text = #"Chennai";
}
else if (indexPath.section == 3)
{
if(indexPath.row == 0)
cell.text = #"NOKIA";
else if ( indexPath.row == 1)
cell.text = #"SAMSUNG";
else
cell.text = #"SONY";
}
}
return cell;
}

Related

iOS: TableView inside TableView cell not scrolling

I have a tableview inside a tableview cell. I am unable scroll that table. I have also attached the screenshot of the table Here is my code:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString
*)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.frame = CGRectMake(0, 0, 300, 50);
self.subMenuTableView = [[UITableView alloc]initWithFrame:CGRectZero style:UITableViewStylePlain];
self.subMenuTableView.tag = 100;
self.subMenuTableView.delegate = self;
self.subMenuTableView.dataSource = self;
self.subMenuTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.subMenuTableView.bounces = NO;
self.subMenuTableView.scrollEnabled = YES;
self.subMenuTableView.alwaysBounceVertical = NO;
[self addSubview:self.subMenuTableView]; // add it cell
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
//UITableView *subMenuTableView =(UITableView *) [self viewWithTag:100];
self.subMenuTableView.frame = CGRectMake(0.2, 0.3, self.bounds.size.width-5, self.bounds.size.height-5);//set the frames for tableview
}

Hiding Tableview section headers on the fly

I am trying to hide and display section headers when there is or isn't data populating the tableview. The code I have now works as intended occasionally, but mostly, one or both of the section headers will remain displayed, (However, if I drag the header off screen when there is no data it will disappear as intended).
This is the code I am using to hide/unhide the sections.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Create custom header titles for desired header spacing
if (section == 0) {
UIView *headerView;
if (self.searchUsers.count || self.searchTracks.count)
{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
}
else{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 0, 0)];
}
HeaderTitleLabel *headerTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 10, 150, 20) headerText:#"USERS"];
[headerView addSubview:headerTitle];
return headerView;
}
else if (section == 1){
UIView *headerView;
if (self.searchUsers.count || self.searchTracks.count)
{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
}
else{
headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 0, 0)];
}
HeaderTitleLabel *headerTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 0, 150, 20) headerText:#"SONGS"];
[headerView addSubview:headerTitle];
return headerView;
}
else
return nil;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
//custom header spacing
if (section == 0) {
if (self.searchUsers.count || self.searchTracks.count)
{
return 40;
}
else
return 0;
}
else if (section == 1) {
if (self.searchUsers.count || self.searchTracks.count)
{
return 50;
}
else
return 0;
}
else
return 0;
}
I check if my array has objects, if not then set the frame's height to 0. This doesn't seem to be working though. Any ideas how I should go about doing this? Thank you.
Your model should drive your table view. Don't just return a 'number of rows' value from your model, but also a 'number of sections' value.
Consider an array that contains two arrays as your model.
You are using wrong approach of table view. Can you clarify what do you exactly want to do. I could help you. Rather then UIView use UITableView Cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.searchUsers.count || self.searchTracks.count)
{
if (indexPath.section ==0) {
static NSString *simpleTableIdentifier = #"CategoryCell";
CategoryCell *cell = (CategoryCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CategoryCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.lbl_CatName.text = [[self.CategoryArray objectAtIndex:indexPath.row] objectForKey:kNAME];
return cell;}
else if (indexPath.section ==1){
static NSString *EarnPointIdentifier = #"EarnPointTableCell";
EarnPointTableCell *EarnPointcell = (EarnPointTableCell *)[tableView dequeueReusableCellWithIdentifier:EarnPointIdentifier];
if (EarnPointcell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"EarnPointTableCell" owner:self options:nil];
EarnPointcell = [nib objectAtIndex:0];
}
return EarnPointcell ;
}
else{
//it will have transparent background and every thing will be transparent
static NSString *RewardIdentifier = #"RewardTableCell";
RewardTableCell *RewardCell = (RewardTableCell *)[tableView dequeueReusableCellWithIdentifier:RewardIdentifier];
if (RewardCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"RewardTableCell" owner:self options:nil];
RewardCell = [nib objectAtIndex:0];
}
return RewardCell ;
}
}
}
It seems to be working fine now. What I did was create boolean values to keep track of my data when my data source is nil. Like so;
[SoundCloudAPI getTracksWithSearch:userInput userID:nil withCompletion:^(NSMutableArray *tracks) {
self.searchTracks = tracks;
[self.tableView beginUpdates];
if (tracks.count) {
self.songsHeaderIsHidden = NO;
}
else
self.songsHeaderIsHidden = YES;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
[[self.tableView headerViewForSection:1] setNeedsLayout];
[[self.tableView headerViewForSection:1] setNeedsDisplay];
[self.tableView endUpdates];
}];
Then set the header accordingly..
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Create custom header titles for desired header spacing
if (section == 0) {
if (self.userHeaderIsHidden) {
return nil;
}
else {
UIView *userHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
HeaderTitleLabel *usersHeaderTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 10, 150, 20) headerText:#"USERS"];
[userHeaderView addSubview:usersHeaderTitle];
return userHeaderView;
}
}
else if (section == 1){
if (self.songsHeaderIsHidden) {
return nil;
}
else {
UIView *songsHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
songsHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
HeaderTitleLabel *songsHeaderTitle = [[HeaderTitleLabel alloc] initWithFrame:CGRectMake(17, 0, 150, 20) headerText:#"SONGS"];
[songsHeaderView addSubview:songsHeaderTitle];
return songsHeaderView;
}
}
else
return nil;
}
Personally, I would use UICollectionView and write your own layout. That way you can explicitly layout where the section headers will be at all times. The best approach is to probably subclass UICollectionViewFlowLayout because you get all the superclass properties for free (such as itemSize, headerReferenceSize, etc.)
Here's an implementation I wrote for having sticky headers, like the Instagram app.
Notice that in prepareLayout, I just calculate the normal positions of every element. Then in layoutAttributesForElementInRect I go through and figure out where to place the header. This is where you can go through and see if the header should be shown or not.
import UIKit
class BoardLayout: UICollectionViewFlowLayout {
private var sectionFrames: [CGRect] = []
private var headerFrames: [CGRect] = []
private var footerFrames: [CGRect] = []
private var layoutAttributes: [UICollectionViewLayoutAttributes] = []
private var contentSize: CGSize = CGSizeZero
override func prepareLayout() {
super.prepareLayout()
self.sectionFrames.removeAll(keepCapacity: false)
self.headerFrames.removeAll(keepCapacity: false)
self.footerFrames.removeAll(keepCapacity: false)
self.layoutAttributes.removeAll(keepCapacity: false)
let sections = self.collectionView?.numberOfSections()
var yOffset: CGFloat = 0.0
for var i: Int = 0; i < sections; i++ {
let indexPath = NSIndexPath(forItem: 0, inSection: i)
var itemSize: CGSize = self.itemSize
if let d = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout {
if let collection = self.collectionView {
if let size = d.collectionView?(collection, layout: self, sizeForItemAtIndexPath: NSIndexPath(forItem: 0, inSection: i)) {
itemSize = size
}
}
}
let headerFrame = CGRect(x: 0, y: yOffset, width: self.headerReferenceSize.width, height: self.headerReferenceSize.height)
self.headerFrames.append(headerFrame)
var headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withIndexPath: indexPath)
headerAttribute.frame = headerFrame
headerAttribute.zIndex = 1000
self.layoutAttributes.append(headerAttribute)
yOffset += self.headerReferenceSize.height - 1 // - 1 so that the bottom border of the header and top border of the cell line up
let sectionFrame = CGRect(x: 0, y: yOffset, width: itemSize.width, height: itemSize.height)
self.sectionFrames.append(sectionFrame)
var sectionAttribute = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
sectionAttribute.frame = sectionFrame
self.layoutAttributes.append(sectionAttribute)
yOffset += itemSize.height
let footerFrame = CGRect(x: 0, y: yOffset, width: self.footerReferenceSize.width, height: self.footerReferenceSize.height)
self.footerFrames.append(footerFrame)
var footerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withIndexPath: indexPath)
footerAttribute.frame = footerFrame
self.layoutAttributes.append(footerAttribute)
yOffset += self.minimumLineSpacing + self.footerReferenceSize.height
}
self.contentSize = CGSize(width: self.collectionView!.width, height: yOffset)
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
var newAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in self.layoutAttributes {
let frame = attributes.frame
if !CGRectIntersectsRect(frame, CGRect(x: 0, y: rect.origin.y, width: rect.size.width, height: rect.size.height)) {
continue
}
let indexPath = attributes.indexPath
let section = indexPath.section
if let kind = attributes.representedElementKind {
if kind == UICollectionElementKindSectionHeader {
var headerFrame = attributes.frame
let footerFrame = self.footerFrames[section]
if CGRectGetMinY(headerFrame) <= self.collectionView!.contentOffset.y {
headerFrame.origin.y = self.collectionView!.contentOffset.y
}
if CGRectGetMinY(headerFrame) >= CGRectGetMinY(footerFrame) {
headerFrame.origin.y = footerFrame.origin.y
}
attributes.frame = headerFrame
self.headerFrames[section] = headerFrame
} else if kind == UICollectionElementKindSectionFooter {
attributes.frame = self.footerFrames[section]
}
} else {
attributes.frame = self.sectionFrames[section]
}
newAttributes.append(attributes)
}
return newAttributes
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
let frame = self.sectionFrames[indexPath.item]
let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
attributes.frame = frame
return attributes
}
override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
let attributes = super.layoutAttributesForSupplementaryViewOfKind(elementKind, atIndexPath: indexPath)
if elementKind == UICollectionElementKindSectionHeader {
attributes.frame = self.headerFrames[indexPath.section]
} else if elementKind == UICollectionElementKindSectionFooter {
attributes.frame = self.footerFrames[indexPath.section]
}
return attributes
}
override func collectionViewContentSize() -> CGSize {
return self.contentSize
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
}
I'd recommend using the UICollectionViewDelegateFlowLayout methods so you can dynamically determine the reference size of the header.

Error when try becomeFirstResponder call for UIMenuController

I have a messenger, so I want to show UIMenuController on cell long tap.
I need image view to catch long press, because I have text and media messages. But I get a crash becomeFirstResponder call after I received long press gesture on iOS 8(iOS 7 works well):
*** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<UICompatibilityInputViewController: 0x13fd75b90> should have parent view controller:<FriendPlay.WLDialogViewController: 0x13fd54d10> but requested parent is:<UIInputWindowController: 0x140815000>'
This is the part of code:
override func handleLongPress(longPress: UILongPressGestureRecognizer!) {
let alpha: CGFloat = 0.6
let point = longPress.locationInView(tableView)
let indexPath = tableView.indexPathForRowAtPoint(point)
if indexPath != nil {
let cell = tableView.cellForRowAtIndexPath(indexPath!) as MessageCell
let convertedPoint = longPress.locationInView(cell.balloonImageView)
if CGRectContainsPoint(cell.balloonImageView.frame, convertedPoint) {
var myView = cell.balloonImageView
if cell.message.type == MessageType.Text {
cell.balloonImageView.alpha = alpha
}
else {
myView = cell.mediaImageView
cell.mediaImageView.alpha = alpha
}
if longPress.state == UIGestureRecognizerState.Began {
showMenuAtPoint(convertedPoint, myView: myView)
}
}
}
}
private func showMenuAtPoint(point: CGPoint, myView: MessageImageView) {
myView.becomeFirstResponder()// <--- that crashes
let rect = CGRectMake(point.x, point.y, 0, 0)
let mc = UIMenuController.sharedMenuController()
mc.setTargetRect(rect, inView: myView)
let sendItem = UIMenuItem(title: "Send", action: "sendAgain:")
mc.menuItems = [sendItem]
mc.setMenuVisible(true, animated: true)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "menuDidHide:", name: UIMenuControllerDidHideMenuNotification, object: nil)
}
func sendAgain(sender: AnyObject) {
println("Send!")
}
For class MessageImageView (inherited from UIImageView) I redefine becomeFirstResponder to return always true.
I initialize my cell in - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.messageMaxWidth = messageMaxWidth;
self.backgroundColor = [UIColor clearColor];
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
self.panGesture.delegate = self;
[self addGestureRecognizer:self.panGesture];
[self setInitialSizes];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleOrientationWillChandeNote:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
}
return self;
}
- (void)setInitialSizes
{
if (self.containerView) {
[self.containerView removeFromSuperview];
}
if (self.timeLabel) {
[self.timeLabel removeFromSuperview];
}
self.userImageView = [[UIImageView alloc] init];
self.userImageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
self.textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.messageMaxWidth, 0)];
self.timeLabel = [[UILabel alloc] init];
self.mediaImageView = [[SOmessageImageView alloc] init];
self.mediaOverlayView = [[UIView alloc] init];
self.balloonImageView = [[SOmessageImageView alloc] init];
self.balloonImageView.userInteractionEnabled = YES;
if (!CGSizeEqualToSize(self.userImageViewSize, CGSizeZero)) {
CGRect frame = self.userImageView.frame;
frame.size = self.userImageViewSize;
self.userImageView.frame = frame;
}
self.userImageView.contentMode = UIViewContentModeScaleAspectFill;
self.userImageView.clipsToBounds = YES;
self.userImageView.backgroundColor = [UIColor clearColor];
self.userImageView.layer.cornerRadius = 5;
if (!CGSizeEqualToSize(self.mediaImageViewSize, CGSizeZero)) {
CGRect frame = self.mediaImageView.frame;
frame.size = self.mediaImageViewSize;
self.mediaImageView.frame = frame;
}
self.mediaImageView.contentMode = UIViewContentModeScaleAspectFill;
self.mediaImageView.clipsToBounds = YES;
self.mediaImageView.backgroundColor = [UIColor clearColor];
self.mediaImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleMediaTapped:)];
[self.mediaImageView addGestureRecognizer:tap];
self.mediaOverlayView.backgroundColor = [UIColor clearColor];
[self.mediaImageView addSubview:self.mediaOverlayView];
self.textView.textColor = [UIColor whiteColor];
self.textView.backgroundColor = [UIColor clearColor];
[self.textView setTextContainerInset:UIEdgeInsetsZero];
self.textView.textContainer.lineFragmentPadding = 0;
[self hideSubViews];
self.containerView = [[UIView alloc] initWithFrame:self.contentView.bounds];
self.containerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
[self.contentView addSubview:self.containerView];
[self.containerView addSubview:self.balloonImageView];
[self.containerView addSubview:self.textView];
[self.containerView addSubview:self.mediaImageView];
[self.containerView addSubview:self.userImageView];
self.contentView.clipsToBounds = NO;
self.clipsToBounds = NO;
self.timeLabel.font = [UIFont fontWithName:#"AvenirNextCyr-Light" size:11];
self.timeLabel.textColor = [UIColor grayColor];
self.timeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
UIImage *backgroundImage = [UIImage imageNamed:#"messagesDateBackground"];
if (backgroundImage) {
self.timeLabel.textColor = [UIColor whiteColor];
if (self.backgroundImageView) {
[self.backgroundImageView removeFromSuperview];
}
self.backgroundImageView = [[UIImageView alloc] initWithImage:[backgroundImage resizableImageWithCapInsets:UIEdgeInsetsMake(20, 20, 20, 20)]];
self.backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[self.contentView addSubview:self.backgroundImageView];
}
[self.contentView addSubview:self.timeLabel];
}
Please give me some suggestions, how to fix this strange bug.
Your view controller probably has a property named inputView that is merely a subview, not an inputView as UIResponder interface expects it to be.
Starting with iOS 8 they check that UIResponder's inputView has no parent.

Scrolling UITableView to top doesn't scroll properly

My UIViewController splits to 2:
UIView
UITableView
I've added a footer view to table in order to hide the rest of the table.
Since I cannot use static cells and also hide all bottom view of the table, I did it a bit tricky.
But the table view didn't scroll to top when I tap on my textfields correctly.
The keyboard hides the UITextField and doesn't scroll to correct point.
How can I fix it?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 6;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier;
if (indexPath.row == 0) identifier = #"cell_name";
else if (indexPath.row == 1) identifier = #"cell_password";
else if (indexPath.row == 2) identifier = #"cell_password_verify";
else if (indexPath.row == 3) identifier = #"cell_email";
else if (indexPath.row == 4) identifier = #"cell_cellphone";
else if (indexPath.row == 5) identifier = #"cell_social";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
return cell;
}
- (void)configureUI
{
UIView *tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), 1)];
tableFooterView.backgroundColor = [UIColor whiteColor];
self.tableView.tableFooterView = tableFooterView;
tableFooterView.backgroundColor = [UIColor whiteColor];
self.tableView.tableFooterView = tableFooterView;
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
}
UPDATE:
Problem was, scroll view cannot scroll because tableFooterView is too short, then I modified my code.
Basically,#Roger Nolan right, I also added the following code and now it works perfect:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview.superview;
[self.tableView scrollToRowAtIndexPath:[self.tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (void)registerObservers
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
CGFloat keyboardHeight = [CoreGraphicsHandler keyboardFramFromNotification:notification].size.height;
UIView *tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), keyboardHeight)];
tableFooterView.backgroundColor = [UIColor whiteColor];
self.tableView.tableFooterView = tableFooterView;
}
- (void)keyboardDidHide:(NSNotification *)notification
{
UIView *tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), 1)];
tableFooterView.backgroundColor = [UIColor whiteColor];
self.tableView.tableFooterView = tableFooterView;
}
You need to make the controller a delegate of your text fields and then send the tableview scrollToRowAtIndexPath:atScrollPosition: animated:
See this exact dupe: Making a UITableView scroll when text field is selected
I had a similar problem and, as suggested by Idan Moshe in the update, I solved inserting a footerView in my tableView.
The solution in Swift 4:
func registerObservers(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide), name: NSNotification.Name.UIKeyboardDidHide, object: nil);
}
#objc func keyboardDidShow(_ notification: Notification) {
DispatchQueue.main.async {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardHeight = keyboardFrame.cgRectValue.height;
let tableFooterView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: keyboardHeight))
self.tableView.tableFooterView = tableFooterView;
}
}
}
#objc func keyboardDidHide(_ notification: Notification) {
DispatchQueue.main.async {
let tableFooterView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 1));
self.tableView.tableFooterView = tableFooterView;
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let cell = self.selectedTExtField?.superview?.superview;
if let idxPath = self.tableView.indexPath(for: cell){
self.tableView.scrollToRow(at: idxPath, at: .top, animated: true);
}
else{
print("Error: index path not found");
}
}

possible to change uitable cell rows without change page or runoithread

i got a service code, and if i save the row should be change color, but doesnt change after i click. But if i change page with segue the next results are available, i used runoithread and reload table data but doesnt work. Any idea? my code ad bottom available
first image : http://i.stack.imgur.com/DHl9A.png
and second image http://i.stack.imgur.com/W8jYj.png
here my getcell and save button at bottom
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
var cellStlye = UITableViewCellStyle.Subtitle;
if (cell == null) {
cell = new UITableViewCell (UITableViewCellStyle.Subtitle, cellIdentifier);
}
cell.TextLabel.Text = tableItemsX [indexPath.Row].MesajBasligi;
cell.DetailTextLabel.Text = tableItemsX [indexPath.Row].CagriAcan;
// cell.Accessory = UITableViewCellAccessory.Checkmark;
if (tableItemsX [indexPath.Row].Durum == true) {
cell.TextLabel.TextColor = UIColor.White;
cell.DetailTextLabel.TextColor = UIColor.White;
cell.BackgroundColor = UIColor.FromRGB (74, 160, 44);
cell.Accessory = UITableViewCellAccessory.Checkmark;
cell.TintColor = UIColor.FromRGB (43, 84, 126);
} else {
cell.BackgroundColor = UIColor.FromRGB (43, 84, 126);
cell.Accessory = UITableViewCellAccessory.DetailButton;
cell.TextLabel.TextColor = UIColor.White;
cell.DetailTextLabel.TextColor = UIColor.White;
cell.TintColor = UIColor.FromRGB (74, 160, 44);
}
return cell;
}
up.Process.Clicked += delegate {
new System.Threading.Thread (new System.Threading.ThreadStart (() => {
InvokeOnMainThread (() => {
bool state;
if (up.durumT.On)
state = true;
else
state = false;
var responseX = up.client.Post (new RequestUpdate {
Durum = state,
Gorulme = true,
TamamlanmaVakti = DateTime.Now,
YapilanIs = up.pesrsonelRaporu.Text,
CagriIlgilenen = up.sicil,
Id = conv
});
up.RequestListTable.ReloadData ();
});
})).Start ();
};

Resources