Scaling a view and all its contents - ios

I'm wondering if there's a way to scale a UIView and all it's subviews.
When I say scaling, I'm not just talking about the frames, I wan't to scale everything so the fonts too.
At the moment I'm doing it manually :
-(void)setScale:(float)scale
{
// Apply scale on all subviews
myLabel.font = [UIFont myFontWithSize:baseSize*scale];
[myButton setScale:scale]
[myCustomView1 setScale:scale]
[myCustomView2 setScale:scale]
}
-(void)layoutSubviews
{
// Apply scale on layout
myIcon.frame = CGRectMake(posX, posY, baseW*scale, baseH*scale);
[...]
}
But the setScale method must be defined in all my views, that's quite tedious.
And I've got some problems when I wan't to scale some html text displayed with a UIWebView, this content can't be so easily scaled ...
I've tried another way too : when my view is scaled, it doesn't accept userInteraction so I tried taking snapshots, images are much easier to scale.
But it was too heavy for my iphone memory, especially when the views I wan't to scale are hundreds of uicollectionviewCells.
Is there any faster way to do that ?

You could apply a scaling transform to the top level view.
For example this will scale everything in the view by a factor of 1.5 (50% bigger):
view.transform = CGAffineTransformMakeScale(1.5, 1.5);

Related

How to pinch to zoom a view without making the view bigger?

