IOS/Objective-C: Center Line under Button Programmatically - ios

I am trying to do a simple line under three buttons to create a Tabs effect. While I could do this in Storyboard, I'd like to do it in code and ideally make the line a subview of an element already in the view to avoid having to give it special constraints.
My approach has been to create a UILabel with no text and a background color. I can make the UILabel a subview of the View, however, that does not attach it to the bottom of the buttons.
On the other hand, if I make the UILabel a subview of one of the buttons, I can't see it.
Can anyone suggest a simple way to do this?
[centerButton setTitle:#"Favorites" forState:UIControlStateNormal];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 120, 280, 2)];
label.backgroundColor = [UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
label.numberOfLines = 1;
label.text = #"";
[self.view addSubview: label];
Thanks in advance for any suggestions.
Edit:
I tried adding some NSLayoutConstraints Programmatically:
NSLayoutConstraint *con3 = [NSLayoutConstraint
constraintWithItem:label attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:centerButton
attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[label addConstraints:#[con3]];
//tried with and without
[self.view layoutIfNeeded];
This returned the exception:
[LayoutConstraints] The view hierarchy is not prepared
for the constraint: <NSLayoutConstraint:0x174e85500 V:
[UIButton:0x100b0baf0'Now']-(0)-[UILabel:0x108555160]
(inactive)>
When added to a view, the constraint's items
must be descendants of that view (or the view itself).
This will crash if the constraint needs to be resolved
before the view hierarchy is assembled.
Break on -[UIView(UIConstraintBasedLayout)
_viewHierarchyUnpreparedForConstraint:] to debug.
The button created in storyboard is a subview of self.view and I add the label to self.view as a subview in code so not sure why the exception is occurring.

I wonder from the error message if you're possibly attempting to add the NSLayoutConstraint prior to the redLabel view being added to it's parentView (or too early in the lifecycle). In any event, I think this is pretty close to what you're attempting to accomplish:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
UIView *buttonSuperview = [clickedButton superview];
if (buttonSuperview != nil) {
[self _putRedLineWithHeight:3.0f atTheBottomOfView:buttonSuperview animate:YES];
}
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfView:(UIView *)viewToPutUnder animate:(BOOL)animate {
// remove our previous red line
if (self.previousRedLabel) {
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to check if the superView == viewToPutUnder
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[viewToPutUnder addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to parentView's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == parentView's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now the bottom constraint (place it at the bottom of the parent view)
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[viewToPutUnder addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
Example of Animated:
And one of Non-Animated:
EDITED ANSWER TO HANDLE THE CASE IF EACH BUTTON ISN'T IN IT'S OWN SUPERVIEW
Adjusted for if all buttons are in one superview (width based on button width, center to button center, and pinning the top of the label to the bottom of the button)
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to store the previous clicked button and check whether it's the same button
[self _putRedLineWithHeight:3.0f atTheBottomOfButton:clickedButton animate:YES];
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate {
UIView *buttonSuperview = button.superview;
NSAssert(buttonSuperview != nil, #"Button has to have a superview");
// remove our previous red line
if (self.previousRedLabel) {
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[buttonSuperview addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to button's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == button's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now pin the top of the label to the bottom of the button
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[buttonSuperview addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
#end
Animated:
Not Animated:

Related

Constraints programmatically with Objective C

I don't know what I'm doing wrong: I'm creating a UIView that occupies all the screen (it has already constraints) and then, programmatically I'm creating an UI Image View:
_panel = [[UIImageView alloc] initWithImage:[self loadImageForKey:#"registerPanel"]];
_panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES,
[self.scrollView addSubview:_panel];
And here it comes the problem: I'm adding constraints to the panel I created but it crashes (I'm doing it on the ViewWillAppear):
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
Error message:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
You can constrain a scrollView's subview to the scrollView's parent (self.view in this case), but that's probably not what you want.
Edit: For clarification, the reason you were getting the error was because you initialize your constraints:
toItem:self.view
and then you try to add them:
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
You want to add them to the toItem object:
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
Again, you probably don't want to center _panel in the main view, but this will compile and run:
#import "AddPanelScrollViewController.h" /// just default .h
#interface AddPanelScrollViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *panel;
#end
#implementation AddPanelScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
_scrollView = [UIScrollView new];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_scrollView];
[_scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20.0].active = YES;
[_scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20.0].active = YES;
[_scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0].active = YES;
[_scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0].active = YES;
_scrollView.backgroundColor = [UIColor blueColor];
_panel = [UIImageView new];
// required
_panel.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:_panel];
// frame will be ignored when using auto-layout / constraints
// _panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES;
_panel.backgroundColor = [UIColor redColor];
// _panel needs width and height constraints
[_panel.widthAnchor constraintEqualToConstant:100.0].active = YES;
[_panel.heightAnchor constraintEqualToConstant:100.0].active = YES;
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
// if constraints are releated to "self.view" that's where they need to be added
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
}
First you can't create constraints between panel & self.view because there is no common parent , instead you want to create them with the scrollview
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
[_scrollView addConstraint:centreHorizontallyConstraint];
[_scrollView addConstraint:centreVerticalConstraint];
Also both constraints are centerX , you need also width & height , or better top , leading , trailing and bottom to scrollView ,,, with width and height static or proportional to self.view
//
Also for any view you want to add constraints programmatically you must set
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints: NO];
[self.panel setTranslatesAutoresizingMaskIntoConstraints: NO];

iOS Objective C ScrollView

I am trying to implement a UIScrollView and load it with images from an array of images in Xcode using objective-C, each image in the UIScrollView must be full screen both in portrait and in the landscape mode.I have been able to make it work in portrait mode but not in landscape mode. It should be fullscreen in all iOS device sizes. Below is the code I have written so far. I have UIScrollView in my storyboard, a button and a label. Any answer or pointing to a tutorial that implements this will be appreciated. Thanks in advance.
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat widthInPixel = screen.size.width;
CGFloat heightInPixel = screen.size.height;
float increaseAmount = widthInPixel;
self.imageScrollView.contentMode = UIViewContentModeScaleAspectFit;
self.imageScrollView.pagingEnabled = YES;
[self.imageScrollView setAlwaysBounceVertical:NO];
[self.imageScrollView setAlwaysBounceHorizontal:NO];
imageViews = [[NSMutableArray alloc] init];
self.imageScrollView.clipsToBounds = YES;
NSInteger imageNumbers = [self.images count];
UIImageView *image;
for(NSInteger i = 0; i < imageNumbers; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
image = [[UIImageView alloc] initWithFrame:
CGRectMake(xOrigin, 0,
widthInPixel,
self.imageScrollView.frame.size.height)];
image.contentMode = UIViewContentModeScaleAspectFit;
image.clipsToBounds = YES;
image.image = self.images[i];
[image setAutoresizingMask:
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight];
[self.imageScrollView addSubview:image];
}
self.imageScrollView.contentSize = CGSizeMake(image.frame.size.width *
imageNumbers,
self.imageScrollView.frame.size.height);
You really should learn how to use auto-layout and constraints. Use your favorite search engine and search for ios auto layout tutorial ... you'll find plenty of material.
Edit:
Scroll offset is an inherent issue when rotating a scroll view with paging enabled. See the edit below for an implementation of viewWillTransitionToSize.
But, to give you an idea, this will do what you want, including auto-resizing on device rotation:
//
// ViewController.m
// ScrollingImages
//
// Created by Don Mag on 7/19/18.
//
#import "ViewController.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIScrollView *theScrollView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *images = #[#"a", #"b", #"c", #"d", #"e"];
[_theScrollView setPagingEnabled:YES];
[_theScrollView setAlwaysBounceVertical:NO];
[_theScrollView setAlwaysBounceHorizontal:NO];
// we'll use this to hold the most recently added view
UIImageView *prevImageView = nil;
for (int i = 0; i < images.count; i++) {
// create an image view with named image from array
UIImageView *v = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[i]]];
// we want to use auto-layout
v.translatesAutoresizingMaskIntoConstraints = NO;
// we want aspect-fit
v.contentMode = UIViewContentModeScaleAspectFit;
// add it to the scroll view
[_theScrollView addSubview:v];
// set width and height constraints equal to the scroll view
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0.0] setActive:YES];
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0.0] setActive:YES];
if (i == 0) { // if it's the first image
// add top constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0] setActive:YES];
// and leading constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0] setActive:YES];
} else {
// constrain leading to previous image view trailing
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:prevImageView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0] setActive:YES];
// and top to previous image view top
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:prevImageView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0] setActive:YES];
}
if (i == images.count - 1) { // if it's the last image
// add trailing constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0] setActive:YES];
// and bottom constraint
[[NSLayoutConstraint
constraintWithItem:v
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_theScrollView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0] setActive:YES];
}
// reference to most recently added view
prevImageView = v;
}
}
- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// execute before rotation
// get the "index" of the current image in the scroll view
NSUInteger idx = (unsigned)(_theScrollView.contentOffset.x / _theScrollView.frame.size.width);
[coordinator animateAlongsideTransition:^(id _Nonnull context) {
// execute during rotation
// update the scroll view's contentOffset, based on the "index"
self.theScrollView.contentOffset = CGPointMake(idx * self.theScrollView.frame.size.width, 0);
} completion:^(id _Nonnull context) {
// execute after rotation (if additional code wanted)
}];
}
#end
You can download a working example project here: https://github.com/DonMag/ScrollingImages

