Layout in UIScrollView - ios

I am new to UIScrollView, and they are driving me crazy.
I am trying to create a screen that has a title and some descriptive text at the top, and then a scroll view (with paging enabled) in the bottom two thirds of the screen. This scroll view will contain 1-3 images. Ideally, I'd like the first image to appear centered in the initial scroll view window, and then enable the user to view the other images by scrolling/paging horizontally, again ideally with each image centered in its 'page'.
The code below loads the images for 'item' from the Internet, creates a UIImageView for each, and inserts them into the scroll view. According to the frame calculations, I would expect the first image to be up against the left side of the scrollview, and then the other images to the right, with no space between them. But instead, the first image is shifted to the right, about half-way across the screen, and there are equally large spaces between each image.
I have turned off paging to simplify matters, and I have tried experimenting with tweaking the image frame values, trying to understand what's happening, but nothing works as I expect it to.
I am guessing that autolayout is messing with me. Can someone confirm that, and maybe give me some hints on how to get this scrollview under control?
Thanks in advance.
self.imageScrollView.contentSize = CGSizeMake(self.imageScrollView.frame.size.width * (imageFieldNames.count),
self.imageScrollView.frame.size.height);
self.imageScrollView.pagingEnabled = NO;
CGFloat xPos = 0.0;
for (NSString *field in imageFieldNames) {
NSString *name = [self.item valueForKey:field];
if (name.length > 0) {
UIImageView *iv = [[UIImageView alloc] init];
iv.contentMode = UIViewContentModeScaleAspectFit;
[self loadImage:iv withFile:name];
iv.frame = CGRectMake(xPos, 0.0,
self.imageScrollView.frame.size.width,
self.imageScrollView.frame.size.height);
[self.imageScrollView addSubview:iv];
xPos += self.imageScrollView.frame.size.width;
}
}

Related

UIButton background image used for animation not aligned across devices

