Update UITableView size based on UIView constraints - ios

I have a UIView with a UITableView inside.
In a UIViewController, I instantiate the UIView like this:
viewMain = [ViewMain alloc]initwithframe:cgrectmake(0,0,200,500)];
Inside this view, in the method initWithFrame:(CGRect)frame, I instantiate the UITableView like this:
_tableView = [UITableView alloc]iniWithFrame:CGRectMake(0,0,frame.size.width,frame.size.height)];
The problem is when I apply constraints to viewMain, how can I update de size of the tableView based on the viewMain layout constraints?

you would need to add constraints to the childview (i.e. autolayout itself if the superview changes), like so:
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
[_tableView addConstraint:left];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[_tableView addConstraint:top];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
[_tableView addConstraint:width];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
[_tableView addConstraint:height];
Not sure if you also need to do this:
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
before adding the constraints.

If you are setting table view with frame then you will not able to update layout using constraints. If you want to update your table view according to your parent view, use constraints from storyboard to update constraints.

Basically you have to call -setNeedUpdateConstraintsand -updateConstraintsIfNeeded on the cell in -tableView:cellForRowAtIndexPath:.
See a very detailed explanation and sample code in this answer.
Here is a great Tutorial by Ray Wenderlich.
You can also try this in your tableViewController:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (IS_IOS8_OR_ABOVE) {
return UITableViewAutomaticDimension;
}
//self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
[self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded];
return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}

I would set the tableView's frame equal to the mainView's frame.
i.e.:
_tableView = [UITableView alloc]iniWithFrame:CGRectMake(mainView.frame.origin.x,mainView.frame.origin.y,mainView.frame.size.width,mainView.frame.size.height)];

Related

iOS Custom Keyboard Extension Autolayout

