iPhone X TableView Footer problems - ios

Ok, two things first: I'm still new to iOS development and I bet this question was already answered a few times, so feel free to link it to a working solution 😊
Now to the problem: I have a UITableViewController including custom footer views. (They are custom to add a little border on the bottom, the build-in footer is just plain grey). Everything looks ok for the old school iPhones, however on the new X ones I get the following:
Is there a way to extend the footer view all down to the bottom of the view area?

The default layout is like this because of safe area. If you want to let the footer view cover the content I can give you 2 solutions.
Use UITableViewStyleGrouped instead, but the footer view will not static on the screen.
I don't think this is the best practise, but I think the layout should be you are looking for. This is a little bit tricky. The step is:
Create an extend view that the height equal to the bottom safe area.
Put this extend view below the footer view and set the background color same as header view's background color.
Make sure the header view height equals to the bottom safe area height. (This step is the tricky point)
Sample code
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UILabel *footerView = [[UILabel alloc] init];
footerView.backgroundColor = [UIColor colorWithRed:0.968 green:0.968 blue:0.968 alpha:1]; //Section Header Background Color
footerView.textAlignment = NSTextAlignmentRight;
footerView.text = #"Footer";
UIView *extendView = [[UIView alloc] init];
extendView.translatesAutoresizingMaskIntoConstraints = NO;
extendView.backgroundColor = footerView.backgroundColor;
[footerView addSubview:extendView];
[footerView addConstraints:#[
[NSLayoutConstraint constraintWithItem:extendView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:footerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:extendView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:footerView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:extendView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:footerView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:extendView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:footerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:self.view.safeAreaInsets.bottom]
]];
return footerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return self.view.safeAreaInsets.bottom;
}
Screen Record GIF

Related

How to add autolayout constraints to a multi-line UILabel inside tableview headerView?

