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
Related
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:
I have a ViewController that adds an UIView, SpeciesImageView as a subview in viewDidLoad and set constraints in viewWillLayoutSubviews.
SpeciesImageView does not have a nib file. When we create speciesImageView in viewDidLoad, it calls initWithFrame in SpeciesImageView class.
This works fine (in both landscape and portrait) until the phone rotates. I tried setting the constraints as speciesImageView.frame.size.width, but that doesn't work because initWithFrame isn't called when the orientation changes, so the height/width of the speciesImageView remains unchanged.
On the other hand, using screenRect doesn't change the actual size of the UIView, it changes its size within the superview. So in other words, I haven't found a way to change the size of the actual speciesImageView on orientation change.
And for reasons lost to me, it gets completely messed up when you rotate it back to the original position.
- (void)viewDidLoad
{
self.tabBarController.tabBar.hidden=YES;
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
self.navigationController.navigationBar.hidden = NO;
//self.navigationController.navigationBar.translucent = YES;
UIImage *plantinfo;
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
plantinfo = [UIImage imageNamed:#"plantinfo_frame.png"];
} else {
plantinfo = [UIImage imageNamed:#"plantinfo.png"];
}
UIBarButtonItem *tempButton = [[UIBarButtonItem alloc] initWithImage:plantinfo
style:UIBarButtonItemStylePlain
target:self
action:#selector(toggleText:)];
self.navigationItem.rightBarButtonItem = tempButton;
[tempButton release];
self.title = theSpecies.scientificName;
//[self.navigationItem.backBarButtonItem setTitle:#""];
self.navigationItem.backBarButtonItem.title = #"";
infoViewSegmentedControl.backgroundColor = [UIColor blackColor];
webView.backgroundColor = [UIColor blackColor];
_activityIndicator.hidden = YES;
[webView setOpaque:YES];
webView.delegate = self;
// Do double justification
[webView loadHTMLString:[self formatHTML:theSpecies] baseURL:nil];
showingInfoView = NO;
//
// Resize containerView, infoview according to iphone 5 screen size.
//
infoView.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
CGPoint screenOrigin = [[UIScreen mainScreen] bounds].origin;
CGSize viewSize = [[UIScreen mainScreen] bounds].size;
CGPoint origin = infoView.frame.origin;
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
infoView.frame = CGRectMake(screenOrigin.x,
screenOrigin.y + statusBarFrame.size.height,
viewSize.width,
viewSize.height - origin.y - statusBarFrame.size.height);
speciesImageView = [[SpeciesImageView alloc]
initWithFrame:CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height)];
} else {
infoView.frame = CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height - origin.y - statusBarFrame.size.height);
speciesImageView = [[SpeciesImageView alloc]
initWithFrame:CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height - statusBarFrame.size.height)];
}
speciesImageView.delegate = self;
[containerView addSubview:speciesImageView];
managedObjectContext = [(LeafletAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self parseImageURLArray];
}
-(void)viewWillLayoutSubviews{
if(speciesImageView.window != nil){
CGRect screenRect = [[UIScreen mainScreen] bounds];
speciesImageView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConst = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:screenRect.size.width];
NSLayoutConstraint *heightConst = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:screenRect.size.height];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraints:#[widthConst, heightConst, bottomConstraint, rightConstraint]];
}
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
imageScrollView = [[UIScrollView alloc] initWithFrame:frame];
imageScrollView.delegate = self;
imageScrollView.backgroundColor = [UIColor blackColor];
[self addSubview:imageScrollView];
imageScrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConst = [NSLayoutConstraint constraintWithItem:imageScrollView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:imageScrollView.frame.size.width];
NSLayoutConstraint *heightConst = [NSLayoutConstraint constraintWithItem:imageScrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:imageScrollView.frame.size.height];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint
constraintWithItem:imageScrollView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:imageScrollView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
[self addConstraints:#[widthConst, heightConst, bottomConstraint, rightConstraint]];
}
return self;
}
If you want your views to adjust to the size of the superview,
then you need something like this (set the margin to whatever you like):
CGFloat margin = 0;
NSString * visualFormatH = [NSString stringWithFormat:#"|-(%f)-[speciesImageView]-(%f)-|", margin, margin];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatH
options:0
metrics:Nil
views:#{#"speciesImageView": speciesImageView}]];
NSString * visualFormatV = [NSString stringWithFormat:#"V:|-(%f)-[speciesImageView]-(%f)-|", margin, margin];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatV
options:0
metrics:Nil
views:#{#"speciesImageView": speciesImageView}]];
Now speciesImageView will adjust its frame whenever the superview frame changes.
Here is a generic example:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView * sampleView = [UIView new];
sampleView.backgroundColor = [UIColor redColor];
sampleView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:sampleView];
CGFloat margin = 0;
NSString * visualFormatH = [NSString stringWithFormat:#"|-(%f)-[sampleView]-(%f)-|", margin, margin];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatH
options:0
metrics:Nil
views:#{#"sampleView": sampleView}]];
NSString * visualFormatV = [NSString stringWithFormat:#"V:|-(%f)-[sampleView]-(%f)-|", margin, margin];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatV
options:0
metrics:Nil
views:#{#"sampleView": sampleView}]];
}
Auto Layout Getting Started
Auto Layout Visual Format Documentation
I have problem with adding subview to UITextView using autoLayout:
in SubTextView : UITextView .m file:
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
_baseline.translatesAutoresizingMaskIntoConstraints = NO;
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints
{
[super updateConstraints];
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
But, this do not work... the self.baseline's frame is always (0, 0, 0, 0)...
Anyone can help?
Edited:
I set a break point at layout subviews, at that time, when layoutSubviews the subviews are inited, and the UITextView is :
<InventoryOverviewListingNameTextView: 0x7fd92b2a1800;
baseClass = UITextView;
frame = (234 36; 399 70.2);
text = '11111111';
clipsToBounds = YES;
gestureRecognizers = <NSArray: 0x7fd92cbd8ca0>;
layer = <CALayer: 0x7fd92cb08900>;
contentOffset: {-0, 10};
contentSize: {399, 70.199997425079346}>
But the frame of the baseline is:
<UIView: 0x7fd92cb90710;
frame = (0 -10; 399 0.5);
layer = <CALayer: 0x7fd92cb907e0>>.
Do not know how to make the y = -10 to y = 69.7.
I feel it is a little like scrollView, to which I solve by add a UIView as contentView on UIScrollView, and UIScrollView itself is added on another UIView. Then I set the contentView's top to scrollView's top, contentView's heigh, and !!!(this is important or autoLayout won't work properly)
contentView's left and right to UIView rather than scrollView.
But I do not see it works for UITextView...
Anyone can help?
The frame will always be (0,0,0,0) because that is the default one that comes whit [[UIView alloc] init].
Have you tried defining the view setting a frame?
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
//Add a frame, does not matter the size
_baseline = [[UIView alloc] initWithFrame:CGRectMake(0,0,20,20)];
//Just to make sure it's there let us give it a color
[_baseline setBackgroundColor:[UIColor green]];
[self addSubview:_baseline];
//Don't run this line just yet.
//[self setNeedsUpdateConstraints];
}
}
If that works, then we found the problem, if it does not, maybe the problem is on the - (void)updateConstraints method.
UPDATE:
So, I sat down and ran your code, I got a few crashes but eventually I managed to make the black box apear inside the text view. I found that the box was not instantiated when the view was looking to layout its subviews, so I tinkered around and here goes, this works, it throws a few constraints warnings but I guess you can work those by yourself.
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
if (!_baseline) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints {
[super updateConstraints];
if (_baseline) {
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
}
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];
I am currently having trouble with my iOS app and Autolayout. I want to let the user scroll photos from Tumblr in a UIScrollView (similar to Photos app, when scrolling your library). So when my ImageViewController gets on screen, it has an array of posts but no data for the images yet. My problem is the following :
In my scrollView, I want to add as many UIImageViewS as needed, and I would like them to be all the same size. How should I do it? I tried many (probably bad designed :-/) ways, like initwithframe: with auto layout on and keeping a reference to them in a NSMutableArray...
My goal now is to add them to my scrollView in viewDidLoad and have correct constraints set.
Thanks for your help and sorry for my poor English
EDIT
OK I solved my problem: I used a nice scrollView and set its constraints with auto layout in viewWillAppear
Here is the code for those interested (sorry for layout) :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self downloadPhotos];
[self.scrollView setContentOffset:CGPointMake(self.selectedImageIndex * self.scrollView.frame.size.width, 0) animated:NO];
// Add UIImageViewS to self.scrollView with constraints and so on...
NSMutableDictionary *viewsDictionnary = [[NSMutableDictionary alloc] init];
NSMutableString *imageViewsString = [[NSMutableString alloc] init];
NSMutableArray *imageViews = [[NSMutableArray alloc] init];
for (int i = 0; i < self.fetchedResultsController.fetchedObjects.count; i++) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.scrollView.bounds];
imageView.image = [UIImage imageNamed:#"placeholder_imageView"];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:imageView];
[imageViews addObject:imageView];
[viewsDictionnary setObject:imageView forKey:[NSString stringWithFormat:#"imageView%d", i]];
[imageViewsString appendString:[NSString stringWithFormat:#"[imageView%d]", i]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
}
self.imageViews = imageViews;
NSString *horizontal = [NSString stringWithFormat:#"H:|%#|", imageViewsString];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:horizontal options:0 metrics:0 views:viewsDictionnary]];
[self.scrollView setContentOffset:CGPointMake(self.selectedImageIndex * self.scrollView.frame.size.width, 0) animated:NO];
}
I recommend not using a scrollView but using a collectionView instead (iOS 6 and above). You will probably insist that you need to use iOS 5 or iOS 4. Make things easy for yourself, collectionViews make memory management easy and if you plan on loading a lot of photos in view, just go with collection views. You can then create a collectionViewCell which will have a UIImageView. The UIImageView will be only one size, you can then set its image scaling propery to scale to fit so no matter what shape or size, the images will fit in the image view and not be distorted. Do some research on colletion views, heck, there probably is a tutorial you can use to load images from the internet and display them using a collection view.
-(void)viewDidLoad
{
[super viewDidLoad];
float posX = 0.0 , poxY = 0.0;
float sizeWidth = 100 , sizeHeight = 200;
float scrollContentWidth = 320.0;
scrollview.showsHorizontalScrollIndicator=YES;
scrollview.scrollEnabled=YES;
scrollview.userInteractionEnabled=YES;
self.scrollView.pagingEnabled = TRUE;
for(int i=0;i<[imageArray count];i++)
{
NSString *imageURL = [imageArray objectAtIndex:i];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(posX,posY,sizeWidth,sizeHeight)];
[imageView setImage:[UIImage imageWithContentsOfFile:imageURL]];
[self.scrollView addSubview:imageView];
[imageView addConstraint:
[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:imageView
attribute:NSLayoutAttributeWidth
multiplier:1
constant:100]];
[imageView addConstraint:
[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:imageView
attribute:NSLayoutAttributeHeight
multiplier:1
constant:200]];
[self.scrollView setContentSize:CGSizeMake(480.0,scrollContentWidth)];
scrollContentWidth=scrollContentWidth+320.0;
posX = posX + 320.0;
}
}
Hope it will help you.