iOS 7.1 UITableView layoutSubviews issue - ios

I'm working on a project that must support both iOS 8 and iOS 7.1. Right now I'm running into a problem that only appears on iOS 7.1 but works properly on iOS 8. I have a ViewController that contains a tableview and a custom view in the tableHeaderView. I'll post the code as follows. All constraints are added programatically.
//View Controller.
-(void)viewDidLoad
{
[super viewDidLoad];
self.commentsArray = [NSMutableArray new];
[self.commentsArray addObject:#"TEST"];
[self.commentsArray addObject:#"TEST"];
[self.commentsArray addObject:#"TEST"];
[self.commentsArray addObject:#"TEST"];
[self.commentsArray addObject:#"TEST"];
[self.commentsArray addObject:#"TEST"];
[self.view addSubview:self.masterTableView];
self.masterTableView.tableHeaderView = self.detailsView;
[self.view setNeedsUpdateConstraints];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.view layoutIfNeeded];
}
//Table view getter
- (UITableView *)masterTableView
{
if(!_masterTableView)
{
_masterTableView = [UITableView new];
_masterTableView.translatesAutoresizingMaskIntoConstraints = NO;
_masterTableView.backgroundColor = [UIColor greyColor];
_masterTableView.delegate = self;
_masterTableView.dataSource = self;
_masterTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_masterTableView.showsVerticalScrollIndicator = NO;
_masterTableView.separatorInset = UIEdgeInsetsZero;
}
return _masterTableView;
}
-(void)updateViewConstraints
{
NSDictionary *views = #{
#"table" : self.masterTableView,
#"details" : self.detailsView,
};
NSDictionary *metrics = #{
#"width" : #([UIScreen mainScreen].applicationFrame.size.width)
};
//Table view constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[table]-0-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[table]-0-|" options:0 metrics:0 views:views]];
//Details View
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[details(width)]-0-|" options:0 metrics:metrics views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[details]-0-|" options:0 metrics:metrics views:views]];
}
//Details View Getter
- (DetailsView *)detailsView
{
if(!_detailsView)
{
_detailsView = [[DetailsView alloc]initWithFrame:CGRectMake(0, 0, 0, 100)];
_detailsView.backgroundColor = [UIColor orangeColor];
return _detailsView;
}
Now the details view contains some basic subviews which all derive from a UIView and the details view itself derives from a more general super class. I'll post the code as follows.
//Parent View
#interface ParentView (): UIView
- (instancetype)init
{
self = [super init];
if (self)
{
[self setupViews];
}
return self;
}
- (void)setupViews
{
[self addSubview:self.publishedTimeView];
}
- (void)updateConstraints
{
NSDictionary *views = #{
#"time" : self.publishedTimeView,
};
// Header with published video time
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[time]-10-|" options:0 metrics:0 views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[time(11)]" options:0 metrics:0 views:views]];
[super updateConstraints];
}
//Getter for the timePublished view added to the detail view. Will not post all its related code for //the sake of brevity.
- (TimePublishedDetailView *)publishedTimeView
{
if(!_publishedTimeView)
{
_publishedTimeView = [TimePublishedDetailView new];
_publishedTimeView.translatesAutoresizingMaskIntoConstraints = NO;
}
return _publishedTimeView;
}
//Child View (or the detailsView) of the view controller.
#interface DetailsView : ParentView
#implementation RecordingDetailsView
- (void)updateConstraints
{
NSDictionary *views = #{
#"time" : self.publishedTimeView,
};
//Vertical alignment of all views
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[time]" options:0 metrics:nil views:views]];
[super updateConstraints];
}
- (void)setModel:(DetailViewModel *)model
{
self.publishedTimeView.date = model.dateTime;
[self setNeedsUpdateConstraints];
[self layoutSubviews];
}
Now on iOS 8 this looks like this:
However on iOS 7.1 this will crash with this error message:
"Exception: Auto layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super"
I've googled this and played around with the code in my layout calls to see if I can remedy the problem but so far have been unsuccessful. If anyone could post some tips or advice on how to fix this I would really appreciate it.