I have a UIButton backgroundImage that I use to display a weather condition image when the loading is complete. I also create a UIImageView that replaces the UIButton to animate a series of images as a progress indicator.
My question: How can fix this animated UIImageView x-axis misalignment across multiple screen sizes?
Here's what the sequence looks like on 4.7" iPhone, the red box indicates the image I'm talking about:
First, the UIImageView animating as a progress indicator (imagine it spinning, alignment is correct)
Second, the download complete, the progress indicator replaced by a UIButton with a backgroundImage:
Third, the UIImageView animating on 4" iPhone (note misalignment on x-axis):
Fourth, the download complete, UIButton replaces it, aligned correctly:
Here's how the UIImageView *progressIndicator is configured.
Note that conditionButton is the UIButton with backgroundImage of the weather condition:
self.progressIndicator = [[UIImageView alloc] initWithFrame:self.conditionButton.frame];
self.progressIndicator.contentMode = UIViewContentModeCenter;
self.progressIndicator.animationImages = #[...long series of images...];
self.progressIndicator.animationDuration = 0.5f;
[self.conditionButton setBackgroundImage:[UIImage imageNamed:#"empty.png"] forState:UIControlStateNormal];
[self.progressIndicator startAnimating];
[self.view addSubview:self.progressIndicator];
I'm pretty sure the issue is with
self.progressIndicator = [[UIImageView alloc] initWithFrame:self.conditionButton.frame];
But I'm not sure how to resolve this.
The same problem occurs when I switch to 5.5" iPhone. I have no Auto Layout warnings, and the constraints that apply to the conditionButton are:
Align Center X to superview
Width = 94
Height = 94
Bottom and Top space to nearest neighbor = default
Any help would be greatly appreciated!

Autolayout Constraints on empty UIView do not work as expected

So I have setup a UIView that contains a UIScrollView (and child content view) that has sub views that are series of UILabels and UIViews that grow and shrink depending on the content contained in them, all using AutoLayout from the Storyboard. This works when I have something like Label - Label - Label - View w/o any issues, however if I put an empty UIView in-between two labels and insert sub views on the UIView, I'm not seeing the results I'm expecting. I have the following layout in a storyboard:
...where the teal and blue views are labels that grow to infinite height and the orange view (optionsPanel) is an empty UIVIew that I later inject sub views into. The rest of the stuff on the window is UILabels and UISegment controls. Between each row of views I have a Vertical Space constraint with a constant of 8. This all worked beautifully until I had to put in the empty UIView and programmatically inject sub views. The code I would expect to work would be something like (optionsPanel is the orange colored UIView)...
optionsPanel.translatesAutoresizingMaskIntoConstraints = YES;
NSArray *options = [product objectForKey:#"options"];
lastTop = 10;
for(int i=0;i<options.count; i++) {
NSDictionary *option = [options objectAtIndex:i];
NSArray *values = [option objectForKey:#"values"];
if([self hasNoneValue:values] && values.count == 2) {
NSDictionary *value = [self notNoneValue:values];
M13Checkbox *optionCheck = [[M13Checkbox alloc] initWithTitle:[option objectForKey:#"name"]];
optionCheck.frame = CGRectMake(0, lastTop, 280, 25);
[optionsPanel addSubview:optionCheck];
lastTop += 25;
} else {}
}
...where the orange UIView would magically grow and everything would just get pushed around accordingly, however this is what I'm seeing:
...the orange UIView does not grow at all, and the other two top UIView have gone somewhere off the screen. So my next guess was to turn off the Autoresizing Mask using...
optionsPanel.translatesAutoresizingMaskIntoConstraints = NO;
...but I'm getting a result where everything appears to be working but the orange UIView (optionsPanel) has no height for whatever reason and looks like:
This is getting closer to what I would expect, so I thought I would force the height of the orange UIView using code like...
frame = optionsPanel.frame;
frame.size.height = lastTop;
optionsPanel.frame = frame;
...but this appears to have no affect on anything.
Purely guessing, I found that this code almost works, if I arbitrary set the optionPanel's origin to something much larger than the space that is needed....
optionsPanel.translatesAutoresizingMaskIntoConstraints = YES;
NSArray *options = [product objectForKey:#"options"];
lastTop = 10;
for(int i=0;i<options.count; i++) {
NSDictionary *option = [options objectAtIndex:i];
NSArray *values = [option objectForKey:#"values"];
if([self hasNoneValue:values] && values.count == 2) {
NSDictionary *value = [self notNoneValue:values];
M13Checkbox *optionCheck = [[M13Checkbox alloc] initWithTitle:[option objectForKey:#"name"]];
optionCheck.frame = CGRectMake(0, lastTop, 280, 25);
[optionsPanel addSubview:optionCheck];
lastTop += 25;
} else {}
}
lastTop += 10;
frame = optionsPanel.frame;
frame.size.height = lastTop;
frame.origin.y += 300; //some arbitrarily‎ large number
optionsPanel.frame = frame;
..which gives this result:
...but apparently the AutoLayout has decided that the name label needs to take up the extra space. Its an ugly approach but if I could figure out how much space I need then I could just push everything down, if I had to. What's the secret to having a dynamic UIView between two dynamically sized labels and everything just work???
As #Timothy says, you need to manually add constraints to the subviews of the orange view if you want it to resize based on its contents—views don’t do this by default.
In general, if you’re using autolayout in a window, you should never be manually setting the frame of any view. Autolayout overrides any frames you set the every time it’s called, so even if you manage to manually get it working for a second it’ll fail the next time anything triggers a layout.
For views created in code, it's perfectly fine to set their frames as long as their translatesAutoresizingMaskIntoConstraints property is YES (the default, by the way).
However, for a view instantiated in storyboard or a nib, you can not set its translatesAutoresizingMaskIntoConstraints to YES.

Dynamically changed UIImage width

I have UIImage like this with place for UITextField at free white space between red lines at left side:
UIImage real borders bigger than visible part because gesture recognizer linked to this image and it needs to be bigger for more comfortable using with gestures.
Text alignment in text field set to right side. So task is to crop image frame from left side depending on entered text length, when keyboard dismissed after entering text. I used this code:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
ruleImage.layer.anchorPoint = CGPointMake(1,1);
[ruleImage setFrame:CGRectMake(0, 0, 120 + ruleTextfield.text.length * 15 , ruleImage.frame.size.height)];
}
But this code compressed image horizontally, not cropped, and from left to right. So questions:
how to set anchor point to top (or bottom)right corner?
what property can I use to crop image?
P.S. also I tried
ruleImage.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
ruleImage.contentMode = UIViewContentModeTopRight;
but this properties not solved my problem.
Try
ruleImage.contentMode = UIViewContentModeScaleAspectFill;

UIScrollView - paging UIImage larger than the screens width

Am currently developing an iPad app which uses a UIScrollView. The UIScrollView is populated with UIImage(s) and all the images are larger than the iPads width, twice the width, 1536px. What I would like it to do is when swiped/flicked it will scroll to the second image, i.e. 1536 and the third image to 3072 and so on. Its just to see a quick image when sliding across. I've had a look at scrollViewDidEndDragging but it gets really nasty and jumpy at times.
Is there a way of setting the animation to scroll by 1536 each time apart from the last UIImage in the UIScrollView? I know you can use setContentOffset but if I use this in the above method it doesn't work as its using the current transition still and therefore making it very jumpy.
Edit
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"cocktail_%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect rect = imageView.frame;
rect.size.width = kMainImageWidth;
rect.size.height = kMainImageHeight;
rect.origin = CGPointMake(xpos, 0);
imageView.frame = rect;
[mainImgScrollView addSubview:imageView];
[imageView release];
xpos += kMainImageWidth;
}
Edit
All I needed to do was make the UIScrollView 1536px wide and it worked. The app will have 6 images to start with and more in the future. Hopefully there won't be a memory issue to start with?
Thank you for your help.
The page size is the UIScrollView size. It is perfectly acceptable to make the UIScrollView itself larger than the width of the iPad screen. If you make the UIScrollView 1536px wide, then each page will be 1536px wide.
To allow the user to scroll around and see each picture, what you want is a scrollview containing a row of scrollviews. The outer scrollview is the one in the window, it is 1536px wide, and it is just for paging. The inner scrollviews are the width of the screen and they have their contentSize set to the image size so that the user can scroll around in each one and see the image.
(However, you're going to want to rethink your architecture, since an app into which you have predrawn multiple images 1536px wide will not run (because it will exceed the available memory for the device). The WWDC2010 videos include an excellent talk on this very topic, i.e. how to design a paging scroll view that lets you page from image to image.)
I think you want to turn on paging on your UIScroll view. This will make it "snap" to the width of the scroll view. It will behave like the home screen on the iPad/iPhone does. You may need to make your scrollView's frame wider than the screen also to get the paging effect correct. You may encounter some lag no matter what due to the size of your images.

UIView subview placement

I have a UIView that I am placing UIImageView's into, with each UIImageView being 16 pixels wide.
The query is if I add say 5 of the UIImageViews using addSubView, and the UIView is spec'd at 16 tall by 300 wide, is there any way to have the images in the view 'stack up' so to speak, so that the images are not on top of each other? Like adding image tags to a web page.
The easiest way I see of doing this is to keep a running tally of where you last placed an image, then increment by width, something like this:
-(void) addImage:(UIImage*)img toView:(UIView*)view
{
static CGRect curFrame = CGRectMake (0,0,16,16);
UIImageView* imgView = [[UIImageView alloc] initWithFrame:curFrame];
imgView.image = img;
[view addSubview:imgView];
curFrame.origin.x += 16;
}
This will have the images appear within your view from left to right
I think I understand your question correctly. You want all the images to line up in a row correct? You would need to set the frame of the UIImageView's view. The orgin will be where the top left of your image is(x,y coordinates inside the UIView that contains it) - so you would move that over 16 each time you add another image.

Resources