This is what I currently have:
func handlePinching(recognizer : UIPinchGestureRecognizer) {
self.layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformScale(self.transform, recognizer.scale, recognizer.scale));
recognizer.scale = 1.0;
}
Using self.view.transform for that also makes it bigger. I want to make it zoom "internally" (I really don't know how to explain it).
So I'm not 100% sure to understand the question but I guess I get it.
For example you want to zoom on an image without zooming the other element of the UI (NavBar, Buttons, ...) right?
So I guess in you're example you're in a viewController, which means, when you change the scale of self.view you'll zoom everything. You have to apply the scale on the specific view that you want to zoom in.
In the example below, to zoom on the image, the image is inside of an UIImageView, and this imageView is subView of self.view. And you will just apply a transform on this imageView.
Moreover I think you get a little bit confused on how to zoom, considering the view you want to zoom is imageView you just need to do
imageView.transform = CGAffineTransformMakeScale(recognizer.scale,recognizer.scale)
I hope this answer your question, let me know if something is not clear.

Adjust content scale and offset on resize of UIScrollView

I have UI which hast two states of layouts (besides portrait-landscape). In each state some other part of UI is exposed (is larger).
Problem is that UIScrollView with some UIImageView is resized on those states changes, and in each state different part of image is shown since scale and offset remains unchanged.
Is there some nice way to update this scale and offset values so more or less same part of image is shown for a large and small sized UIScrollView?
How about the UIScrollView method
- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated
Rect is a rectangle in the coordinate space of the view returned by viewForZoomingInScrollView:.
Determine what rectangle of the zoomed view is being shown.
Change your views so that the UIScrollView bounds are changed.
Do the zoomToRect to show the same content, scaled as necessary.
Without having compiled and run this, it should be approximately...
CGSize rectSize;
rectSize.origin = scrollview.contentOffset;
rectSize.width = scrollview.bounds.size.width * scrollview.zoomScale;
rectSize.height = scrollview.bounds.size.height * scrollview.zoomScale;
// Do whatever makes the views change
[scrollView zoomToRect:rectSize animated:whateverYouLike];

Take a screenshot of UIScrollView, starting at contentOffset point?

I looked at this code here, which lets you take a screenshot of either the top visible portion of a UIScrollView or of the whole UIScrollView:
Getting a screenshot of a UIScrollView, including offscreen parts
What I want is to take a screenshot of just the portion from contentOffset onwards, as the UIScrollView can be quite long and thus it would take a while to take a screenshot of it. Is it possible to do this?
The solution you mentioned basically resize the scroll view to match the size of its content, and therefore the entire content will be rendered without a scroll and then captured.
If you want to apply the same solution, you can resize the scroll view to match the size of the visible content + the size of remaining content til the end.
So try to replace those lines:
_scrollView.contentOffset = CGPointZero;
_scrollView.frame = CGRectMake(0, 0,
_scrollView.contentSize.width,
_scrollView.contentSize.height);
With:
//_scrollView.contentOffset = CGPointZero; // Don't change the offset
_scrollView.frame = CGRectMake(0, 0,
_scrollView.contentSize.width,
_scrollView.contentSize.height - _scrollView.contentOffset);
I had asked similar question before Taking screenshot of Part of Screen
You can take screenshot of what the user is seeing in terms of 320 x 480 but you cannot take screenshot of a portion of the screen. X and Y coordinates have absolutely no effect on where the screenshot starts/stops.

UIView UIScrollview - frames, bounds, center confusions

I am putting a UIImageView inside a UIScrollView, and trying to control the image so that it is centred on the scrollview after a zoom. and I am not sure the best way to do this.
The apple docs tell us NOT to use the frame property: "Warning If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored." So I am attempting using the following in a UIViewController subclass whose xib contains a scrollView and contained imageView:
scrollView.bounds =
CGRectMake
(scrollView.contentSize.width/2 - scrollView.center.x,
scrollView.contentSize.height/2 - scrollView.center.y,
scrollView.bounds.size.width,
scrollView.bounds.size.height);
containedView.center =
CGPointMake
(containedView.bounds.size.width*scrollView.zoomScale/2,
containedView.bounds.size.height*scrollView.zoomScale/2);
This works accurately where the width and height of the containedView is larger than that of the scrollView and sets the views so that subsequent scrolling will take you exactly to the edges of the containedView. However when either dimension of the image is smaller than the scrollView width and height the image is magnetically attracted to the top left corner of the screen. In the iPad Simulator (only) when the images is shrunk to the size of minimumZoom it does lock on to the centre of the screen. The magnetic attraction is very smooth as if something in the UI is overriding my code after the image has been centred. It looks a bit like a CALayer contentsGravity ( kCAGravityTopLeft ) thing, maybe?
Apple contradict their own advice in their code sample, photoScroller (in a subclass of UIScrollView):
// center the image as it becomes smaller than the size of the screen
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = imageView.frame;
// center horizontally
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// center vertically
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
imageView.frame = frameToCenter;
This method does a better job of centring when the image is smaller, but when I try this on my project it introduces some kind of inconsistencies. For example, with scrollView.bounces = NO, a horizontal image whose height is smaller than the height of the scrollView but whose width is larger (so it can be scrolled from left to right) will scroll further to the left than it should (when scrolling to the right it stops correctly at the edge of the image, although if scrollView.bounces = YES it then bounces in from the edge so the image is always cropped on the left) When the image is larger in both dimensions than its containing scrollview this issue accentuates and the whole result feels broken, which is unsurprising given Apple's documented advice.
I have scoured the forums and can't find much comment on this. Am I missing something really obvious?
You don't appear to be using the transform property, so you can ignore that warning about not using the frame property when using the transform property. Go ahead and use the frame property, just like Apple (and the rest of us) do.

Using UIPopoverBackgroundView class

Apple is missing documentation on how to use UIPopoverBackgroundView class introduced in iOS5. Anyone have an example?
I have tried to subclass it, but my XCode 4.2 on Lion is missing UIPopoverBackgroundView.h
Edit: Unsurprisingly, it should have been imported as #import <UIKit/UIPopoverBackgroundView.h>
To add to the other, link-only answers, here is how this is done.
Create a new subclass of UIPopoverBackgroundView
Declare the following in your interface:
+(UIEdgeInsets)contentViewInsets;
+(CGFloat)arrowHeight;
+(CGFloat)arrowBase;
#property(nonatomic,readwrite) CGFloat arrowOffset;
#property(nonatomic,readwrite) UIPopoverArrowDirection arrowDirection;
The class methods are straightforward: contentViewInsets returns the width of your borders all the way round (not including the arrow), arrowHeight is the height of your arrow, arrowBase is the base of your arrow.
Implement the two property setters, making sure to call [self setNeedsLayout].
In your initialisation method, create two image views, one holding your arrow (which should be the size of the arrow dimensions in your class methods) and one holding your background image (which must be a resizable image) and add these as subviews. It doesn't matter where you put the subviews at this point, as you don't have an arrow direction or offset. You should make sure the arrow image view is above the background image view so it blends in properly.
Implement layoutSubviews. In here, according to the arrowDirection and arrowOffset properties, you have to adjust the frames of your background view and arrow view.
The frame of your background view should be self.bounds, inset by arrowHeight on whatever edge the arrow is on
The frame of the arrow view should be aligned so that the centre is arrowOffset away from the centre of self (correct according to the axis). You have to change the image orientation if the arrow direction is not up, but my popover would only be up so I didn't do that.
Here is the layoutSubviews method for my Up-only subclass:
-(void)layoutSubviews
{
if (self.arrowDirection == UIPopoverArrowDirectionUp)
{
CGFloat height = [[self class] arrowHeight];
CGFloat base = [[self class] arrowBase];
self.background.frame = CGRectMake(0, height, self.frame.size.width, self.frame.size.height - height);
self.arrow.frame = CGRectMake(self.frame.size.width * 0.5 + self.arrowOffset - base * 0.5, 1.0, base, height);
[self bringSubviewToFront:self.arrow];
}
}
Another link only answer, but customising UIPopoverBackgroundView is more work than you might realise given the limited documentation available and this github project has a complete working example which saved me a lot of time: https://github.com/GiK/GIKPopoverBackgroundView
It's fairly straightforward to drop into your own project. The most fiddly part is adapting the cap insets for whatever custom images you're using. I'd recommend doing your customisations in-situ in the project and as it's easy to verify all the popover orientation/direction use cases display correctly in the simulator before migrating it into your own project.

Resources