How to get constraints from UIView Programmatically

I want get UILabel constraints from UIView but I can't get any constraints.
I set the constraints in CustomView.m like this:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_titleLabel = [UILabel new];
[self addSubview:_titleLabel];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *titleLabelBottom = [NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self addConstraints:#[titleLabelBottom]];
...more code
}
in ViewController.m
CustomView *view = [CustomView alloc] initWithFrame:viewFrame];
NSLog(#"%#",view.titleLabel.constraints); // nil
you can get constraints in NSArray like,
NSArray *constraintArr = self.backButton.constraints;
NSLog(#"cons : %#",constraintArr);
And you can set instance variable like,
NSLayoutConstraint *titleLabelBottom;
and then use,
titleLabelBottom = [NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self addConstraints:#[titleLabelBottom]];
so, you can use titleLabelBottom anywhere in class.
hope this will help :)
You are getting nil because the constraint has not been created yet.
Try logging your constraints in:
- (void)viewDidLayoutSubviews;
Assumming that constraint is essential to your CustomView, you should create that constraint in your initWithFrame method method.

Autoresizing of subviews ( UICollectionView) on orientation change

I have Created a CustomView ganttChartView and added it from storyboard . Now on ganttChartView I have a UICollection View which will represent timeLine and added programatically.
// Initialize GanttChat View from Interface Builder or Storyboard File
-(id)initWithCoder:(NSCoder *)aDecoder
{
self= [super initWithCoder:aDecoder];
if (self) {
self.timeLineHeight =KMinTimeLineCellHeight;
self.timeLineCellWidth=kMinTimeLineCellWidth;
self.backgroundColor = [UIColor redColor];
self.autoresizesSubviews = YES;
}
return self;
}
-(void)reloadTimelineView
{
[self initializeTimeLineView];
[self.timeLineCollectionView reloadData];
}
-(void) initializeTimeLineView
{
// Initialization of StartDate End Date and DateMode Property
[self initializeTimeLineDates];
// Creating Layout for Collection view
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
CGSize cellSize =CGSizeMake(self.timeLineCellWidth, self.timeLineHeight) ;
flowLayout.itemSize = cellSize ;
flowLayout.minimumInteritemSpacing= 1.0f;
flowLayout.minimumLineSpacing=5.0f;
CGRect timeLineFrame =CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.timeLineHeight);
// Initialization of CollectionView for TimeLine
self.timeLineCollectionView = [[UICollectionView alloc] initWithFrame:timeLineFrame collectionViewLayout:flowLayout];
[self.timeLineCollectionView registerClass:[A3TimeLineCollectionViewCell class] forCellWithReuseIdentifier:timeLineCell_ID];
self.timeLineCollectionView.backgroundColor = self.timeLineBackgroundColor;
// Initialization of CollectionView DataSource and Delegate with Start Date and End date and DateMode
self.timeLineDataSource = [[A3GanttChartTimeLineDelegate alloc] initWithDate:self.startDate andDate:self.endDate withMode:self.dateType];
self.timeLineDataSource.gantChartView = self;
self.timeLineDataSource.timeLineEachCellColor = self.timeLineEachCellColor;
self.timeLineCollectionView.delegate=self.timeLineDataSource;
self.timeLineCollectionView.dataSource=self.timeLineDataSource;
[self addSubview:self.timeLineCollectionView];
}
Now From Storyboard I have disabled AutoLayout option and from size Inspector of ganttChartView I have set top and left corner fixed so that it resized after orientation change .
Now the problem is that TimeLineCollection View is not resizing on orientation change to Landscape. As its added programatically So What I need to do make it resized on orientation change .
Profit Mode
Landscape Mode
You need to set also right corner fixed in order to resize after orientation change
self.timeLineCollectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleBottomMargin;
I have fixed this issue using NSLayoutConstraint .
// NSLayoutConstraint for making same width of timelineCollectionView with the GanttChart
NSLayoutConstraint *timeLineCollectionViewWidth =[NSLayoutConstraint
constraintWithItem:self.timeLineCollectionView
attribute:NSLayoutAttributeWidth
relatedBy:0
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self addConstraint:timeLineCollectionViewWidth];
// NSLayoutConstraint for making same left position of timelineCollectionView with the GanttChart
NSLayoutConstraint *timeLineCollectionViewLeft = [NSLayoutConstraint
constraintWithItem:self.timeLineCollectionView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeft
multiplier:1.0f
constant:0.f];
[self addConstraint:timeLineCollectionViewLeft];
// NSLayoutConstraint for seting height of timelineCollectionView
NSLayoutConstraint *heightConstraint =
[NSLayoutConstraint constraintWithItem:self.timeLineCollectionView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:self.timeLineHeight];
[self.timeLineCollectionView addConstraint:heightConstraint];

Position one UIlabel under another UILabel (top label has varying height)

I have two UILabels inside of a XIB, and I want to position one label underneath of another label. That said, the top label's height (descriptionLabel) varies. Does anyone know how I can go about doing this? I feel like I've tried everything.
Here is the code for my Labels so far; I want to position my second label (bodyLabel) about 25 pixels below descriptionLabel (regardless of how long descriptionLabel is):
CGRect frame = descriptionLabel.frame;
frame.origin.y=400;//pass the cordinate which you want
frame.origin.x= 12;//pass the cordinate which you want
descriptionLabel.frame= frame;
CGRect frame2 = bodyLabel.frame;
bodyLabel.frame= frame;
do this in viewDidLayoutSubviews;
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
/* set label1's frame first */
CGRect newFrame = _label2.frame;
newFrame.origin.y = CGRectGetMaxY(_label1.frame)+25;
_label2.frame = newFrame;
}
CGRectGetMaxY takes the frame's origin into account when returning a value. keep in mind that frames are not yet set for views if you're doing things in loadView or viewDidLoad, this could be why things keep ending up with a 0 origin - they are still 0 at that time.
Suppose you have two UILabels. Say, firstLabel and secondLabel. Suppose you have set the first frame like so:
firstLabel.frame = CGRectMake(0,0,50,50);
If your first frame dynamically changes its height, and if want your secondLabel to be always under the first, you can set the y coordinate of the secondLabel in such a way it is always under it. The code for it can be something like:
secondLabel.frame = CGRectMake(0,firstLabel.frame.size.height,50,50);
Using this, the y position of the secondLabel is dynamic and is dependent on the firstLabel's height.
In your case, the position of the bodyLabel can be :
CGRect bodyLabelFrame = CGRectMake(0,descriptionLabel.frame.size.height,50,50);
bodyLabel.frame = bodyLabelFrame;
Have you tried using autolayout? Using autolayout, this is how I might do this if the superview for the labels was superView:
// This is necessary to use autolayout
descriptionLabel.translatesAutoResizingMasksIntoConstraints = NO;
bodyLabel.translatesAutoResizingMasksIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(descriptionLabel, bodyLabel);
// this will pin the top of bodyLabel to the bottom of the descriptionLabel with a gap of 25px
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[descriptionLabel]-25-[bodyLabel]" options:nil metrics:nil views:views]];
You'll need to do it in code. I use auto-layout.
First, create two private NSLayoutContstraint variables for your two labels — you'll use these to adjust your label height when you set the text.
#interface CustomView ()
#property (strong, nonatomic) NSLayoutConstraint *firstLabelHeightCn;
#property (strong, nonatomic) NSLayoutConstraint *secondLabelHeightCn;
#end
Second, define the first labels X, Y, and width — the height will be set depending on the text you set in it.
NSLayoutConstraint *cnX;
NSLayoutConstraint *cnY;
NSLayoutConstraint *cnWidth;
// first label
_firstLabel = [[UILabel alloc] init];
_firstLabel.lineBreakMode = NSLineBreakByWordWrapping;
_firstLabel.numberOfLines = 0;
_firstLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_firstLabel];
cnX = [NSLayoutConstraint constraintWithItem:_firstLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:H_MARGIN];
cnY = [NSLayoutConstraint constraintWithItem:_firstLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:V_MARGIN];
cnWidth = [NSLayoutConstraint constraintWithItem:_firstLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:-H_MARGIN];
_firstLabelHeightCn = [NSLayoutConstraint constraintWithItem:_firstLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0 constant:0];
[self addConstraints:#[ cnX, cnY, cnWidth, _firstLabelHeightCn ]];
Third, define the second labels X, Y, and width off the first labels properties. For the Y position you'll want to set the second labels TOP to the first labels BOTTOM (+ any margin).
// second label
_secondLabel = [[UILabel alloc] init];
_secondLabel.lineBreakMode = NSLineBreakByWordWrapping;
_secondLabel.numberOfLines = 0;
_secondLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_secondLabel];
cnX = [NSLayoutConstraint constraintWithItem:_secondLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_firstLabel attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
cnY = [NSLayoutConstraint constraintWithItem:_secondLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_firstLabel attribute:NSLayoutAttributeBottom multiplier:1.0 constant:V_MARGIN];
cnWidth = [NSLayoutConstraint constraintWithItem:_secondLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:_firstLabel attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
_secondLabelHeightCn = [NSLayoutConstraint constraintWithItem:_secondLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0 constant:0];
[self addConstraints:#[ cnX, cnY, cnWidth, _secondLabelHeightCn ]];
Finally, create two methods to set the text for your two labels. These methods will take the incoming text, calculate the height, adjust your layout constraint constants, and then set the actual text in the label. Since you're using auto-layout once you change the text/height of the first label, the second label will automatically adjust.
- (void)setFirstText:(NSString *)firstText
{
_firstText = firstText;
if (_firstText.length) {
_firstLabelHeightCn.constant = [CustomView textHeight:_firstText width:self.bounds.size.width font:_firstLabel.font];
_firstLabel.text = _firstText;
} else {
_firstLabelHeightCn.constant = 0;
_firstLabel.text = nil;
}
}
- (void)setSecondText:(NSString *)secondText
{
_secondText = secondText;
if (_secondText.length) {
_secondLabelHeightCn.constant = [CustomView textHeight:_secondText width:self.bounds.size.width font:_secondLabel.font];
_secondLabel.text = _secondText;
} else {
_secondLabelHeightCn.constant = 0;
_secondLabel.text = nil;
}
}
Here is a real-life example:
ContextView.h
https://gist.githubusercontent.com/rosem/4fc7f9ed80c114ba45a0/raw/05f46c0340e1682823d6bbeb95f8b084ba4449d5/gistfile1.mm
ContextView.m
https://gist.githubusercontent.com/rosem/6d768776991569496ab6/raw/76ce4f47b3f86555ee4755e7d52d12511adcec27/gistfile1.m

Resources