I'm developing a custom keyboard and i want to use only a UICollectionView as content. The CollectionView should fill (center x and y, width and height available space) of the keyboardview.
My problem is that if i rotate the device the collection view won't be resized.
I tried layout constraints, resizing the view on viewWillTransitionToSize but it does not work. Can anyone give me a hint or show me a way to get this done?
Autolayout should set translatesAutoresizingMaskIntoConstraints to NO before adding Constraint. The code below can make a UICollectionView autolayout with equal edges.
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.translatesAutoresizingMaskIntoConstraints = NO;
collectionView.backgroundColor = [UIColor redColor];
[self.view addSubview:collectionView];
//Adding layout constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
}
In addition, I recommend you to use Masonry to build autolayout. In your case, you just need to run the code below to make constraints.
[collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
By the way, autolayout will use more memory then calculating frame yourself. As iOS Keyboard extension has a very harsh memory usage limit (around 40mb depends on your device). If your keyboard would use many memory on process other things, I suggest you don't use autolayout.

Autolayout for UITextview inside custom UITableviewcell

I have a UITextView inside a custom UITextviewCell. I want the cell to grow in size according to the content of UITextview. I have pinned top, bottom, right, left of textview to the content view of cell. When I pre-populate textview with text, it is working fine as expected. Its not working when I enter text with keyboard. Inside my custom cell.
UITextView * textView = [[UITextView alloc]init];
[textView setTranslatesAutoresizingMaskIntoConstraints:NO];
[textView setBackgroundColor:[UIColor redColor]];
[textView setScrollEnabled:NO];
[textView setDelegate:self];
[self.contentView addSubview:textView];
NSLayoutConstraint *xConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
NSLayoutConstraint *yConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
NSLayoutConstraint *rightConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
NSLayoutConstraint *bottomConstrain = [NSLayoutConstraint constraintWithItem:textView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[self.contentView addConstraint:xConstrain];
[self.contentView addConstraint:yConstrain];
[self.contentView addConstraint:rightConstrain];
[self.contentView addConstraint:bottomConstrain];
In my tableview controller added following line
-(void)viewdidload
{
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 100.0f ;
}
There are a couple thing you can do here, one that comes to mind:
• As you are entering text, keep track of the height of the UITextView, as in keep an 'old height' variable and a 'new height' variable. To get the 'new height' you could use the sizeThatFits method of the UITextView with height set to for example 1000 (large enough that it won't constrain your result).
• The moment the new height is different than the old height, either smaller or larger, you reload the cell that houses the UITextView you are working on. You can event set the [cell setNeedsLayout], followed by [cell layoutIfNeeded] to make sure it resizes.
I'd imagine this should work, please let me know if it doesn't.

Custom UIView (with xib) autolayout width and pushing viewcontroller from delegate

I have problem with setting constraints to custom UIView and pushing new ViewController by delegate when I tap the view. I tried to find solution for these problems but couldn't find one that answers to these questions.
Autolayout problem
I have custom xib-file which has been set Size: Freeform, Width: 600 and Height: 25, it also includes one label and one button with constraints in this view. I have added this view successfully below navigation bar where I want it. Problem is, that it don't make anything to fit it's width equally with navigation bar / window size (I have tried multiple choices eg. making new frame for view that is width of window / navigation bar). It only appears to have static 600 width all the time whatever I try.
First two constraints are working, it appears 25 points below navigation bar and it centers it. But last one won't make anything.
How should I do this properly? So far have this:
[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.subView];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.navBar
attribute:NSLayoutAttributeBottom
multiplier:1
constant:25.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
Should I do something more with xib-file that it will make this width to fit it's parent view? I have also implemented initWithFrame, initWithCoder and intrinsicContentSize to my custom view.
Solution
I ended up to make containerView for my subView and center it vertically and horizontally and found right constraint for width. I also forgot to update my subView's view frames to match navigation bar width. Here is what I ended up to (if there is better way to do this, I take critic with pleasure):
self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 62, self.navBar.frame.size.width, 25)];
[self.view addSubview:self.containerView];
self.subView = [[SubView alloc]init];
[self.subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.containerView addSubview:self.subView];
self.subView.view.frame = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.containerView addConstraint:
[NSLayoutConstraint constraintWithItem:self.subView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.containerView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0]];
Delegate problem (solved)
For answer to this problem: check MisterGreen's answer below.
Another problem occured when I made UITapGestureRecognizer with delegate in my custom view. What I want is when I tap the view, it opens another ViewController. The delegate function is like this where I implement my custom view:
-(void)pushViewControllerUsingDelegate
{
NSLog(#"DELEGATE WAS : %#", self.subView.delegate);
[self pushViewController:self.anotherViewController animated:YES];
}
Now it gives exception when I tap the view:
DELEGATE WAS : <MasterViewController: 0x7fc96132e7d0> <-- Delegate is OK
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AnotherViewController 0x7fc961248230> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key subViewButton.'
What this actually means? I have this subViewButton IBOutlet with weak property, does it have something to do with this? Or is there another way to make this happen?
Tutorial which I followed: https://www.youtube.com/watch?v=TfKv1MYxnA4
Because there is not enough data to be exactly sure what is the problem you encountered, i have just created a code snippet that is working and doing exactly what you are trying to get.
About the constraints i think the problem is the hight constraint that is missing(unless you determined it elsewhere),
try to remember that when you add constraints provide enough data to the compiler to understand how to resize and position your subview according to it's superview, in your case it didn't know what is the hight cause you didn't supply nor bottom or hight constraint to determine it.
About the delegate method you didn't supply enough data to exactly determine what is the problem, so i've written something that i think is doing what you are trying to get.
This code snippet is tested and working:
The subview:
View.h
#protocol viewManager <NSObject>
#optional
- (void)subviewWasTapped;
#end
#interface View : UIView
#property (nonatomic, strong) id<viewManager>delegate;
#end
View.m
#implementation View
- (void)awakeFromNib{
[super awakeFromNib];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(viewWasTapped:)];
[self addGestureRecognizer:tap];
}
- (void)viewWasTapped:(NSNotification *)notification
{
[self sendViewWasTappedToDelegate];
}
- (void)sendViewWasTappedToDelegate
{
#synchronized(_delegate)
{
if([_delegate respondsToSelector:#selector(subviewWasTapped)])
{
[_delegate subviewWasTapped];
}
}
}
#end
FirstViewController:
#interface ViewController () <viewManager>
#property (nonatomic, strong) View *subview;
#end
#implementation ViewController
#synthesize subview;
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil];
subview = [subviewArray objectAtIndex:0];
[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:subview];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topLayoutGuide
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0.0]];
// Height constraint to determine the
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:25.0]];
[self.view layoutIfNeeded];
[subview setDelegate:self];
}
#pragma mark - viewManager delegate method
- (void)subviewWasTapped{
SecondeViewController *secondeVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondeViewController"];
[self.navigationController pushViewController:secondeVC animated:YES];
}

