I was working in Xcode 4.6 and have a basic UItableview set up where the cells are populated from an array. When working in 4.6, the view behaved perfectly and was just how I liked it. After the update to 5.1.1, scrolling seems to have been enabled on the table view, the table view is loaded from the bottom of the view and the 3 populated cells at the top are not visible. Sometimes the view will rubber band and not allow me to scroll all the way back up to the top, and sometimes it won't rubber band and will let me. I am fairly new to this, but have tried messing around with auto layout, but to no avail.
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_Manufacturers = #[#"1",
#"2",
#"3"];
}
- (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 _Manufacturers.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableCell";
TableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
int row = [indexPath row];
cell.TitleLabel.text = _Manufacturers[row];
return cell;
}
The code for UITableView working in Xcode 4.6 should work perfectly in Xcode 5.1.1. Major change I have observed is that table moves under status bar. This can be fixed-up by below snippet :
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
However, provide code so that I can get what's going on.
Related
I have a table view with custom cells (all configured in a subclass using auto layout).
The cells load fine, display fine, everything is fine.
The issue is when I am inserting more rows (at the bottom). The table view is representing a feed for posts, so when the user scrolls to the bottom, before reaching the last cell, I load new posts, and then insert them into the table.
When I do this, I get this weird glitchy effect where the cells randomly come down (behind the previous cells) into place, the table view scrolls up a bit, messy.
CODE AT BOTTOM
I've uploaded a clip of me scrolling. When you see the activity indicator,
I stop scrolling. The rest of the movement is from the glitchy behavior.
Is the reason for the glitch because the cells are being drawn with auto-layout?
I would hope not, but idk..I'm not sure what to do regarding a solution. If anyone has any ideas please let me know.
FYI:
I have this (of course, since the cells are all using auto layout)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
I've tried setting the estimated height to an "average" of the expected cell heights, around 65. No difference.
Update
Here's some code:
HomeViewController.m --> viewDidLoad
...
self.tableView = [KATableView.alloc initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.refreshDelegate = self;
self.tableView.estimatedRowHeight = 75;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.view addSubview:self.tableView];
// Constrains to all 4 sides of self.view
[SSLayerEffects constrainView:self.tableView toAllSidesOfView:self.view];
my table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (!self.dataManager.didFinishFetchingData) return 4;
if (self.contentObjects.count == 0) return 1;
if (self.dataManager.moreToLoad) return self.contentObjects.count + 1;
return self.contentObjects.count + 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MYObject *object = self.contentObjects[indexPath.row];
SomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:object.documentID];
if (!cell) {
cell = [SomeTableViewCell.alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:object.documentID];
cell.delegate = self;
} else [cell startListeningForChanges];
return cell;
}
Here is how I am loading more data and adding it to the table view..
- (void)getHomeFeedData:(nullable void(^)(BOOL finished))completed {
[self.dataManager fetchHomeFeedDataForFeedOption:self.homeNavController.feedFilterOption completion:^(NSError * _Nullable error, NSArray<__kindof KAObject *> * _Nullable feedObjects) {
if (error != nil) {
NSLog(#"something went wrong: %#", error.localizedDescription);
if (completed) completed(NO);
return;
}
NSInteger originalCount = self.contentObjects.count;
if (self.dataManager.isFirstTimeLoading) self.contentObjects = feedObjects.mutableCopy;
else {
if (self.dataManager.isGettingNew) for (MYObject *obj in feedObjects) [self.contentObjects insertObject:obj atIndex:0];
else if (feedObjects.count > 0) [self.contentObjects addObjectsFromArray:feedObjects];
}
if (feedObjects.count > 0) {
if (self.dataManager.isFirstTimeLoading) [self.tableView reloadData];
else {
[self.tableView insertCells:feedObjects forSection:0 startingIndex:self.dataManager.isGettingNew? 0 : originalCount];
}
} else if (self.dataManager.isFirstTimeLoading) [self.tableView reloadData];
if (completed) completed(YES);
}];
}
NOTE:
[self.tableView insertCells:feedObjects forSection:0 startingIndex:self.dataManager.isGettingNew? 0 : originalCount];
is simply this:
- (void)insertCells:(nullable NSArray *)cells forSection:(NSInteger)section startingIndex:(NSInteger)start {
if (!cells) return;
NSMutableArray *indexPaths = #[].mutableCopy;
for (id obj in cells) {
NSInteger index = [cells indexOfObject:obj] + start;
[indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:section]];
}
[self insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
}
Update 2
My UITableViewCell subclass content is hidden ATM (too much difficulty in editing all my post content for the purpose of this post). I just have the subviews of each cell set to alpha = 0.f. It's just an image view, some labels, and some buttons.
No constraint issues in console, cells render perfectly when calling [self.tableView reloadData] so maybe there is something I'm doing wrong when inserting the cells?...
When you dealing with UITableView glitches:
Make sure you call UIKit API's on a main thread - turn on Main Thread checker
In your case, there might be an issue that fetchHomeFeedDataForFeedOption:completion: completion block is called not on a main thread.
Your insert is definitely wrong - all delete/insert/update/move calls for UITableView should be wrapped in beginUpdates/endUpdates
Your "load more" component at the bottom might be an issue. You need to address how it's managing contentSize/contentOffset/contentInset of table view. If it does anything but manipulating contentInset - it does wrong job.
While it's hard without debugging the whole solution, I bet options 2 & 3 are the key problems out there.
I've got a UITableView which is automatically set up for multiple selection in Edit Mode using the following lines in viewDidLoad:
self.tableView.allowsMultipleSelectionDuringEditing = YES;
[self setEditing:YES animated:YES];
However, I'd like to indicate that a row was selected by changing its background color, rather than by the checkmarks which automatically appear along the left of each row. (For example, the ones that appear when editing the email list in the Mail app, or being discussed in this SO question.) I've got it working for the most part, except that I can't get those checkboxes, which are automatically created as part of putting the UITableView into Edit Mode, to go away.
Below is the code I'm working with:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _Hierachy.cellCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *testCell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(testCell == nil) {
testCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
[[testCell textLabel] setText:#"Test Cell"];
return testCell;
}
Those are the only UITableView methods I've got so far, so everything else should be default behavior.
Does anyone know how to hide those checkmarks along the left, in Edit Mode? I've seen many questions about checkmarks in the accessory portion of the cell, but as I understand it, this is a different thing. I've also seen people talk about the tableView:didSelectRowAtIndexPath: method, but these checkmarks are created when the table enters Edit mode and dismissed when the user taps "Done," so that method doesn't seem related.
The closest I've come is finding this method:
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}
But that just prevents the cell's content from indenting to make room for the checkmarks. The checkmarks still appear.
Surely there's a way to hide those checkmarks, and still allow multiple selection in Edit Mode? Or are those checkmarks seriously mandatory behavior for a UITableView in Edit Mode with Multiple Selection enabled?
EDIT: I am (reluctantly) open to answers that are somewhat hack-y, like moving the frame of the checkmarks until it's off the screen. This app is for internal use, and won't need to be approved for the App Store. But given that the checkmarks are created automatically when the UITableView moves into Edit Mode, I don't even know how to get them as objects to alter. Any help would be appreciated!
You'll have to subclass your UITableViewCell and override the (void)setEditing:animated: method like this:
#import "MyCustomCell.h"
#implementation MyCustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)setSelectedBackgroundView:(UIView *)selectedBackgroundView
{
//Cell Selected Color: CLEAR
[super setSelectedBackgroundView:selectedBackgroundView];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
//Cell Edit Mode NO Indent & Selected Color: CLEAR
[super setEditing:NO animated:animated];
[self setNeedsLayout];
}
#end
After you do that, go to Inteface Builder and make your cell part of the class MyCustomCell.
After you make your cell part of MyCustomCell class in IB, import MyCustomCell.h in your UITableViewController and modify the following in your code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *testCell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(testCell == nil) {
testCell = [[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
[[testCell textLabel] setText:#"Test Cell"];
return testCell;
}
UPDATE:
You could also do the following in your TableView's tableView:editingStyleForRowAtIndexPath:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
But you will get your cell indented. To remove that indent you'll have to subclass the Cell.
You should be good to go after doing this! I've just tested it and it works the way you want it!
Here is the most simple solution for multiple selection with no checkmarks:
- (BOOL)tableView:(UITableView*)tableView canEditRowAtIndexPath:(NSIndexPath*)indexPath {
return NO;
}
This will cause the cells to be selected using the default selection style (gray or using your custom selection background), and no checkmarks will appear.
A word on whatever solution you pick. Users expect a consistent experience across multiple applications, and these checkmarks are part of this consistency. Make sure to have a good reason to change a normal OS look and feel.
here is how to achieve:
swipe to delete works
you don't see the checkbox, delete item or anything else on the left of the cell when in editing mode
Cell indentation still works normally (you can turn this off if you want)
(bonus) Support multiple selection
so - in your UITableViewDelegate
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.isEditing)
{
return UITableViewCellEditingStyleNone;
}
else
{
return UITableViewCellEditingStyleDelete;
}
}
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
(do stuff)
}
you also need to configure your UITableView
self.table.allowsMultipleSelectionDuringEditing=NO;
now if you actually do want multiple selection;
self.table.allowsSelectionDuringEditing=YES;
then manage the selected cells yourself.
I put a custom checkbox in my UITableViewCell subclass, and I also change the value in response to
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
The questioner wants to indicate selection with the background colour - that part should be straightforward.
I have a UITableView inside of a View. I've dragged the size of the UITableView to only displays 4 rows at a time. In the ViewController code that loads data into that UITableView, I've generated 6 items to load into the UITableView, because I wanted to verify that it would scroll.
However, when I run the program and preview it in iOS Simulator, it is not scrolling. I have checked that scrolling is enabled (Screenshot).
Here is the code in which the UITableView is populated:
#import "APFacebookFriendsViewController.h"
#interface APFacebookFriendsViewController ()
#end
#implementation APFacebookFriendsViewController
#synthesize facebookFriendsTableView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// set View title
self.title = #"Facebook Friends";
// load Facebook friends
self.facebookFriends = [self getFacebookFriends:#"Test Facebook User"];
}
- (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 [self.facebookFriends count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];
UITableViewCell *cell = [self.facebookFriendsTableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell
NSString *facebookFriend = [self.facebookFriends objectAtIndex:[indexPath row]];
[cell.textLabel setText:facebookFriend];
return cell;
}
- (NSMutableArray *)getFacebookFriends:(NSString *)facebookUser
{
// placeholder data
NSMutableArray *facebookFriendsArray = [NSMutableArray array];
[facebookFriendsArray addObject:#"Friend A"];
[facebookFriendsArray addObject:#"Friend B"];
[facebookFriendsArray addObject:#"Friend C"];
[facebookFriendsArray addObject:#"Friend D"];
[facebookFriendsArray addObject:#"Friend E"];
[facebookFriendsArray addObject:#"Friend F"];
// END placeholder code
return facebookFriendsArray;
}
#end
It's pretty hard to tell what you're doing wrong without looking at your whole project. A few things that you might want to try:
Make sure that the table view is on the top of the other views
Check that table view's userInteractionEnabled is set to true.
Make sure that the table view's delegate and data source points to your view controller.
Try to make the table view full size and see if it's still not scrolling. If so, probably you didn't set one of the table view properties right. You can try to remove and re add the table view.
There are many more things that can go wrong, and as I said it's pretty hard to tell without seeing your project.
1.Check Your table view, and its superviews, has userInteractionEnabled set to TRUE.
2.The custom view that the table view is inside is smaller than the table view with clipsToBounds=NO, a subview that beyond the bounds of its parentview and its not interacted with. so set to clipsToBounds YES.
Insert this into viewDidLoad;
table.delegate=self;
table.datasource=self;
I've got two views, each of which contains a tableview. They are identical in every respect except the height. They are each fed data from the same entity (currently filled with gibberish) in my Core Data store.
One of them works perfectly when scrolled, the other behaves differently. The problem is that the cells scroll past the top section header. Succeeding section titles bump older ones up as expected, and the header title slides up along with the cells.
Here are a couple of screenshots that I hope will illustrate the issue:
First, the one that works properly:
"A" is the top section header, and the cells don't scroll past it.
The next two shots are of the tableview that's behaving weirdly. I've added some arrows to show what's happening:
I've compared the settings in the Attributes Inspector, and unless I've overlooked something, they appear identical.
Anyone have any ideas?
Thanks!
Edit:
Here's the code from the associated UIViewController which is the delegate and the datasource for the TableView:
//
// AvsAViewController.m
// WMDGx
//
// Created by Tim Jones on 2/6/14.
// Copyright (c) 2014 TDJ. All rights reserved.
//
#import "AvsAViewController.h"
#interface AvsAViewController ()
{
NSFetchedResultsController *frc;
}
#end
#implementation AvsAViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self refreshData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[frc sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id<NSFetchedResultsSectionInfo> sectionInfo = [[frc sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
// Customize the appearance of table view cells.
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell to show the activity's name
ListActivity *thisActivity = [frc objectAtIndexPath:indexPath];
cell.textLabel.text = thisActivity.activityName;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"aVSaCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
cell.textLabel.textColor = [UIColor redColor];
NSAttributedString *attString;
attString = cell.textLabel.attributedText;
return cell;
}
// Section Label
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *sectionLabel = [[[frc sections] objectAtIndex:section]name];
return [sectionLabel uppercaseString];
}
-(void) refreshData
{
//This was the turning point for proper MR grouping. The two Properties (activityCategory and activityName) are used as Sort descriptors in the underlying core data methods
frc = [ListActivity MR_fetchAllSortedBy:#"activityCategory,activityName"
ascending:YES withPredicate:nil
groupBy:#"activityCategory"
delegate:nil];
[self.myTableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Make sure "Clip Subviews" is selected for that UITableView in the Property Inspector, and that the table's clipsToBounds is not set to NO somewhere in the code.
I resolved the issue by simply deleting the TableView and dragging in a new one, wiring it up. The problem is therefore fixed, but I have no idea what caused it.
Thanks to #drhr, who commented, and all others who took a look!
Edit:
Just wondering if anyone else has run across this problem. I just had it repeat on another tableview, and had to resolve it as above. FWIW, I'm using Xcode 5, and developing an iPhone app.
I guess fixed is good, but it would be nice to actually know what caused the problem.
I have a table view which display the contacts from array. I setup the table view delegates by follows.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [contactArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ContactCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [contactArray objectAtIndex:indexPath.row];
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60.0;
}
But the first cell in the table view always empty. It starts display only from second cell. I thought it may be header view. So I removed the header using the following delegate methods.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 0.0;
}
But still have the problem. I attached the screenshot about this issue.
Any help will be appreciated.
Your TableView is fine and it is working correctly, this is due to some other problem that is included in iOS 7, that automatically scroll insets. To solve this problem, go to your storyboard and select the viewcontroller in which your TableView is and select the ViewController and select the Properties of that ViewController, and uncheck this checkbox, which is read as Adjust ScrollView Insets. See this screen shot,
Your table is correct.Just your table was auto adjusted by the viewController.
You can write self.automaticallyAdjustsScrollViewInsets = NO;
Your Deduction is wrong your first cell isn't missing, but your tableview has started by 64 points down. So change your frame of your tableview or your tableview constraints accordingly.
Tip : Try setting a background colour when you have to debug things like this to clear your doubts.