Dynamic UITableViewHeader View with Label - ios

I have a UIView in .xib file which I'm loading at runtime and setting it as tableHeaderView of UITableView. I have a UILabel in my xib file which can grow dynamically with fixed UIButton at bottom.
If I set the width of UILabel to fixed width it works.
If I set the Leading/Trailing on UILabel then it doesn't work :(
I'm using the below code to handle the height of headerView
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Dynamic sizing for the header view
if (table.tableHeaderView) {
UIView *headerView = table.tableHeaderView;
float height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = headerView.frame;
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if (height != headerFrame.size.height) {
headerFrame.size.height = height;
headerView.frame = headerFrame;
table.tableHeaderView = headerView;
}
}
}
Anybody can explain why setting the leading/trailing is not working?
My view with constraint is like (without fixed width):

call in the end of viewDidLayoutSubviews.
[viewWithLabelAndButton setNeedLayout];
[viewWithLabelAndButton layoutIfNeeded];

Related

Fail to set height constraints of UITextView in iOS

I create a xib file and it includes a UITextView, I add a height constraints for UITextView. When the UIViewController load the xib view I want to change the UITextView height based on the text height. I create an IBOutlet namedheightConstraints for the height constraints and textView for UITextView. I set it in
- (void)viewWillLayoutSubview {
CGFloat height = [self getTextHeight:self.textView];
self.heightConstraints.constant = height;
[self.textView layoutIfNeeded];
}
But it does not change the height of UITextView. Can anyone help?
Can you try
- (void)viewWillLayoutSubview {
self.heightConstraints.constant = self.textView.contentSize.height;
[self.textView layoutIfNeeded];
}
Implement like this, you are required to confirm the UITextView delegate method to change it's height dynamically
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
[self updateTextViewSize:textView];
return YES
}
- (void)updateTextViewSize:(UITextView*)aTextView{
CGFloat height = [self getTextHeight:aTextView];
self.heightConstraints.constant = height;
[self.textView layoutIfNeeded];
}
- (CGFloat)getTextHeight:(UITextView*)aTextView{
/*
calculate frame based on the text and return
*/
}

UITableViewCell width is not adjusting dynamically based on ios device type

My tableview cells are created entirely programmatically (I'm trying to learn to build an app from scratch without using storyboards) and the width of the cells is getting messed up.
Here is a screen shot http://imgur.com/ki6txqg of what the cell looks like in an iPhone 6 Plus. I'm trying to set the cell so that the UIView in the cell(self.view) gets adjusted automatically so that it fills the entire screen. I'm not sure why the width is staying static. Any advice would be greatly appreciated.
-(instancetype)initWithTweet:(PCRTweet *)tweet reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super init];
if (self) {
_tweet = tweet;
reuse = reuseIdentifier;
CGSize cellSize = self.contentView.frame.size;
CGRect backgroundView = CGRectMake(10.f, 5.f, (cellSize.width-20.f), (cellSize.height + 90.f));
self.view = [[UIView alloc] initWithFrame:backgroundView];
self.view.backgroundColor = [UIColor whiteColor];
self.view.layer.cornerRadius = 8;
self.view.layer.masksToBounds = YES;
self.view.layer.borderWidth = 1.0f;
self.view.layer.borderColor = background_color_gray.CGColor;
self.view.layer.masksToBounds = NO;
[self.contentView addSubview:self.view];
CGRect picView = CGRectMake(0.f, 0.f, 65.f, 65.f);
self.contentView.backgroundColor = background_color_gray;
}
return self;
}
Please read the points on following checklist to ensure you doing it all right:
-[ ] Have you checked that your contentView is dynamically changing?
-[ ] Have you tried putting constraints programatically?
-[ ] Try using constraints on the largest view : will auto adjust the relative views
Apart from it, you can auto-resizing for your frame.
You try this two UITableView Delegate method in table view class
For Dynamic Height
#pragma mark - UITableView Delegates
-(CGFloat)tableView:(UITableView )tableView estimatedHeightForRowAtIndexPath:(NSIndexPath )indexPath {
return 44.0;
}
-(CGFloat)tableView:(UITableView )tableView heightForRowAtIndexPath:(NSIndexPath )indexPath {
return UITableViewAutomaticDimension;
}
For Width (Get view controller width and take to backgroundView)
CGSize viewWidth = self.contentView.superview.superview.superview.superview.frame.size;
// self.contentView.superview -> return UITableViewCell
// self.contentView.superview.superview -> return UITableViewWrapperView
// self.contentView.superview.superview.superview -> return UITableView
// self.contentView.superview.superview.superview.superview -> return View Controller
CGRect backgroundView = CGRectMake(10.f, 5.f, (viewWidth.width-20.f), (cellSize.height + 90.f));
Try to use auto-resizing for your view.
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;