I know this question is old, but I ran into a similar problem recently and after a lot of Googling, trying and failing I made it work with the help of this answer and a few changes.
Just add this category to your project and call [UITableView fixLayoutSubviewsMethod]; only once (I recommend inside AppDelegate).
#import <objc/runtime.h>
#import <objc/message.h>
#implementation UITableView (FixUITableViewAutolayoutIHope)
+ (void)fixLayoutSubviewsMethod
{
Method existing = class_getInstanceMethod(self, #selector(layoutSubviews));
Method new = class_getInstanceMethod(self, #selector(_autolayout_replacementLayoutSubviews));
method_exchangeImplementations(existing, new);
}
- (void)_autolayout_replacementLayoutSubviews
{
[super layoutSubviews];
[self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
[super layoutSubviews];
}
#end

Related

How to apply constraint programmatically based on simulator orientations

In my ViewController I have added one UIView programmatically using Auto-layouts
Here I am applying constraints based on simulator orientations but here based on my code constraints only applying when simulator at portrait mode but when change it at landscape mode constraints are not applying what did I do here wrong
my code:
#import "MainViewController.h"
#interface MainViewController (){
UIView * MainView;
}
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
MainView = [[UIView alloc] init];
MainView.backgroundColor = [UIColor orangeColor];
MainView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:MainView];
[self AppyingAutolayoutsAtPortraint];
}
-(void)AppyingAutolayoutsAtPortraint{
NSDictionary * HeaderDictionary = NSDictionaryOfVariableBindings(MainView);
//Appliying Autolayouts for MainView
[self.view removeConstraints:self.view.constraints];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[MainView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[MainView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary]];
[self.view setNeedsLayout];
}
-(void)AppyingAutolayoutsLandAtScpae{
NSDictionary * HeaderDictionary = NSDictionaryOfVariableBindings(MainView);
//Appliying Autolayouts for MainView
[self.view removeConstraints:self.view.constraints];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-10-[MainView]-10-|"]
options:0
metrics:nil
views:HeaderDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-10-[MainView]-10-|"]
options:0
metrics:nil
views:HeaderDictionary]];
[self.view setNeedsLayout];
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context){
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait) {
NSLog(#"UIInterfaceOrientationPortrait");
[self AppyingAutolayoutsAtPortraint];
}
else if (orientation == UIInterfaceOrientationLandscapeLeft) {
NSLog(#"UIInterfaceOrientationLandscapeLeft");
[self AppyingAutolayoutsLandAtScpae];
}else if (orientation == UIInterfaceOrientationLandscapeRight){
NSLog(#"UIInterfaceOrientationLandscapeRight");
[self AppyingAutolayoutsLandAtScpae];
}
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
}];
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
#end
You can use size classes for your requirement. Using size classes, when designing your app, you can now create a single layout, which works on all current iOS devices – without crufty platform-specific code. All you need to do is to open storyboard, goto file inspector and make sure that the Use Size Classes checkbox is ticked.
Here is a really nice tutorial on Size Classes.
You call 2 different functions in your UIInterfaceOrientationLandscapeLeft and UIInterfaceOrientationLandscapeRight
This seems to make no sense:
else if (orientation == UIInterfaceOrientationLandscapeRight){
NSLog(#"UIInterfaceOrientationLandscapeRight");
[self AppyingAutolayoutsAtPortraint]; //?
}

UIScrollview add subview horizontally pure autolayout with for loop

I have the following UIView hierarchy:
-UIView
-UIScrollView
My constraint for UIScrollview with relation to it's super view are very simple:
#"H:|-%f-[%#]-%f-|"
and
#"V:|-%f-[%#]-%f-|"
They are working as expected.
I am trying to add a UIImageView as subview of scrollview Horizontal.
So my view hierarchy will become:
-UIView
-UIScrollView
-UIImageView
I am adding UIImageView as subview programmatically in UIScrollView using a for loop.
In the for loop, how can I achieve:
[SuperView]-10-[scrollview]-10-[UIImageView]-10-[UIImageView]-10-[UIScrollView]-10-[SuperView]
The problematic section is the bold part.
What I have tried:
for(int i=1;i<3;i++)
{
UIImageView *image = [[UIImageView alloc] init];
[image setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d.jpg",i]]];
image.translatesAutoresizingMaskIntoConstraints = NO;
[_scrollView addSubview:image];
UIView *superView = _scrollView;
NSDictionary * views = NSDictionaryOfVariableBindings(superView, image);
NSString *formate = [NSString stringWithFormat:#"H:|-%f-[%#]-%f-|", scrollViewLeftMarginFromParent, #"image", scrollViewRightMarginFromParent];
NSArray * WIDTH_CONSTRAINT = [NSLayoutConstraint constraintsWithVisualFormat:formate options:0 metrics:nil views:views];
formate = [NSString stringWithFormat:#"V:|-%f-[%#]-%f-|", scrollViewTopMarginFromParent, #"image", scrollViewBottomMarginFromParent];
NSArray * HEIGHT_CONSTRAINT = [NSLayoutConstraint constraintsWithVisualFormat:formate options:0 metrics:nil views:views];
[superView addConstraints:WIDTH_CONSTRAINT];
[superView addConstraints:HEIGHT_CONSTRAINT];
}
The approach I can think of:
LeftSide:
[scrollview]-10-[UIImageView]
Right side:
[UIImageView]-10-[scrollview]
in between:
[UIImageView]-10-[UIImageView]
If it's the right approach, then how do I achieve this in for loop.
If it's not then what is best approach.
It's quite simple actually. Your approach is correct, all you need is how you convert that into code. I will try to simplify this for you. I am assuming a UIImageView's width & height as 100. You can change as you like
-(void)setUI
{
lastView = nil; //Declare a UIImageView* as instance var.
arrayCount = [array count]; //In your case a static count of 3
for(NSInteger index =0; index < arrayCount; index++)
{
UIImageView *view = [[UIImageView alloc] init];
[self.mainScroll addSubview:view];
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.mainScroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[view(100)]-20-|" options:0 metrics:nil views:#{#"view":view}]];
//--> If view is first then pin the leading edge to main ScrollView otherwise to the last View.
if(lastView == nil && index == 0) {
[self.mainScroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view(100)]" options:0 metrics:nil views:#{#"view":view}]];
}
else {
[self.mainScroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[lastView]-10-[view(100)]" options:0 metrics:nil views:#{#"lastView":lastView, #"view":view}]];
}
//--> If View is last then pin the trailing edge to mainScrollView trailing edge.
if(index == arrayCount-1) {
[self.mainScroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[view]-10-|" options:0 metrics:nil views:#{#"view":view}]];
}
//--> Assign the current View as last view to keep the reference for next View.
lastView = view;
}
}
I had encountered similar situation where my scrollview along with its content view was created from IB, but the subviews were added programatically. Writing constraints for subviews was making the View controller bloated. Also the for loop was was getting a lots of ifs and elses,hence I wrote a UIView Subclass to handle this scenario.
Change the class type for you Content View in IB, get a reference of it, add subviews through directly setting the property stackViewItems,or methods -(void)insertStackItem:, -(void)insertStackItem:atIndex:
#import "IEScrollContentView.h"
#interface IEScrollContentView()
{
NSMutableArray * _stackViewItems;
}
#property (nonatomic,strong) NSLayoutConstraint * topConstraint;
#property (nonatomic,strong) NSLayoutConstraint * bottomConstraint;
#end
#implementation IEScrollContentView
#synthesize stackViewItems = _stackViewItems;
//-----------------------------------------------------------------//
#pragma mark - Init Methods
//-----------------------------------------------------------------//
-(instancetype)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder])
_stackViewItems = [NSMutableArray new];
return self;
}
-(instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame])
_stackViewItems = [NSMutableArray new];
return self;
}
//-----------------------------------------------------------------//
#pragma mark - Public Methods
//-----------------------------------------------------------------//
-(void)setStackViewItems:(NSArray *)stackViewItems {
if(!_stackViewItems)
_stackViewItems = [NSMutableArray new];
for (UIView * view in stackViewItems) {
[self insertStackItem:view];
}
}
-(void)insertStackItem:(UIView *)stackItem
{
[self insertStackItem:stackItem atIndex:_stackViewItems.count];
}
-(void)insertStackItem:(UIView *)stackItem atIndex:(NSUInteger)index
{
if(!stackItem || index > _stackViewItems.count)return;
if(index == 0)
[self addView:stackItem
belowView:self
aboveView:_stackViewItems.count>0?_stackViewItems.firstObject:self];
else if(index==_stackViewItems.count)
[self addView:stackItem
belowView:_stackViewItems[index-1]
aboveView:self];
else
[self addView:stackItem
belowView:_stackViewItems[index-1]
aboveView:_stackViewItems[index]];
}
//-----------------------------------------------------------------//
#pragma mark - Constraining Views
//-----------------------------------------------------------------//
-(void)addView:(UIView *)view belowView:(UIView *)viewAbove aboveView:(UIView *)viewBelow {
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
NSArray * defaultConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[view]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)];
NSLayoutConstraint * upperConstraint,* lowerConstraint;
if(viewAbove==self) {
[self removeConstraint:_topConstraint];
upperConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[view]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)].firstObject;
_topConstraint = upperConstraint;
}
else
upperConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewAbove]-0-[view]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view,viewAbove)].firstObject;
if(viewBelow==self) {
[self removeConstraint:_bottomConstraint];
lowerConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[view]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)].firstObject;
_bottomConstraint = lowerConstraint;
}
else
lowerConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[view]-0-[viewBelow]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view,viewBelow)].firstObject;
[self addConstraints:defaultConstraints];
[self addConstraints:#[upperConstraint,lowerConstraint]];
[_stackViewItems addObject:view];
}
#end
I have uploaded the files here
IEScrollContentView.h
IEScrollContentView.h.m