I have searched all day for a solution to this problem. I am trying to have my UILabels autosize themselves based on the length of the text inside a tableViewHeaderView. Normally, with my UILabels inside a UIView, I would set top, leading, and trailing constraints to the UILabel and it would work just like how I wanted. However, I can't get that working inside a tableViewHeaderView. I am able to set top and leading constraints but my trailing constraint doesnt seem to be working. The text goes beyond the width of the screen.
Setting the preferredMaxLayoutWidth property to a number solves the issue but I don't want to have to hard code that.
Correct me if I am wrong, but setting the leading and trailing constraints should be able to give me the width of the view does it not? Then I could set preferredMaxLayoutWidth with that value. But that value is 2403 which is way longer than the width of the screen.
Anyone else experience this?
#import "CustomTableViewHeader.h"
#implementation ReplyHeader{
UILabel *questionLabel;
}
questionLabel = [[UILabel alloc]init];
questionLabel.text = #"SAMPLE TEXT: I had a question about how I can be a better human being using your method. I belive it is an integral part of what it means to be a human so I want to learn more if you are able give more details about it. I also found that what you said about the dogs out there is very cool and would love to learn more about that.";
questionLabel.font = [UIFont fontWithName:#"AvenirNext-Regular" size:17];
questionLabel.lineBreakMode = NSLineBreakByWordWrapping;
questionLabel.numberOfLines = 0;
questionLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:questionLabel];
Constraints
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0f constant:5.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-5.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0f constant:10]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-10]];
update view
- (void)viewDidLayoutSubviews
{
questionLabel.preferredMaxLayoutWidth = questionLabel.frame.size.width;
[self layoutIfNeeded];
}
UITableView's do not get along with AutoLayout. Both header and footer have inherit that problem.
As you might already know, UITableViews are fancy UIScrollViews. If you have ever tried to work with a scroll view, lots of elements and AutoLayout you must know that it does not work well. It lags. That's why UITableView reuse cells. It improves performance and they do not use AutoLayout.
You should try something like
// with this you will get the most compressed size for your view if the constraints properly define it's size
CGFloat height = [footer systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
footer = CGRectMake(CGRectGetMinX(view.frame), CGRectGetMinY(footer.frame), CGRectGetWidth(footer.bounds), height);
// This will tell the layout engine to ignore the view and not try to resize it
view.autoresizingMask = UIViewAutoresizingNone;
self.tableView.tableFooterView = footer;

how to add multiple images with each of its constraints using For/While loop

What i want to achieve is set buttons/images Horizontally Centered with main view, including its width set to 75% of the screen width.
I want to fit like 7 such images/buttons vertically on the screen (line-by-line).
I am using the following code, which is working perfect:
UIImageView *l1 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[l1 setImage:[UIImage imageNamed:#"level-1"]];
[l1 setContentMode:UIViewContentModeScaleAspectFit];
l1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:l1];
NSLayoutConstraint *c1 = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:20.f
];
[self.view addConstraint:c1];
NSLayoutConstraint *c1b = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.f
];
[self.view addConstraint:c1b];
NSLayoutConstraint *c1c = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:0.75f
constant:0.f
];
[self.view addConstraint:c1c];
Instead of repeating the same code for different images, i want to use some iteration process and increment the image name [UIImage imageNamed:#"level-xxx"]] and bind top position to the bottom position of the last added item.
How it would be possible? Thx
iOS 9 introduces a new class called UIStackView that allows you to do this :
https://www.hackingwithswift.com/read/31/2/uistackview-by-example
There are even back-ports of this to iOS 7 on Github.
Some people might downvote me for this but sometimes , when it comes to autolayout I just say , $##& this I'll write the layout code myself. And this is one of those cases (especially when UIStackView is not available).
First create all your views and store them in an array in viewDidLoad.
Override viewDidLayoutSubviews on your view controller and loop through the array , set the frames and lay them out one by one.
-(void)viewDidLayoutSubviews
{
NSArray* imageList = self.imageViewList
// calculate it if necessary
CGPoint startPoint = CGPointMake(200,200)
CGFloat spacing = 50
for(UIView *view in imageList) {
view.center = startPoint
startPoint = CGPointMake(startPoint.x , startPoint.y + spacing)
}
}
This is way more obvious , way easier to debug than a mess of constraints created in code.

iOS - Pure AutoLayout and UIScrollView not scrolling

This is my first time using UIScrollViews with a pure Autolayout approach. This is what the view hierarchy looks like
view
-scrollview
--view1
--view2
--view3
scrollview should contain view1|view2|view3 in that order.
I set the scrollviews width, height, centerx and bottom space to superview. The view1, view2 and view3 that are created all have their width and height constraints setup in their updateConstraints method. Additionally, some constraints are provided in code. What is the reason this scrollview is not scrolling from left to right? I have read literally all of the guides I can find online about creating and adding subviews to a UIScrollView programmatically with auto layout. I found some mention about having to provide four different constraints, leading, trailing, top and bottom for each view added as a subview to the scrollview. Are these the only NSLayoutAttributes that one can specify? How do attributes such as NSLayoutAttribueLeft or NSLayoutAttribueRight relate? I have read documentation on Apples website as well, specifically https://developer.apple.com/library/ios/technotes/tn2154/_index.html. I am attaching the setup I currently have. Everything is done via code.
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = #[ [[PCCGenericRating alloc] initWithTitle:#"Easiness"
andMessage:#"WHAT A JOKERRRR"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]],
[[PCCGenericRating alloc] initWithTitle:#"Joker"
andMessage:#"WHAT A JOKERRRR"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]],
[[PCCGenericRating alloc] initWithTitle:#"Difficulty"
andMessage:#"YOu are not difficult at all"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]]
];
[self initView];
}
- (void)initView {
CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
CGFloat heightDifference = navigationBarHeight + statusBarHeight;
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.delegate = self;
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.scrollView];
//setup constraints
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0f
constant:-heightDifference]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.0]];
[self.dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PCCGenericRating *rating = (PCCGenericRating *)obj;
PCCGenericRatingView *ratingView = [self createViewWithRating:rating];
[self.scrollView addSubview:ratingView];
int multiplier = (idx == 0) ? 1 : (int) (idx + 1) ;
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:ratingView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:multiplier
constant:0.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:ratingView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.0f]];
}];
}
- (PCCGenericRatingView *)createViewWithRating:(PCCGenericRating *)rating {
PCCGenericRatingView *view = [PCCGenericRatingView genericRatingViewWithTitle:rating.title andMessage:rating.message];
return view;
}
Upon printing out the scrollview constraints, they look okay to me:
po self.scrollView.constraints
<__NSArrayM 0x115b051f0>(
<NSLayoutConstraint:0x1145d9290 PCCGenericRatingView:0x114579880.centerX == UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145d9410 PCCGenericRatingView:0x114579880.centerY == UIScrollView:0x11458d4b0.centerY>,
<NSLayoutConstraint:0x1145d9dd0 PCCGenericRatingView:0x1145d9560.centerX == 2*UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145d9e40 PCCGenericRatingView:0x1145d9560.centerY == UIScrollView:0x11458d4b0.centerY>,
<NSLayoutConstraint:0x1145da6b0 PCCGenericRatingView:0x1145d9e90.centerX == 3*UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145da730 PCCGenericRatingView:0x1145d9e90.centerY == UIScrollView:0x11458d4b0.centerY>
)
Here is a screenshot of what it looks like:
I find it odd that the last element in the datasource is the first view controller showing up in the scrollview, when it should be the last view. It also doesn't scroll left to right as it should.
Make sure your top_constraint for the view1 and bottom_constraint for view3 will be as per your scrollView's constraints. Otherwise scrollview's contentSize: {0, 0}.
Wherever you are printing your constraints, try printing scrollview.contentSize, it will likely be 0,0 and that is where your problem is. As far as I know, and as you mentioned in your post, you have to explicitly set the subviews of a scrollview to the scrollviews top bottom left and right constraints. Setting these automatically sets the contentSize of the scrollview which will enable it to scroll. It looks like you are only setting centerX and centerY constraints which will not set the scrollviews contentSize to what you need.
Try setting these programatically (this is pseudocode but you get the idea):
view1.topConstraint = scrollView.topConstraint
view1.leftConstraint = scrollView.leftConstraint
view3.bottomConstraint = scrollView.bottomConstraint
view3.rightConstraint = scrollView.rightConstraint
If you set all of those correctly, your scrollview will scroll properly. Just remember to check the contentsize, and if the contentsize is 0,0 then your constraints aren't properly set up.