iOS: Get SubView width after constraints in a separate UIView

I currently have a storyboard linked to a ViewController (.h and .m). Inside the storyboard (Main View), I have created another UIView that takes about half of the top screen. I have assigned that to a custom Class. The custom class has a .h .m and XIB file. A specific code fragment in my custom class contains the following:
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self){
[[NSBundle mainBundle] loadNibNamed:#"myXIB" owner:self options:nil];
NSLog(#"Width before: %f",self.bounds.size.width);
NSLog(#"Height After: %f",self.bounds.size.height);
//[self setNeedsLayout];
//[self layoutIfNeeded];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 44)];
[self.view addSubview:searchBar];
CGRect newFrame = self.view.frame;
newFrame.size.width = self.bounds.size.width;
newFrame.size.height = self.bounds.size.height;
[self.view setFrame:newFrame];
[self addSubview:self.view];
}
return self;
}
The code currently creates a UISearchBar and add its to the sub view in my story board. My problem is related to calculating the width. The UISearchBar takes the argument for the width to be:
self.bounds.size.width
This calculates the frame drawed in the storyboard. However because the UIView inside my story board has constraints, the frame is not the final width. E.g. If i drawed 200px width in the story board, my code will retrieve the 200 and set the UISearch bar as 200 width. But in theory, the constraints will kick in and set the width to the size of the device e.g. 600px. So my question is, how can I find the updated width?
I have tried:
[self setNeedsLayout];
[self layoutIfNeeded];
They did not seem to work. Please can you suggest ideas?
-(void)layoutSubviews
{
[super layoutSubviews];
NSLog(#"Width before: %f",self.bounds.size.width);
NSLog(#"Height After: %f",self.bounds.size.height);
}
This should give you the actual size

how to add array elements in scrollview during the button click

I want to add the elements in scroll view during the button click.. if the elements add means the scroll view want to become big. depends upon the adding elements in scroll view.. the elements should be added one by one in scrollview when i clicked the button..
Maintain one height index in viewcontroller say
float = scrollViewheight;
When adding any object say adding UILabel to scrollView then increase scrollViewheight like this
UILabel *lblText = [UILabel new];
lblText.frame = CGRectMake(20,20,280,22);
[scrollView addSubview:lblText];
//update scrollViewheight
scrollViewheight = lblText.frame.origin.y+lblText.frame.size.height;
//update scrollView's content size
[scrollView setContentSize:CGSizeMake(0,scrollViewheight);
When adding new object use scrollViewheight
UILabel *lblText1 = [UILabel new];
lblText1.frame = CGRectMake(20,scrollViewheight+20,280,22);
[scrollView addSubview:lblText1];
//update scrollViewheight
scrollViewheight = lblText1.frame.origin.y+lblText1.frame.size.height;
//update scrollView's content size
[scrollView setContentSize:CGSizeMake(0,scrollViewheight);
EDIT : Just formatted
You can add your UIView elements with [self.scroll addSubview:myView] and resize your content size accordingly.
- (void)buttonClicked:(UIButton *)button {
// adds new view to scrollView
[self.scrollView addSubview:anotherView];
int padding = 10;
// set scrollView content size
[self.scrollView setContentSize:CGSizeMake(self.scrollView.contentView.size.width, self.scrollView.frame.size.height + padding + anotherView.frame.size.height)];
[self.scrollView setContentOffset:CGPointMake(0, anotherView.frame.origin.y) animated:YES]; // scrolls to new view
}
Hope it helps

table header view height is wrong when using auto layout, IB, and font sizes

I am trying to create a header view for my uiTableView (not a section header, I already have those.) I have set up an XIB in interface builder. All the connections are hooked up and it runs beautifully... except the table doesn't give it enough room! My problem is that the top of the table overlaps the table's header by a little.
My XIB is setup with autlayout for all the buttons, and IB is happy that the constraints don't conflict / ambiguous. The view is set to Freeform size, which in my case ended up being 320 x 471. Then in constraints for the view, I set an intrinsic size for the view of the same.
Now this works perfectly with my table. Everything looks great. But if I manually change any of the fonts in the header view with code, the layout makes the view bigger, and it ends up underneath my table.
Any ideas how to get the tableviewcontroller to leave enough room for the header view after setting fonts and sizes? I hope I've made sense explaining this.
Note: A Swift 3+ version can be found here: https://gist.github.com/marcoarment/1105553afba6b4900c10#gistcomment-1933639
The idea is to calculate header's height with help of systemLayoutSizeFittingSize:targetSize.
Returns the size of the view that satisfies the constraints it holds.
Determines the best size of the view considering all constraints it
holds and those of its subviews.
After changing header's height it is necessary to reassign tableHeaderView property to adjust table cells.
Based on this answer: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
- (void)sizeHeaderToFit
{
UIView *header = self.tableView.tableHeaderView;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect frame = header.frame;
frame.size.height = height;
header.frame = frame;
self.tableView.tableHeaderView = header;
}
I encountered a similar problem when I was trying to set my table view header to a custom view I defined with auto layout using interface builder.
When I did so, I would find that it would be obscured by a section header.
My work around involved using a "dummy view" as the table header view and then adding my custom view to that dummy view as a subview. I think this allows auto layout to configure the appearance as per the constraints and the containing view's frame. This is probably not as elegant as vokilam's solution, but it works for me.
CGRect headerFrame = CGRectMake(0, 0, yourWidth, yourHeight);
UIView *tempView = [[UIView alloc] initWithFrame:headerFrame];
[tempView setBackgroundColor:[UIColor clearColor]];
YourCustomView *customView = [[YourCustomView alloc] initWithFrame: headerFrame];
[tempView addSubview:movieHeader];
self.tableView.tableHeaderView = tempView;
Because your tableHeaderView is inited form xib with auto layout, so the constraints of your custom headerView and the superView is unknown.You should add the constraints of your custom headerView:
1.in viewDidLLoad assign your custom headerView to the tableView's tableHeaderView
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *yourHeaderView = [[[NSBundle mainBundle] loadNibNamed:#"yourHeaderView" owner:nil options:nil] objectAtIndex:0];
//important:turn off the translatesAutoresizingMaskIntoConstraints;apple documents for details
yourHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.tableHeaderView = yourHeaderView;
}
2.in - (void)updateViewConstraints,add the essential constraints of your custom headerView
- (void)updateViewConstraints
{
NSDictionary *viewsDictionary = #{#"headerView":yourHeaderView};
NSArray *constraint_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView(121)]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[headerView(320)]"
options:0
metrics:nil
views:viewsDictionary];
[self.headerView addConstraints:constraint_H];
[self.headerView addConstraints:constraint_V];
NSArray *constraint_POS_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_POS_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
[self.tableView addConstraints:constraint_POS_V];
[self.tableView addConstraints:constraint_POS_H];
[super updateViewConstraints];
}
OK!
PS:Here is the related document:Resizing the View Controller’s Views
What solved my issue was this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var newFrame = headerView.frame
// Needed or we will stay in viewDidLayoutSubviews() forever
if height != newFrame.size.height {
newFrame.size.height = height
headerView.frame = newFrame
tableView.tableHeaderView = headerView
}
}
}
I found this solution somewhere and worked like charm, I can't remember where though.
I found vokilam's answer to work great. Here's his solution in Swift.
func sizeHeaderToFit() {
guard let header = tableView.tableHeaderView else { return }
header.setNeedsLayout()
header.layoutIfNeeded()
let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
header.frame.size.height = height
tableView.tableHeaderView = header
}
The other answers pointed me in the right direction for getting the tableHeaderView to resize appropriately. However, I was getting a gap between the header and the tableView cells. This could also cause your cells to overlap with your header, depending on if the header grew or got smaller.
I went from this:
tableView.reloadData()
let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableView.tableHeaderView?.frame.size.height = height
To fix the gap/overlap all I had to do was move tableView.reloadData() to after I set the new height instead of before.
To this:
let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableView.tableHeaderView?.frame.size.height = height
tableView.reloadData()
In my case systemLayoutSizeFittingSize return incorrect size, because one of the subviews use aspect ratio (width to height) 4:1 constraint.
After I change it to constant height constraint value (100), it begin to works as expected.
Combining answers by #SwiftArchitect, #vokilam, and #mayqiyue, and using a programmatically created tableHeaderView instead of a nib (although, I don't see any reason why it shouldn't work with a nib), and under iOS 10.x/Xcode 8.2.1, here is what I have working:
Create the view in your view controller's -viewDidLoad or -init methods, or wherever you're using the table view:
MyCustomTableHeaderView *myCustomTableHeaderView = [[MyCustomTableHeaderView alloc] init]; // Don't set its frame
myCustomTableHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.myTableView.tableHeaderView = myCustomTableHeaderView;
self.myCustomTableHeaderView = myCustomTableHeaderView; // We set and update our constraints in separate methods so we store the table header view in a UIView property to access it later
Wherever you set up your custom header view's constraints (could be in `updateConstraints but this method could be called multiple times):
// We use Pure Layout in our project, but the workflow is the same:
// Set width and height constraints on your custom view then pin it to the top and left of the table view
// Could also use VFL or NSLayoutConstraint methods
[self.myCustomTableHeaderView autoSetDimension:ALDimensionWidth toSize:CGRectGetWidth(self.superview.frame)]; // Width set to tableview's width
[self.myCustomTableHeaderView autoSetDimension:ALDimensionHeight toSize:height]; // Whatever you want your height to be
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeLeft];
// This is important
[self.myCustomTableHeaderView layoutIfNeeded]; // We didn't need to call -setNeedsLayout or reassign our custom header view to the table view's tableHeaderView property again, as was noted in other answers
sizeHeaderToFit suggested by #vokilam didn't work for me.
What worked is a modified version of #mayqiyue, invoked during viewDidLoad, wired height, equal width to table view.
// Anchor the headerview, and set width equal to superview
NSDictionary *viewsDictionary = #{#"headerView":self.tableView.tableHeaderView,
#"tableView":self.tableView};
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[headerView(==tableView)]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView.tableHeaderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView(121)]"
options:0
metrics:nil
views:viewsDictionary]];
Below code has worked for me :-
Add this in your UIView class -
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.frame = CGRectMake(0, 0, self.frame.size.width, [[self class] height]);
}
return self;
}
#rmigneco answer worked for me, so here's the swift version of it:
let headerFrame = CGRectMake(0, 0, 100, 1);
let tempView = UIView(frame: headerFrame);
tempView.backgroundColor = UIColor.clearColor()
let yourCustomView = CustomView(frame: headerFrame)
tempView.addSubview(yourCustomView)
self.tableView.tableHeaderView = tempView

Resources