UICollectionView in embedded view controller does NOT call cellForItemAtIndexPath

I have 2 view controllers embedded in a view vertically. View controller A (uploader), and B (docList).
B contains a UICollectionView
All, methods except cellForItemAtIndexPath inside the datasource of the collection view get called correctly, and i double checked everything. There is 1 section. There are more than 0 rows. The size of the rows I return is smaller than the collection view, etc
Here's a diagram to illustrate the setup:
My issue is:
Unless i turn on setTranslatesAutoresizingMaskIntoConstraints to YES, cellForItemAtIndexPath will never be called. If i set that property to YES on the View Controller B's view, then it does get called. But the layout is then screwed up, because I am not using springs and struts. We only use constraints here.
Do you know what i can be doing wrong when embedding the view controller that contains the UICollectionView?
Here is the code that embeds the two view controllers' views, and sets them as child controllers:
- (MFFormBaseCell *)cellForComponent
{
self.cell = [[MFFormBaseCell alloc] initWithFrame:CGRectZero];
[self.cell addSubview: uploader.view];
[self.cell addSubview: docList.view];
UIView* uploaderView = uploader.view;
UIView* docListView = docList.view;
NSMutableArray* tempConstraints = [[NSMutableArray alloc]init];
[tempConstraints addObjectsFromArray:
[NSLayoutConstraint constraintsWithVisualFormat: #"V:|-8-[uploaderView]-1-[docListView]-8-|"
options: NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views: NSDictionaryOfVariableBindings(uploaderView, docListView)]];
[tempConstraints addObjectsFromArray:
[NSLayoutConstraint constraintsWithVisualFormat: #"H:|-[uploaderView]-|"
options: NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views: NSDictionaryOfVariableBindings(uploaderView)]];
[tempConstraints addObjectsFromArray:
[NSLayoutConstraint constraintsWithVisualFormat: #"H:|-[docListView]|"
options: NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views: NSDictionaryOfVariableBindings(docListView)]];
uploaderConstraints = [tempConstraints copy];
[self.cell addConstraints: uploaderConstraints];
[self.embedder addChildViewController:uploader];
[uploader didMoveToParentViewController:self.embedder];
[self.embedder addChildViewController:docList];
[docList didMoveToParentViewController:self.embedder];
docList.view.frame = self.cell.bounds;
return self.cell;
}
And here is the code from View Controller B, that sets up the UICollectionView and a vertical flow layout for it.
- (void)modelDidLoad
{
_dataSource = [[MFCardDataSource alloc] initWithData: self.cardModel];;
UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[aFlowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
_collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:aFlowLayout];
[_collectionView setDelegate: self];
[_collectionView setDataSource:_dataSource];
[_collectionView setBackgroundColor:[UIColor clearColor]];
for (NSString* type in [MFCardCollectionModel typeArray])
[_collectionView registerClass:[MFImageCard class] forCellWithReuseIdentifier: type];
[_collectionView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:_collectionView];
[self registerConstraintsForView:_collectionView];
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
[super modelDidLoad];
}
And the contents of registerConstraintsForView:
-(void) registerConstraintsForView:(UIView*)collectionView
{
NSDictionary* metrics = #{ #"padding": #PADDING };
NSDictionary* views = NSDictionaryOfVariableBindings(_collectionView);
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-padding-[_collectionView]-padding-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:metrics
views:views]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-padding-[_collectionView]-padding-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:metrics
views:views]];
}
I got around the problem by Subclassing UICollectionView, and using the subclass instead.
On the subclass I overrode 2 methods:
- (void) setContentSize:(CGSize)contentSize
{
CGSize origSize = self.contentSize;
[super setContentSize:contentSize];
if (!CGSizeEqualToSize(contentSize, origSize))
{
[self invalidateIntrinsicContentSize];
}
}
- (CGSize) intrinsicContentSize
{
return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}
Then, i have a method inside my view controller which contains the Collection View, which calculates the required Height needed for the collection view.
I then call setContentSize with the calculated height on my subclass of Collection View, and that makes sure that it returns an intrinsicContentSize as tall as it needs to be to show all the card records inside it.