Change the height of UISegmentedControl in iOS

Problem: How to change the height of the UISegmentedControl in iOS?
After Searching, I got the following answers:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGRect frame= segmentedControl.frame;
[segmentedControl setFrame:CGRectMake(10.0, 20.0, 200.0, 50.0)];
}
But this resets the height of the control to default if I select any option of the segmented control in iOS 7
If you have autolayout, set the constraints of the UISegmentedControl. Please see pic of where's easiest to set this, make sure width and height are ticked, plus vertical and horizontal space constraints (select where lines that are deep orange in pic).
Now that you have this, control-drag the constraint that sets the height of the segmented control to your header file and name it something like segmentedControlHeightConstraint.
Once you have done that, within your viewDidLoad in your view controller implementation file add this code
self.segmentedControlHeightConstraint.constant = 50; // or whatever height you wish
This is the best way using auto layout to set the height for this.
Hope this helps
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:segmentContl
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:18]];
"change constant value for changing height"
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:mySegmentedControl
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:fNewHeight];
[mySegmentedControl addConstraint:constraint];

Center custom UIView vertically and horizontally using Auto Layout

I'm trying to build a rather simple animated custom UI using the Auto Layout API newly available iOS 6. The custom view I'm building has a circle that I want to be both vertically and horizontally centered.
Unfortunately I can't figure out why my constraints appear to work fine for UIButton, and UILabel elements but yield weird results when I use a custom view with and custom CALayer (in this case a circle, that will eventually be animated).
To be clear I don't want my view to expand to fill the whole screen, but rather to have dynamic "padding" so that the view is vertically centered both on the iPhone 4 and 5. I should also note that I'm very new to Cocoa and UIKit.
RootViewController.m:
...
- (void)viewDidLoad {
[super viewDidLoad];
// Create Circle View
CGRect circle_view_rect = CGRectMake(0, 0, 100, 100);
UIView *circle_view = [[UIView alloc] initWithFrame:circle_view_rect];
// Create Circle Layer
CircleLayer *circle_layer = [[CircleLayer alloc] init];
circle_layer.needsDisplayOnBoundsChange = YES;
circle_layer.frame = circle_view.bounds;
[circle_view.layer addSublayer:circle_layer];
// Enable Auto Layout
[circle_view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:circle_view];
// Center Vertically
NSLayoutConstraint *centerYConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerYConstraint];
// Center Horizontally
NSLayoutConstraint *centerXConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerXConstraint];
}
...
CircleLayer.m:
...
- (void)drawInContext:(CGContextRef)context {
CGContextAddArc(context, 50, 50, 50, 0.0, 2*M_PI, 0);
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextFillPath(context);
}
...
Basically the constraints I've implemented are:
center vertically inside parent view
center horizontally inside parent view
And this is the result I get:
Any help would be greatly appreciated, I've been pondering this one for a few days now.
Thanks
Try adding a height and width constraint to your circle_view. I couldn't even get just a pain square view to appear at all without adding those (using your code, minus the layer stuff).
NSLayoutConstraint *heightConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:widthConstraint];
Just to add to rdelmar's answer:
The core issue is that as soon as you go the NSLayoutConstraint route, and specify setTranslatesAutoresizingMaskIntoConstraints:NO, the frame you made with CGRectMake is rendered irrelevant for AutoLayout purposes. That's why it didn't use the info from the frame's height and width.

Resources