After a quick search, I couldn't find answer to my problem.
The answer to this question: How to reformat a custom UITableViewCell
didn't work for me.
When my tableView gets into edit mode, the cell's subViews are indented to the right and goes off cell view. Please see attached screen shots.
I need either to reduce the indentation so that they are shifted but do not go beyond the cell. Or, when they enter edit mode, the cell's subViews are shifted a bit towards the left so that they remain within the cell.
I have designed these UITableViewCell in storyboard. I altered the indentation Level and Width in storyboard but they had no effect.
Update: Added screenshot after answer from NaCl
Normal Mode:
Edit Mode:
Change after answer from NaCl
Use this
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
Simple way is in your custom cell class - in layoutSubviews method adjust your cell.view.frame
if (self.isEditing) {
// then shift cell frame to left side
}
else {
// then shift cell frame to right side
}
Your issue is likely stemming from your view hierarchy. The view's contentView automatically resizes for changes in the indentation. All your views should be subviews of contentView to resolve this issue.
Related
I've been struggling with this now for a little while, and I want to make sure I'm doing this the right way. For reference, I am using AutoLayout with Storyboards, and it's an iOS7-only app.
I have a UITableView with a header-view, and the UI for the HeaderView is hooked up in the storyboard. I've left the header in the storyboard, and it contains a few elements, one of which is a non-scrollable UITextView.
Here is a screenshot of how it basically looks in the storyboard:
I darkened the header-view's background to a darker grey so it stands out.
The UITextView's text can be dynamic, so its height needs to change depending on the size of the text. But the tricky part is that the table's header-view needs to adjust its height as well, depending on this text's size. I've tried a few different things with constraints, but it's not really working correctly (unless I disable AutoLayout and re-size things programatically, which I really would like to avoid).
I want this to be as clean as possible with as little code as necessary, of course. I am not tied to using the table-view header, although it works well with my needs, since I want it and its containing textview to scroll with the actual details below. But that all being said, if there is a more simple approach to accomplishing this (i.e. with a uilabel), I will gladly change the underlying structure.
Any thoughts as to an approach? Not looking for someone to hold my hand through an answer; just looking mainly for some good starting points if possible. Thanks in advance!
Some time ago I found new solution, maybe it needs tweaking for animations and is experimental, but, for some range of cases it's fine, so give it a try:
Add a headerView to a UITableView.
Add a subview to headerView, let's call it wrapper.
Make wrapper's height be adjusted with it's subviews (via Auto
Layout).
When autolayout had finished layout, set headerView's height to
wrapper's height. (see -updateTableViewHeaderViewHeight)
Re-set headerView. (see -resetTableViewHeaderView)
Here's some code:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self updateTableViewHeaderViewHeight];
}
/**
tableView's tableViewHeaderView contains wrapper view, which height is evaluated
with Auto Layout. Here I use evaluated height and update tableView's
tableViewHeaderView's frame.
New height for tableViewHeaderView is applied not without magic, that's why
I call -resetTableViewHeaderView.
And again, this doesn't work due to some internals of UITableView,
so -resetTableViewHeaderView call is scheduled in the main run loop.
*/
- (void)updateTableViewHeaderViewHeight
{
// get height of the wrapper and apply it to a header
CGRect Frame = self.tableView.tableHeaderView.frame;
Frame.size.height = self.tableHeaderViewWrapper.frame.size.height;
self.tableView.tableHeaderView.frame = Frame;
// this magic applies the above changes
// note, that if you won't schedule this call to the next run loop iteration
// you'll get and error
// so, I guess that's not a clean solution and could be wrapped in #try#catch block
[self performSelector:#selector(resetTableViewHeaderView) withObject:self afterDelay:0];
}
// yeah, guess there's something special in the setter
- (void)resetTableViewHeaderView
{
// whew, this could be animated!
[UIView beginAnimations:#"tableHeaderView" context:nil];
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
[UIView commitAnimations];
}
All this works seamlessly after the initial autolayout pass. Later, if you change wrapper's contents so that it gains different height, it wont work for some reason (guess laying out UILabel requires several autolayout passes or something). I solved this with scheduling setNeedsLayout for the ViewController's view in the next run loop iteration.
I've created sample project for that: TableHeaderView+Autolayout.
I would suggest setting the height programatically.
You can calculate the hight of the textview with boundingRectWithSize:options:context
Then you just set the frame of the tableViewHeader.
As said above, in UITableView the height for the header is not controlled by auto layout but by
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
Footer height is set by
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
And finally cell height is set by
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
The tableview loads the cells from storyboards, XIB files or code. It uses the above methods to set the size of the frame for the headers, footers and cells, and sizes their views to fit in that space.
It's important to understand that auto layout will not change those sizes, it only handles the rules used to layout the views inside their containers.
If your cells/headers/footers are using static sizes, then it's very easy. You just lock the heights of all your subviews, set up your cell to be the same size you return in heightFor... and everything looks how it should.
Dynamic cells/headers/footers take a bit more work.
Essentially you're asked to tell the tableview the size of your cell/header/footer BEFORE you have actually made it and sized out all the subviews.
You can quickly test this out by setting a breakpoint at the cellForRowAtIndexPath and the heightForRowAtIndexPath methods, the height is called first.
For dynamic tableviewcell heights, a common trick is to create a single cell in viewDidLoad and save it for calculating the heights. Then when you reach heightForRowAtIndexPath or heightForHeaderInSection use the pre made cell to put in the values you will be using in the final cell, for UILabels use sizeWithFont (pre iOS 7) or boundingRectWithSize (iOS 7.0+), and sizeThatFits for UITextViews. (For anybody reading this in the future, check that those methods have not been replaced by something newer)
So get the text you want into the dummy cell, get the heights of the labels and textviews, add the spaces between them and return the final size when you're done.
Then in cellForRowAtIndexPath or headerForSection redo the setting of values but on the actual cell that will show.
If your height calculations match your auto layout settings, then everything will fit perfectly in the space that's made for it.
If you want to change the height of a cell or header after it's already drawn, then you'll want to update the tableview by doing something like:
[self.tableView beginUpdates];
// change the data that will cause cell height to be different in heightForCellAtIndexPath or which will change a header or footer height calculation
[self.tableView endUpdates];
This will cause the tableview to check the sizes it has and only update the cells or header/footer if changed.
A table view header view is given to a table view via the tableView:viewForHeaderInSection: delegate method.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// create your header view here (read the docs on this method, there are some specific requirements)...
}
I don't know if it's possible to design the header view in storyboard; I guess you'd have to use an individual XIB file or do so programmatically.
In other words, I think you're headed towards creating constraints in code if you need to provide table view header views.
On the other hand, maybe you don't really need a table view header view. Maybe, what you're after is just a subview above the table view that doesn't move when the user scrolls the table. To do that, you need to re-work your view hierarchy in IB. Right now your so-called header view is inside the table view. You don't want that.
Or, since it looks like you're using static cells, you could make the top static cell take on the appearance of your so-called header view.
is there any possible way to set a UIImageView as a background for a section (let's say section #3) in a grouped UITableView?
I'm not asking about viewForHeaderInSection 'coz I tried it but didn't work as expected.
thanks so much in advance ...
My guess is that if you were to think very creatively, you could come up with a way to essentially swap the UITableView's backgroundView property as different sections are visible. This doesn't strike me as particularly elegant given that multiple sections might occupy the visible screen at one time, but perhaps I don't understand the question clearly.
The short answer is that there is not a defined/simple way to achieve this. The composition of a section is far removed from the background of a UITableView, and essentially the architecture isn't setup for what you want. I wouldn't assume to know the details of your implementation, but I would also urge caution: The visual and architectural characteristics of UITableView's are pretty well-considered. My personal opinion (again, I haven't seen what you're working on), is that different section backgrounds might overwhelm the user experience in many cases. I can also see cases where it may be a nice UI touch if executed properly.
In this case, if you still want to do it, here is the approach I'd take:
Essentially you're going to watch the position of your tableview's cells. You could do this in scrollViewDidScroll, or tableViewWillDisplay cell - or other places, I'm sure. But you need to know which cells are scroll on and off the screen, and you then need to be able to ask the upper-most-visible cell what it's section is. Once you've established which section it is you should currently be displaying, you can use that to scroll your own set of views representing each section's background.
Essentially, you're going to create each of your dynamic section background views in code, just UIView's, each with it's backgroundProperty set to a repeating pattern (obviously, heights will be dynamic). Add all your section background subviews (or preferably do it lazily) to your UITableView's backgroundView.
Now, as your scroll view scrolls, you're going to observe which rows and sections are coming in and out of the table's view. As rows in sections are scrolled, calculate the height of the section background (multiply the quantity of rows in the section by their heights) and adjust it's Y axis, which is sitting in but clipped by your tableView's backgroundView. As sections scroll on, you'll update the Y offset of the relevant section's background view. Phew!
Another idea might be to toss all your section background views, laid out vertically, in a UIScrollView, user interaction disabled. Place that scroll view in your table view's backgroundView, and then figure out the math to essentially "forward" scroll events from the tableview's scrolling view to your background scrolling view.
This will probably take a bit of work to find an implementation that keeps your animations all smooth and in sync, but, it's an approach that I think could be made to work.
Use myTable.tableHeaderView = customHeaderView if your table has only one section.
viewForHeaderInSection might not have worked if you didn't return an object of type UIView or a subclass in your method. Add some code from your viewForHeaderInSection method to your question for a more precise answer.
EDIT- If by viewForHeaderInSection didn't work as expected, you mean that the header was clipped, then it might be because you have not have implemented the following method:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
If not implemented, each section's height is set to an iOS default value.
create a custom headerView that looks something like this.
.h
#interface CustomViewForHeader: UIView
#end
.m
#import "CustomViewForHeader.h"
#implementation CustomViewForHeader
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
UIImageView *bgImage = [[UIImageView alloc]initWithFrame:self.frame];
bgImage.image = [UIImage imageNamed:#"bill_headerBg.png"];
[self addSubview:bgImage];
}
return self;
}
#end
use it like:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 50.0;
}
-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
CustomViewForHeader *customView = [[CustomView alloc]initWithFrame:CGRectMake(0,0,320,50)]
return customView;
}
I recall something about methods to find the rectangles of each section. Having those, you could appropriate sized images to the scroll view underlying the table view.
Aside from that, you could put the background image into the background of the cells themselves. When the tableview calls for a cell in a given section, you can pick the appropriate image or image tile.
I want to change the appearance of an UITableviewCell in edit mode like it is shown in the address book from apple. The cell should resize and i will add UITextFields as subviews.
I know that to change appearance of a cell you have to overwrite the LayoutSubviews function in the cell. I tried to do that and i had some funny effects and resizing :-)
I have looked for a while to find some hints on the net but i didnt find one.
If anyone could provide some hints how to do this right? Links to tutorials or code will be fine.
Thanks
Eddy
It's NOT a good idea to overwrite setEditing:animated: and reload your table view cells there.
That is very resource-expensive, and not the right place to do it.
In the subclass of UITableViewCell, override method didtransitionToState:
There, you can act directly on the cell outlets, like so :
- (void)didTransitionToState:(UITableViewCellStateMask)state
{
[super didTransitionToState:state];
if (state == UITableViewCellStateShowingEditControlMask) {
// edit mode : peform operations on the cell outlets here
} else if (state ==UITableViewCellStateDefaultMask) {
// normal mode : back to normal
}
when you set myTable.editing=YES; it calls table view datasource and delegate method.
so if you have any data to display in table then the above code line calls the delgate method
so you can code here
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(myTable.editing==YES)
{
return 70;//customize it.
}
return 50;
}
If you want to resize the cells height you should also change "heightForRowAtIndexPath" in your UITableView Delegate.
At least that is a thing I stumbled over a few times.
Overwrite setEditing:animated: and reload your table view cells there.
I am display some long text in a cell, and resizing the height of the cell using heightForRowAtIndexPath. However, when the text is displayed it is running into the area used by the (blank) disclosure indicator.
When such a row is selected, and the checkmark is displayed, the text reformats itself to not use the indicator area, causing a visual effect I do not want.
If there was a UITableViewCellAccessoryBlank accessory type (rather than UITableViewCellAccessoryNone), maybe the text wouldn't wrap into that area when displaying. Am I going to have to create a custom cell and layout my own label, or is there a simpler way?
First of all, I don't see a property call UITableViewCellAccessoryBlank in the UITableView Cell class reference so I don't think this will work.
I think you have 2 options :
Create a custom cell, like you suggest.
Configure the textLabel of your cell to change his contentMode.
I read this in UILabel class reference :
The default content mode of the
UILabel class is
UIViewContentModeRedraw. This mode
causes the view to redraw its contents
every time its bounding rectangle
changes. You can change this mode by
modifying the inherited contentMode
property of the class.
I suppose that the textLabel bounds change every time you change the accessory type, so by default it redraw himself.
You can try this in your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method :
cell.textLabel.contentMode = UiViewSomeContentMode;
Content mode list can be found here. I'm not sure which one you should use so I let you try.
EDIT
It seems that contentMode is not working. So you should use a custom UITableViewCell to prevent any animation when adding an accessoryView.
Hope this helps !
Is there any way to decrease the standard width of grouped UITableViewCell and put a custom button on the left side(outside of cell boundary)? I tried to change the cell size but it keeps same
You are going to have to fake the editing mode.
What I mean by that is that as AtomRiot said you have to subclass UITableViewCell so that when in editing mode you show the button you want on the left, outside the cell.
But first things first.
To change the indentation level for your cells all you need to do is implement this delegate method for the UITableView
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
So that takes care of it. Then in your UITableViewCell subclass all I would do is to implement the method
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
which I assume is called when the table the cell belongs to has changed to editing mode.
There I would fade in (or animate in any way you want) a button to appear on the left of your cell.
I have done it inside a grouped-style cell but never on the outside.
Give it a try!
You could subclass UITableCell and add your own custom views inside of it. I have not personally added a button inside one but it should work. It may get confused with the row selected call the tableview makes if you are implementing that.
The Cocoanetics blog seems to have a pretty good solution to this:
http://www.cocoanetics.com/2010/03/how-to-shrink-cells/