Keyboard not appear when touch over UITextField

I created a simple UIView with a UILabel and a UITextField inside of this UIView, this appear fine in the screen but when I touch over the UITextField, the keyboard doesn't appear.
I'm using autolayout programmatic and I'm think the problem is something about UIVIew frame.
I made a simple code to simulate this problem, the most relevant part of code is:
FormInput interface.
//FormInput.h
#import <UIKit/UIKit.h>
#interface FormInput : UIView
#property (nonatomic, strong) NSString *title;
#end
FormInput implementation
// FormInput.m
#import "FormInput.h"
#interface FormInput () <UITextFieldDelegate>
#property (nonatomic, strong) UILabel *label;
#property (nonatomic, strong) UITextField *field;
- (void)setuSubviews;
#end
#implementation FormInput
- (id)init
{
self = [super init];
if(!self) return nil;
[self setuSubviews];
return self;
}
- (void)setuSubviews
{
NSDictionary *views = #{
#"label":self.label,
#"field":self.field
};
[self addSubview:self.label];
[self addSubview:self.field];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[label(<=120)]-2-[field]-0-|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[label]" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[field]" options:0 metrics:nil views:views]];
}
#pragma mark - Lazy properties
- (UILabel *)label
{
if(!_label)
{
_label = [UILabel new];
_label.textColor = [UIColor blackColor];
_label.translatesAutoresizingMaskIntoConstraints = NO;
}
return _label;
}
- (UITextField *)field
{
if(!_field)
{
_field = [UITextField new];
_field.translatesAutoresizingMaskIntoConstraints = NO;
_field.font = [UIFont systemFontOfSize:16];
_field.placeholder = #"enter text here";
_field.keyboardType = UIKeyboardTypeDefault;
_field.keyboardAppearance = UIKeyboardAppearanceDark;
_field.returnKeyType = UIReturnKeyDone;
_field.clearButtonMode = UITextFieldViewModeWhileEditing;
_field.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
}
return _field;
}
#pragma mark - Properties setters
- (void)setTitle:(NSString *)title
{
_title = title;
self.label.text = title;
}
#end
View controller
#import "ViewController.h"
#import "FormInput.h"
#interface ViewController ()
#property (nonatomic, strong) FormInput *username;
#property (nonatomic, strong) FormInput *email;
- (void)setupLayout;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// [self setupLayout];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setupLayout];
}
#pragma mark - Lazy properties
- (FormInput *)username
{
if(!_username)
{
_username = [FormInput new];
_username.translatesAutoresizingMaskIntoConstraints = NO;
_username.title = #"Username:";
}
return _username;
}
- (FormInput *)email
{
if(!_email)
{
_email = [FormInput new];
_email.translatesAutoresizingMaskIntoConstraints = NO;
_email.title = #"Email:";
}
return _email;
}
#pragma mark - Layout setup
- (void)setupLayout
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.translatesAutoresizingMaskIntoConstraints = NO;
[btn setTitle:#"Button" forState:UIControlStateNormal];
UITextField *field = [UITextField new];
field.translatesAutoresizingMaskIntoConstraints = NO;
field.placeholder = #"Placeholder";
[self.view addSubview:self.username];
[self.view addSubview:self.email];
[self.view addSubview:btn];
[self.view addSubview:field];
NSDictionary *views = #{
#"username" : self.username,
#"email" : self.email,
#"btn" : btn,
#"field" : field
};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[username]-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[email]-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[btn]-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[field]-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-30-[username]-30-[email]-30-[btn]-30-[field]" options:0 metrics:nil views:views]];
}
#end
Make sure parent containers of the text field in question have "User Interaction Enabled" checked. Maybe you checked it for the text field but not your viewcont?
My reputation is "1" and I can't poste an image, but this check button should be in the interaction part...
Ok I found the reason.
The height of each view in the constraint below still intrinsic:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-30-[username]-30-[email]-30-[btn]-30-[field]" options:0 metrics:nil views:views]]
Thus, just add a vertical height for each view in the constraint visual format below.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-30-[username(20)]-30-[email(20)]-30-[btn]-30-[field]" options:0 metrics:nil views:views]]
Not trivial but this makes the views inside added view to responds touch actions.
Thanks!

