I have a UITableView, and inside it is a UIView, and dynamic UITableViewCells.
The UIView is positioned in the top of the Table, and the cells come right below it.
My problem is, when I enlarge the UIView after a certain button press, it overlaps the cells below it. Due to the cells being dynamic, I am not sure how to access the cells and move their origin.y downwards.
Furthermore, if I have a lot of cells, I don't really want to be moving each cell one by one.
I tried looking to see if the UITableView has some kind of offset parameter for its cells, but didn't find anything of that sort.
Through the storyboard, when I enlarge the UIView inside the table, the cells automatically are pushed below it, but unfortunately when I enlarge the UIView programatically through the code (after the button press), the cells stay in their original place and don't move.
Here is an illustration to show what is happening:
My AutoLayout is turned off.
It sounds like you want your custom UIView to behave like a UITableViewCell by moving the cells up and down as it grows in height. So do just that. Create a custom UITableViewCell whose height can change dynamically and render that cell at the top (or wherever else you want it).
You should not be putting a UIView inside a UITableView in the manner that you are doing. As I see it, there are two main approaches:
1) Add the UIView above the UITableView. You have mentioned no reason as to why it needs to be inside of it.
UIView *view = [[UIView alloc] init];
UITableView *tableView = [[UITableView alloc] init];
view.translateAutoResizingMaskIntoConstraints = NO;
tableView.translateAutoResizingMaskIntoConstraints = NO;
[self.view addSubview:view];
[self.view addSubview:tableView];
NSDictionary *viewsDictionary = #{#"view" : view, #"tableView" : tableView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[view]" options:kNilOptions metrics:nil viewDictionary:viewsDictionary];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view][tableView]|" options:NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing metrics:nil viewDictionary:viewsDictionary];
2) Assuming the UIView absolutely needs to be inside the UITableView, the best way to do it is by using the contentOffset.
CGSize innerViewSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 200.0f);
UIView *innerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -innerViewSize.height, innerViewSize.width, innerViewSize.height)];
UITableView *tableView = [[UITableView alloc] initWithStyle:UITableViewStylePlain frame:self.view.bounds];
tableView.contentOffset = CGPointMake(0.0f, innerViewSize.height);
Then, whenever you need to increase the size of the inner view, you change it's y origin and increase the contentOffset.y.
Related
Initially I have a UITableView which takes up only a small part of the screen, using AutoLayout constraints. The UITableViewCells are laid out nicely - they are very basic with just an orange left aligned label and a black right aligned label.
Now, if I swipe up on the UITableView, it animates (the AutoLayout constraints), to take up a larger part of the screen, while reloading the table and bringing additional UITableViewCells into the scene.
My problem is, that the layout of the new UITableViewCells is animated along with the resizing of the UITableView. The black right label occurs right after the orange label, and animates to the right. In the end, everything is aligned as expected, only I can't figure out why the black right labels isn't simply positioned to the very right as soon as the cell appears on screen, but instead is animated into position.
Is there a way to tell the UITableViewCell to layout it's contents right away with respect to the width of the UITableView and the specified AutoLayout constraints, and thus bypassing the animation of the UITableViewCell contents?
My code for creating the UITableViewCell looks like this:
- (instancetype)init
{
self = [super init];
if (self) {
self.textLabel = [[UILabel alloc]init];
self.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
self.textLabel.textColor = [UIColor orangeColor];
[self.contentView addSubview:self.textLabel];
self.amountLabel = [[UILabel alloc]init];
self.amountLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.amountLabel.textAlignment = NSTextAlignmentRight;
self.amountLabel.font = [UIFont boldSystemFontOfSize:16.0];
[self.contentView addSubview:self.amountLabel];
NSDictionary *views = #{#"textLabel": self.textLabel, #"amountLabel": self.amountLabel};
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-10-[textLabel]-10-[amountLabel]-10-|" options:0 metrics:nil views:views]];
}
return self;
}
Initial TableView before animation:
TableView during animation (with black right label in the middle of it's journey to the right):
The following code works for me in swift. I have added the second label in detailTextLabel and in the attribute inspector for the TableView I had selected style as "Right Detail". I have attached the screenshot of the tableview with attribute inspector.
cell.textLabel!.text = arrayLabel[indexPath.row]
cell.detailTextLabel!.text = cntarray[indexPath.row]
Hope it might be helpful to you.
I want to place a UITableView such that it takes up the entirety of one of my view controller's subviews. Here's the view hierarchy, where self is my UIViewController subclass:
[self.view] -> [self.rootView] -> [self.tableView]
I know that I need to set setTranslatesAutoresizingMaskIntoConstraints to NO for any view whose subviews I want to lay out with Auto Layout, so here's my viewDidLoad.
[super viewDidLoad];
self.rootView = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, 320.0, 568.0)];
[self.rootView setTranslatesAutoresizingMaskIntoConstraints:NO];
So far so good. Now I want to add a UITableView.
self.tableView = [[UITableView alloc]init]; // same thing with initWithFrame:CGRectZero
[self.rootView addSubview:self.tableView];
No problem so far, but I can't see the table view (because it doesn't have a frame). Let's add some constraints:
[self.rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-0-[table]-0-|" options:0 metrics:nil views:#{#"table": self.tableView}]];
[self.rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[table]-0-|" options:0 metrics:nil views:#{#"table": self.tableView}]];
Everything breaks. self.rootView.backgroundColor is green and the cells are blue and full of text, but there's nothing on the screen but grey, maybe indicating that self.view is no longer in the view hierarchy? I've done a bunch of googling but can't find a real solution anywhere. I've also tried adding all the constraints one at a time the other way and the same thing happens. Does anyone have any ideas?
I believe you should be calling setTranslatesAutoresizingMaskIntoConstraints:NO on your UITableView instead of on self.rootView. But as rdelmar commented, first make sure your table view is not what you are seeing with the grey background since it will fill the screen and cover the other views.
And another note, I believeself.rootView = [[UIViewController alloc]initWithFrame...] above actually should be [[UIView alloc] initWithFrame...] since controllers don't have frames but maybe it's just incorrect above as this would cause an error when trying to run.
Xcode Interface Builder issue
Personally I do not like the way that interface builder works in Xcode. In this example I am trying to create a fairly complex view controller. On the viewDidLoad of the view controller I show a custom alert view (as such). It is not actually an alert view but more of a view that shows the user some information. I have a dimmed background view and a view on top of this. If I try to create this in interface builder it gets overly complicated as you cannot select the views in the background and move them etc without dropping subviews into the wrong views and so on...
Scenario
What I am trying to do is create a View which holds some labels and a button. The view controller has a difficulty property based on this it will have different text in the labels/amount of labels.
I.e. Easy -- 3 labels
Hard -- 4 labels
I create the dimmedView and alert(styled)View like this:
// Setup the dimmedView
UIView *dimmedView = [[UIView alloc] initWithFrame:self.view.frame];
dimmedView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
// Setup the startingAlertView
UIView *startingAlertView = [[UIView alloc] init];
startingAlertView.backgroundColor = [UIColor whiteColor];
I then create the three/four labels based on some logic and add the necassary labels to the startingAlertView based on logic also.
The issue that is obvious is that at no point a frame for the view is set. This means that it is returning 0,0,0,0. What I would like to happen is the view to take the required height based on the labels added.
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
Yes. you don't use initWithFrame: under auto layout, or rather, you can, but the frame is ignored. Create your dimming view with a frame of CGRectZero, setting translatesAutoresizingMasksToConstraints to NO, add it to your main view and create constraints pinning it to all edges of the superview.
Then, add your alert view, again with a frame of zero and the translates... property set to NO. Create constraints to centre this view in your dimming view. This view will get its size from its subviews, since labels have an intrinsic size.
Add your labels as subviews of this view, with frame of zero and translates... set to NO. Depending on their content you may wish to set preferred max layout width or a width constraint.
Create constraints pinning your labels to the left and right edges of the superview, and lining your labels up in a vertical 'stack'. In each case you could add padding to give your alert a bit of a border.
This can look like a large amount of code, so you may want to read the articles I've written on visual format for auto layout and creating constraints in code, with the associated autolayout convenience category to make your life easier.
If you're going to the auto layout route, then you can add constraints that will keep the proper space between each label, and the proper space between the top and bottom of the view with the first and last labels. However, if you're not doing this in Interface Builder, you might as well skip using auto layout also, because it's fairly simple to just adjust the height of the view as you add labels.
You would start by setting the height of the view to the size of the top and bottom spaces that you want to have around the labels. Then each time you add a label, add to it the height of the label plus the height of the space you're putting between labels.
You could also wait until you've added all of the labels that you want, then set the height to the bottom label's y position plus its height plus the bottom space you want to have around the labels.
Yes, using autolayout you can get the bounds from the parent view.
Here is a quick example, notice that we are not using frame, and using CGRectZero for our UILabels, the positioning comes from updateConstraints instead. I am using Visual Format Language to layout the labels which I recommend if you are doing it programatically.
Here we are making the labels the width of the parent view and then just stacked on top of each other.
#import "View.h"
#implementation View{
UILabel *_label1;
UILabel *_label2;
UILabel *_label3;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_label1 = [[UILabel alloc] initWithFrame:CGRectZero];
_label1.translatesAutoresizingMaskIntoConstraints = NO;
_label1.text = #"LABEL 1";
_label2 = [[UILabel alloc] initWithFrame:CGRectZero];
_label2.translatesAutoresizingMaskIntoConstraints = NO;
_label2.text = #"LABEL 2";
_label3 = [[UILabel alloc] initWithFrame:CGRectZero];
_label3.translatesAutoresizingMaskIntoConstraints = NO;
_label3.text = #"LABEL 3";
[self addSubview:_label1];
[self addSubview:_label2];
[self addSubview:_label3];
}
[self updateConstraintsIfNeeded];
return self;
}
-(void)updateConstraints
{
[super updateConstraints];
NSDictionary *_viewsDictionary = NSDictionaryOfVariableBindings(_label1,_label2,_label3);
// Set the contraintsto span the entire width of the super view
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label1]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label2]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label3]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
// Last setup the vertical contraints other wise they will end up in a random place
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[_label1]-[_label2]-[_label3]"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
I have the following ViewController:
It contains two UILabels at top, an UIImageView, below it a UITextView and below this a UIButton. I have arranged them using the Interface Builder following the blue line. All of this controls are inside a UIScrollView:
[self.scrollView setContentSize:CGSizeMake(320, 660)];
[self.scrollView addSubview:self.descriptionText];
[self.scrollView addSubview:self.descriptionImage];
[self.scrollView addSubview:self.titleLabel];
[self.scrollView addSubview:self.feedNameLabel];
[self.scrollView addSubview:self.load];
So when enabling Autolayout option, I just selected the ViewControler and then "Reset to Suggested Constraints in Description View Controller". But when I run the app, the scroll still appears for the entire page, but the only control scrolling is the UIButton. When scrolling up it will scroll below the UITextView.
I have made the UITextView to resize depending on the text, so I want my UIButton to always have the same distance to the UITextView. For that I have also set Vertical Spacing to the UIButton, but like this I don't have any scroll to my page.
Using the Autolayout for the first time, can I get some suggestions on what am I doing wrong ?
edit:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *contentView = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,660)];
[contentView addSubview:self.descriptionText];
[contentView addSubview:self.descriptionImage];
[contentView addSubview:self.titleLabel];
[contentView addSubview:self.feedNameLabel];
[contentView addSubview:self.load];
contentView.translatesAutoresizingMaskIntoConstraints = YES;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 660)];
//[scrollView setContentSize:CGSizeMake(320, 660)];
[scrollView addSubview:contentView];
[self.view addSubview:scrollView];
// to resize UITextField
[self.descriptionText sizeToFit];
[self.descriptionText layoutIfNeeded];
self.descriptionText.scrollEnabled = NO;
}
Autolayout is a bit tricky when it comes to UIScrollView subviews. I would recommend:
1-Embed all your controls (descriptionText + descriptionImage + titleLabel + feedNameLabel + load) into a UIView first, say contentView:
UIView *contentView = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,660)];
//add all controls as subviews to contenView
2-Add contentView as subview of self.scrollView.
3-Keep the translatesAutoresizingMaskIntoConstraints property of contentView to YES.
I recommend you read this technical note from Apple here.
If you are using AutoLayout you don't have to set the content size of your scroll view. In fact, I believe it has no effect at all. It is set automatically from the constraints you are setting up. The trick is that you have to have at least one constraint related to every side of the scroll view, otherwise the content size will be wrong and it won't scroll. So for example, if you would have a really large image in it you would need 4 constraints connecting the sides of the UIImageView to the sides of the UIScrollView. You can read about this more here.
I am doing a slide menu using a UITableView and I have 2 options on the menu and I want to put a button at the bottom like in this image:
I try to do that add a tableFooterView like that.
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 500, 320, 70)];
footerView.backgroundColor = [UIColor blackColor];
self.tableView.tableFooterView = footerView;
However, the view appears just after the second cell, but I want it at the bottom.
Thanks for help.
No you shouldn't add any empty cells, that's just hacky. If you really need the button to be at the bottom, you should use layoutSubviews to control the frame of the tableView and the footerView.
- (void)layoutSubviews
{
[super layoutSubviews];
self.tableView.frame = // top 80% of the screen
self.footerView.frame = // bottom 20% of the screen
}
You should know that every UITableViewCell has its height and footer is part of a UITableView and will appear at the bottom of a UITableView. If you want to make your UITableView look like what that image shows, you should make sure that your cells are high enough to make sure that your UITableView are high enough so that footer will appear at the bottom of UITableView
My suggestion is to add extra "empty" cell(I mean a cell with no content but has a height).
Add a Container View with View Controller from storyboard. You can use autoresizing to set the buttons on right place.