UIScrollView - paging UIImage larger than the screens width - ipad

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.

Related

Layout in UIScrollView

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;
}
}

Autolayout: UIImageView not rotating with device orientation

There's probably a dozen SO questions with similar titles, and as far as I can see, I've incorporated the advice from every one of them, with no luck. When I rotate the device from portrait to landscape, I want to change the background image. However, in every experiment I have tried, the UIImageView in landscape remains portrait sized.
I am using Autolayout, configured in IB like so:
My view hierarchy is configured like so:
When I rotate the device, I want to rotate everything in UIView viewBackground (the image and all of the buttons). I believe I can manage the button movements through constraints, but need to rotate UIImageView image view background myself. So, I added the following code:
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
int scaleFactor = self.traitCollection.displayScale;
NSString *source = #"drawn";
NSString *orientation;
if (size.width > size.height)
{
orientation = #"Landscape";
}
else
{
orientation = #"Portrait";
}
NSString *platform;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
platform = #"iPad";
}
else
{
platform = #"iPhone";
}
NSString *filename = [NSString stringWithFormat:#"%# %# %# %.0fx%.0f.png", source, platform, orientation, size.width * scaleFactor, size.height * scaleFactor];
// CONFIRMED: we have assembled the correct new image name.
viewBackground.frame = CGRectMake(0, 0, size.width * scaleFactor, size.height * scaleFactor);
viewBackground.autoresizesSubviews = YES;
viewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.autoresizesSubviews = YES;
imageViewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.image = [UIImage imageNamed:filename];
}
The problem: The correct image appears on the screen, but as as mentioned above, it still has the portrait dimensions. I'm certain the image changes as the artwork is significantly different, with elements being stacked horizontally versus vertically. But the new picture is cut off horizontally in landscape mode.
I have tried putting all sorts of wild values into newRect, and none of them make any difference. The image never actually moves, even if I radically change the origin in newRect. so my current theory is that my changes to the view sizes are being ignored/over written.
The Question: What am I missing that's preventing the resizing of the image to landscape mode? Is there some other auto sizing aspect I'm not addressing?
So, the problem turned out to be yet another variable in the saga. There also must be a constraint on the UIImageView that is 0 on each side, with "Constrain to Margins unchecked." Apparently, when I did that step earlier, it didn't actually take.
I have to admit, though, I really don't understand what that constraint is doing for me. It clearly makes the rotation work, but I don't get why. Since that's technically the answer to my original question, I'll gladly award the answer if someone can explain how this constraint enables this scenario.
EDIT:
In case anyone stumbles upon this answer in the future... I see now that setting the aforementioned constraint with all 0 buffers between the UIImageView and the UIView pins the edges of UIImageView to the edges of the UIView. When the device rotates, the viewController resizes the UIView, and with this constraint, the UIImageView resizes also. I was able to remove the majority of my code. The only code I need is to select a portrait or landscape image, and I was able to remove everything that resized the UIView and UIImageView.
The image now changes size properly when rotating just by using the one constraint. I only need code to decide whether to show a landscape or portrait background. (And I vastly simplified that code by storing my images in an asset catalog so the code just selects the name of "portrait" or "landscape", but the size of the image is auto selected based on the device).

Indefinite width horizontal Scroll view to display images

new to iOS development. I'll explain the core purpose, the app should display an array of images on a horizontally scrolling view, these images will be loaded from the app documents itself. But the number of images stored in the app may vary from user to user. So,
I want to create a horizontally scrolling view (UIScrollView/UITableView) which can hold UIImage. I want to change the width of the view as more images are read, but increasing the width of a UIScrollView wouldn't add a UIImage placeholder for the new images.
OR
Should I try with UITableView, any help would be much appreciated !
Use UICollectionView instead of UIScrollView.
Check this SO link.
Here using collectView is easy as it has delegate method same as UITableView.
The best way is to start a UIScrollView, add it to your ViewController and write something like this:
self.scrollView.contentSize = CGSizeMake(self.pictures.count * 170 + 10, self.scrollView.frame.size.height);
Where the 170 for me is the width of the image + 10 (like to have some space between them). The +10 afterwards is 10px after my images too (end of the UIScrollView).
Later, to put on those images you can do
for (int x = 0; x < self.pictures.count; x++) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myImage"]];
imageV.frame = CGRectMake(10 + x * 170, 10, 160, 120);
[self.scrollView addSubview:imageV];
}
Replace the imageNamed with your own image name or something like that...
This is only for about max 20 pictures, if you have a lot of pictures then I would suggest using a collection. multiple threads have started about those...
You can use icarousel in ios https://github.com/nicklockwood/iCarousel
There are many types to choose from.
Hope that might help you !!!