Forcing a subview background color change when rotating device and autolayout

I have a problem with a custom view and autolayout. To make things simple I will use two UILabels, the first one should change its background color when the device rotate. The problem is that it doesn't do it! Any hint?
Thanks!
Nicola
- (id)init
{
self = [super init];
if (self) {
//Add the subviews to the mainView
[self.view addSubview:self.label1];
[self.view addSubview:self.label2];
//Autolayout
//Create the views dictionary
NSDictionary *viewsDictionary = #{#"header":self.label1,
#"table": self.label2};
//Create the constraints using the visual language format
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat: #"H:|[header]|"
options:0
metrics:nil
views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat: #"H:|[table]|"
options:0
metrics:nil
views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[header(==50)][table]|"
options:0
metrics:nil
views:viewsDictionary]];
}
return self;
}
-(UIView*) label1
{
_label1 = [UILabel alloc] init];
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)){
_label1.backgroundColor = [UIColor redColor];
}else{
_label1.backgroundColor = [UIColor greenColor];
}
_label1.translatesAutoresizingMaskIntoConstraints=NO;
return _label1;
}
-(UIView*) label2
{
_label2 = [UILabel alloc] init];
_label2.backgroundColor = [UIColor yellowColor];
_label2.translatesAutoresizingMaskIntoConstraints=NO;
_return label2;
}
-(BOOL) shouldAutorotate
{
return YES;
}
-(NSUInteger) supportedInterfaceOrientations
{
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
//I am on a pad
return UIInterfaceOrientationMaskAll;
} else {
//I am on a Phone
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//I expect the label1 to change its background color
[self.view setNeedDisplay];
}
If you move the code related to [self.label1 setBackgroundColor:] to the delegate method didRotateFromInterfaceOrientation:, it should work better. Also, in your custom getters, you are allocating a new label every time you access the method. In most situations it's preferable to check if the ivar is not nil at the beginning, and returning the ivar, instead for allocating a fresh label.

Resources