What is the best approach to autolayout dynamic size uitableview cells

I am currently stucked trying to create a uitableview cell with autolayouting that dynamically resizes according to the "Dynamic sized labels" within
my approach is subclass the uitableview cell and in the "updateConstraints" method i override :
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:width]];
CGFloat height = self.frame.size.height;
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:height]];;
[super updateConstraints];
at the same time i did all the layouting in the nib file where subviews are resized correctly but the cell height doesnot resize.
what is the newbie mistake i am doing here?
apparently i was sleeping or something just change
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:height]];;

Center UITableView within a subView

I'm ultimately trying to vertically center my table view cells within a UITableView if the total amount of cells * row height < the height of the tableView
My problem is that whenever I try to reference the tableview height (which should be resized when added to its superview. It returns original height of the view as defined in Interface Builder.
Here is the method that adds the subView with the tableView:
- (void)addMissonListView {
missionView = [BSMissionListView loadMissionView:YES];
BSList *list = (BSList *)self.dataModel;
missionView.missionList = list.arrayMissions;
missionView.delegate = self;
missionView.heightofSuperView = [NSNumber numberWithFloat:viewBottomContainer.frame.size.height];
[viewBottomContainer addSubview:missionView];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[BSUtility setConstraintsOnView:viewBottomContainer relativeTo:missionView withConstant:0];
}
This is the setsConstraintsOnView method that's called:
+ (void)setConstraintsOnView:(UIView *)superView relativeTo:(UIView *)subView withConstant:(int)constant
{
[subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeBottom multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeTop multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeRight multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeLeft multiplier:1 constant:constant]];
}
The subview consists of a tableView inside of UIView. In Interface Builder, the size of the UIView is 320 X 586 and the tableView is 310 x 558.
viewBottomContainer's size in IB is 320 x 408
I'm trying to get the information about the view's and tableView's height in the configureGUI method. And once that works, I'll pass it to another method to create a contentOffset in the TableView. but the NSLog's keep returning the original values, not the resized values.
These are the methods from the BSMissionListView class:
- (void)awakeFromNib {
[super awakeFromNib];
[self configureGUI];
}
- (void)configureGUI {
[super configureGUI];
NSLog(#"Bounds height of the View is %f",self.bounds.size.height);
NSLog(#"Bounds height of the tableView is %f",self.tableViewMissionList.bounds.size.height);
[self setBackgroundColor:[UIColor clearColor]];
}
I'll be happy to post more code if that will be helpful. Thank you!
I was able to solve the problem by passing in the tableView's superView's height by ways of the method that the loaded the tableView nib.
-I added a CGFloat variable to the method that loads the nib in the BSMissionListView Class
-I passed in the viewBottomContainer's height and then used that to set the heightOfSuperView value in the newly modified method.
-heightOfSuperView's value is used to calculate to contentInset of the TableView to center it within the superView
Thank you to #Alex Renyolds for helping me out.
- (void)addMissonListView {
[viewBottomContainer setNeedsLayout];
[viewBottomContainer layoutIfNeeded];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLog(#"Height of bottomViewContrainer: %f", viewBottomContainer.bounds.size.height);
BSList *list = (BSList *)self.dataModel;
missionView = [BSMissionListView loadMissionView:YES heightOfSuperView:viewBottomContainer.bounds.size.height listArray:list.arrayMissions];
missionView.delegate = self;
[viewBottomContainer addSubview:missionView];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[BSUtility setConstraintsOnView:viewBottomContainer relativeTo:missionView withConstant:0];
}
BSMissionList
+ (BSMissionListView *)loadMissionView:(BOOL)isEditable heightOfSuperView:(CGFloat)height listArray:(NSMutableArray *)list {
BSMissionListView *missionListView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([BSMissionListView class]) owner:nil options:nil] lastObject];
missionListView.missionList = list;
missionListView.isEditable = isEditable;
missionListView.heightofSuperView = (height- 49);
[missionListView configureGUI];
return missionListView;
}

Resources