UIImgePickerView Images taken in landscape stretched when displayed

In the UIImagePicker the user takes photos, then the photos are saved, and loaded into a tableview, now when the images are taken normally (portrait) they are a perfect size in the image view because this is how I set it. But when the user takes an image with the device in landscape, the image looks skewed and looks very distorted. (See picture)
Top is portrait picture, bottom is landscape
So does anyone have any suggestions on how this can be done?
Any help would be amazing
If it is taken in Landscape, then you show it another frame with size width > height. Or try using the following code and see if it improves something.
imageViewTemp.clipsToBounds = YES;
imageViewTemp.contentMode = UIViewContentModeScaleAspectFit;
The UIImageView stretches images to fit its size. This is what you are seeing. To stop it, you might change the size of the image view, like so:
CGRect newFrame = CGRectMake(imageView.frame.origin.x,
imageView.frame.origin.y,
image.size.width,
image.size.height);
imageView.frame = newFrame;

Make an UIImageView and its UIImage scale proportionally without extra padding

I have an UIView that contains a UIImageView. The UIImageViews works like the branding logo of the app. When I rotate the device, the containing UIView resizes itself to correspond to the landscape or portrait proportions of the screen.
What I'm trying to achieve is to have the UIImageView scaled accordingly, keeping proportions also on the left margin.
This is the actual code for the top white "banner":
UIView *topBanner = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, height_topBanner)];
[topBanner setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin)];
[topBanner setBackgroundColor:[UIColor whiteColor]];
topBanner.autoresizesSubviews = YES;
// the logo
UIImage *topBanner_logo = [UIImage imageNamed:#"logo.png"];
float logoAspectRatio = topBanner_logo.size.width/topBanner_logo.size.height;
topBanner_logoView = [[UIImageView alloc] initWithFrame:CGRectMake(topBanner.frame.size.width/100*3, topBanner.frame.size.height/100*7, (topBanner.frame.size.height/100*86)*logoAspectRatio, topBanner.frame.size.height/100*86)];
[topBanner_logoView setImage:topBanner_logo];
topBanner_logoView.backgroundColor = [UIColor blueColor];
topBanner_logoView.contentMode = UIViewContentModeScaleAspectFit;
[topBanner_logoView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleRightMargin)];
[topBanner addSubview:topBanner_logoView];
[self.view addSubview:topBanner];
This is my starting point: portrait iPad on startup:
This is what happens when I rotate it in landscape:
As you can see, the proportions of the UIImage are ok, but I'm getting extra borders (I set the background color of the UIImageView to highlight it) because the UIImageView stretches itself to follow the change of the size of its container, and the UIImage is fit into the UIImageView and put on its center.
The same - reversed - happens when I start the app directly in landscape mode:
Then I rotate it:
... and I get the logo with extra borders on top and bottom.
I do see that I can write a function to recalculate every size on each rotation change, but I'm asking to myself if is there a way to set the UIImageView and the UIImage to make it works without hacking the autorotate/resize procedures of iOS. It sounds so simple!
You can solve this by not using UIViewContentModeScaleAspectFit, and instead calculating the aspect ratio of the image and using that to explicitly the width or height based on the other (width or height).
e.g. I rotate to landscape, and so I want the height to be 80% of the view.
CGFloat w = logo.image.size.width;
CGFloat h = logo.image.size.height;
CGFloat a = w / h;
CGFloat h_use = self.view.height *0.8;
CGFloat w_use = h_use*a;
Furthermore, set the content mode to UIViewContentModeScaleAspectFill instead now that you've explicitly set the aspect ratio.
You have set the auto resizing mask to flexible height and width:
[topBanner_logoView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleRightMargin)];
If you do not do that, the default is that the view will not chance size, and therefore, the image will not either.
I think it is because of topBanner_logoView.contentMode = UIViewContentModeScaleAspectFit;
Try topBanner_logoView.contentMode = UIViewContentModeCenter or topBanner_logoView.contentMode = UIViewContentModeLeft to prevent the UIImageView's image from resizing (and getting padding).
If the UIImageView is resizing, remove the autoresizing mask.

Resources