I would like to change the background picture of my view controller, when the orientation is changed. I call my setupGUIForOrientation function in shouldAutorotateToInterfaceOrientation, like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[self setupGUIForOrientation:interfaceOrientation];
return YES;
}
The setupGUIForOrientation function:
-(void)setupGUIForOrientation:(UIInterfaceOrientation)orientation
{
if(!background) background = [[UIImageView alloc] init];
UIImage* image = [[StorageProvider storage].imageProvider getImageForSurfaceElement:#"background"];
int width = 0;
int height = 0;
CGRect bounds = [[UIScreen mainScreen] bounds];
if(UIInterfaceOrientationIsPortrait(orientation)) {
width = MIN(bounds.size.width, bounds.size.height);
height = MAX(bounds.size.width, bounds.size.height);
} else {
width = MAX(bounds.size.width, bounds.size.height);
height = MIN(bounds.size.width, bounds.size.height);
}
background.frame = CGRectMake(0, 0, width, height);
[self.view addSubview: background];
[self.view sendSubviewToBack: background];
[background setImage:image];
}
Everything is good (the image and the frame changes) except one thing: when the rotation happens, I can see the view controller's background color for one second, which is really ugly. Can I prevent it somehow? And, can I code this function on a better way? Thanks for any help!
I'm guessing, but you may be updating your view too early. Rather than call your method in shouldAutorotateToInterfaceOrientation, have you tried doing it in didRotateFromInterfaceOrientation?
I.e. making this change:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self setupGUIForOrientation:self.interfaceOrientation];
}
Related
Whenever I click on an image, I want the image to display as a larger image with the ability to zoom. So far when I click on the image, it displays as I want it to inside a scrollview. However, I have to get lucky to be able to zoom in properly. Most of the time when I attempt to zoom, the image just moves down and to the right and does not zoom at all. Here is my code:
-(void) pictureButtonAction
{
self.scrollImageView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
self.scrollImageView.contentSize = self.fullImageView.image.size;
self.scrollImageView.delegate = self;
self.scrollImageView.clipsToBounds = YES;
self.scrollImageView.scrollEnabled = YES;
[self.scrollImageView setMaximumZoomScale:4.0f];
[self.scrollImageView setMinimumZoomScale:1.0f];
[self.view addSubview:self.scrollImageView];
[self.scrollImageView addSubview:fullImageView];
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.fullImageView;
}
ImageViews frame got changed while zooming , so that it moves down. So, You have to centralize the image view each time you zoom .
your scrollviews contentsize should be greater than or equal to your image size , if your fullImageView.image.size is less than your scrollviews bounds ,then set your scrollviews contentSize atleast double the scrollviews bounds .
call the below function in scrollViewDidZoom delegate method
-(void) centerScrollViewContents
{
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
Try this , hope it will help you ; happy coding ! :)
#Maria's answer works but when zooming you'll experience unwanted bounces, instead of using it under scrollViewDidZoom, use scrollViewDidEndZooming: delegate to prevent that..
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
[self centerScrollViewContents];
}
and with a little enhancement from her code:
[UIView animateWithDuration:0 animations:^{
self.previewImageView.frame = contentsFrame;
}];
a bit late for an answer but i hope this will be useful to some..
I'm attempting to allocate a webview with a specific frame, and then animate both its y position and height. When I do so, the webview instantly snaps to its "final" position, not animating the y, but it is animating the height.
Also, just so there's no confusion, I use UIView+Positioning (https://github.com/freak4pc/UIView-Positioning) to set my x,y,height, and width, instead of fiddling with the CGRects :)
Here is a screen capture of the animation, the issue is that the webview is instantly created at y=260/height=0px, where it should start at y=340/height=0px and transition to y=260 and height=maxHeight. y=340px is where the top and bottom split, and is where the "close" animation on the webview focuses to.
The code
for (PageViewController * page in pages) {
[page preFullscreen];
}
[UIView animateWithDuration:0.5f animations: ^() {
for (PageViewController * page in pages) {
[page fullScreen];
}
}];
...
- (void)preFullscreen {
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0.0f, 340.0f, 320.0f, 0.0f)];
_webView.delegate = self;
_webView.scrollView.bounces = NO;
_webView.scrollView.scrollEnabled = NO;
_webView.scalesPageToFit = YES;
_webView.backgroundColor = [UIColor whiteColor];
[_scrollView addSubview:_webView];
[_webView loadHTMLString:[entry storyHTML] baseURL:nil];
}
- (void)fullScreen {
_darkOverlayview.alpha = 0.0f;
_webView.y=260.0f;
_webView.height = [[UIScreen mainScreen] bounds].size.height - 260.0f;
_scrollView.delegate = hsVC;
_textareaView.hidden = YES;
_frontImage.height = 260.0f;
self.view.height = [[UIScreen mainScreen] bounds].size.height;
_scrollView.height = [[UIScreen mainScreen] bounds].size.height;
}
I think the problem is precisely that you are using the UIView+Positioning additions? Internally it ends up setting the frame multiple times within the animation block, so the start position is lost. Use the regular setFrame: to set the entire frame once per view in the animation block.
As for animating the UIWebView, I also recommend setting it to full height in preFullscreen (so that it's below the bottom edge of the screen) and only animating the y coordinate. In my experience resizing webviews during animation can be problematic since the web content is also resized.
In one of my views I have a bottom bar. A tap on this view animates a temporary cart half way to the screen. Now, where there is no item in the temporary table I show a no data overlay which is nothing but a view with an empty cart image and text underneath it.
The issue is: when this table animates to expand and collapse, the no data overlay does not look good during animation. It appears that the image is also shrinking/expanding until the temporary table view stops animating.
So, I tried by playing with the alpha of the temporary table view while this animation happens so that it does not look that bad. But this is not helping either. There is an abrupt flash in the end.
Any suggestions?
self.temporaryTable.backgroundView = self.noDataOverlay;
[UIView animateWithDuration:0.5 animations:^{
self.temporaryTable.alpha = 0.5;
} completion:^(BOOL finished) {
self.temporaryTable.alpha = 1.0;
}];
Here is my code for drawing the image:
- (void)drawRect:(CGRect)iRect scalingImage:(BOOL)iShouldScale {
CGRect aRect = self.superview.bounds;
// Draw our image
CGRect anImageRect = iRect;
if (self.image) {
//scale the image based on the height
anImageRect = CGRectZero;
anImageRect.size.height = self.image.size.height;
anImageRect.size.width = self.image.size.width;
#ifdef ACINTERNAL
if (iShouldScale) {
anImageRect = [self aspectFittedRect:anImageRect max:aRect];
} else {
anImageRect.origin.x = (iRect.size.width/2) - (anImageRect.size.width/2);
anImageRect.origin.y = kTopMargin;
}
#else
anImageRect.origin.x = (iRect.size.width/2) - (anImageRect.size.width/2);
anImageRect.origin.y = kTopMargin;
#endif
if (self.shouldCenterImage)
anImageRect.origin.y = (iRect.size.height/2) - (anImageRect.size.height/2);
[self.image drawInRect:anImageRect];
} else {
anImageRect.origin.y = (iRect.size.height/6);
anImageRect.size.height = (iRect.size.height/6);
}
// Draw our title and message
if (self.title) {
CGFloat aTop = anImageRect.origin.y + anImageRect.size.height + kSpacer;
CGFloat aWidth = aRect.size.width;
UIColor *aColor = [UIColor colorWithRed:96/256.0 green:106/256.0 blue:122/256.0 alpha:1];
[aColor set];
CGRect aTextRect = CGRectMake(0, aTop, aWidth, kTitleHeight * 2);
UIFont *aTitleFont = [UIFont boldSystemFontOfSize:kTitleFontSize];
[self.title drawInRect:aTextRect withFont:aTitleFont lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
if (self.subTitle) {
UIFont *aSubTitleFont = [UIFont systemFontOfSize:kSubTitleFontSize];
aTextRect = CGRectMake(0, aTop+kSpacer, aWidth, kTitleHeight);
[self.subTitle drawInRect:aTextRect withFont:aSubTitleFont lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
}
}
}
- (void)addToView:(UIView *)iView {
// setting a unique tag number here so that if the user has any other tag there should not be a conflict
UIView *aView = [iView viewWithTag:699];
if (aView) {
[aView removeFromSuperview];
}
self.tag = 699;
[iView addSubview:self];
}
- (void)removeView {
[super removeFromSuperview];
}
-(void)setViewFrame:(CGRect) iFrame {
CGRect aRect = self.frame;
aRect.size.width = iFrame.size.width;
aRect.size.height = iFrame.size.height;
self.frame = aRect;
[self setNeedsDisplay];
}
- (CGRect)aspectFittedRect:(CGRect)iRect max:(CGRect)iMaxRect {
float anOriginalAspectRatio = iRect.size.width / iRect.size.height;
float aMaxAspectRatio = iMaxRect.size.width / iMaxRect.size.height;
CGRect aNewRect = iMaxRect;
if (anOriginalAspectRatio > aMaxAspectRatio) { // scale by width
aNewRect.size.height = iMaxRect.size.width * iRect.size.height / iRect.size.width;
} else {
aNewRect.size.width = iMaxRect.size.height * iRect.size.width / iRect.size.height;
}
aNewRect.origin.y = (iMaxRect.size.height - aNewRect.size.height)/2.0;
aNewRect.origin.x = (iMaxRect.size.width - aNewRect.size.width)/2.0;
return CGRectIntegral(aNewRect);
}
One possibility here is to fix the original problem, namely the fact that the empty cart image is expanding/collapsing as the view animates. Without seeing your code it is difficult to solve this problem, but in my own code what I do is set the contentMode of the UIImageView to UIViewContentModeBottom. This causes the image to stay the same size even though the image view that contains it may grow and shrink as part of the animation.
You see a flash because you animate alpha up to 0.5 and then when animation completes you set it from 0.5 to 1.0. Just animate the alpha up to 1.0:
[UIView animateWithDuration:0.5
animations:^
{
self.temporaryTable.alpha = 1.0;
}];
I have an application which has a fullscreen UIScrollView, and within it there are seven images. The images are also meant to be full screen, and the scroll view is set to enable pagination.
I have a method which either creates or moves the image views:
-(void)rebuildImageView{
// set up images
float screenW = self.view.bounds.size.width;
float screenH = self.view.bounds.size.height;
int numImgs = self.soundNames.count;
self.mainScrollView.contentSize = CGSizeMake(screenW * numImgs, screenH);
for(int i=0; i<numImgs; i++){
UIImageView* imageView = (UIImageView*)[self.mainScrollView viewWithTag:i+100];
if(imageView == nil){
imageView = [[UIImageView alloc] init];
imageView.tag = i+100;
[self.mainScrollView addSubview:imageView];
imageView.image = [UIImage imageNamed:[NSString stringWithFormat:#"image%d.jpg",i]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
[imageView release];
}
imageView.frame = CGRectMake(i * screenW, 0, screenW, screenH);
}
// scroll to the current one
[self.mainScrollView scrollRectToVisible:CGRectMake(self.currentSound*screenW, 0, screenW, screenH) animated:YES];
}
I also have this on the view controller:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[UIView animateWithDuration:duration animations:^{
[self rebuildImageView];
}];
}
This code works fine when I autorotate while image 0 is being shown, but when I'm on image 7, you can briefly see most of image 6 when rotating. This video shows what's happening:
http://www.youtube.com/watch?v=1O3jOcTgVP8
Is there a better method I should use to reconfigure the scroll view and images when rotating the device?
Any frame changes put in the willAnimateRotationToInterfaceOrientation:duration method should automatically animate. So you could try removing it from the block?
Personally, I've had a lot more luck with this type of thing subclassing UIScrollView and putting the equivalent layout subview frame code in an override of the layoutSubviews method (don't forget to call super or you might end up with misplaced scroll bars).
I'm at the end of my wisdom...
I have a view which should display an animated circular section which is pre-rendered and available as png files (both retina and non-retina versions, correctly named; pie_p_000.png vs. pie_p_000#2x.png). This section should be animated according the change to some percentage value within the app. So I made a subclass of UIView which has a custom CALayer within its layer hierarchy. The plan is to implement a custom animatable property for the CALayer subclass and change pictures in the overridden method drawInContext:. So far so good, the plan worked and the animation is shown when I change the percentage value using the function setPercentageWithFloat: of the view (full source below).
The thing is: I really don't know why my iPhone4 always presents the low-res image. I tried already playing around with scale factors, but that didn't help. Either the images are presented in the right size and low-res or the images are presented double size.
Overriding display: and setting the contents property directly has the effect that the animation doesn't appear (during the animation no image is presented); after the animation time the final image is presented. In this case the correct resolution image is presented.
Btw: the following code is not very sophisticated in error-handling, flexibility and elegance yet as it is an attempt just to get that thing running ;) - so the image is still presented flipped and so on...
I hope that somebody has a hint for me.
Thanks
The view:
#import "ScrollBarView.h"
#import "ScrollBarLayer.h"
#implementation ScrollBarView
#synthesize sbl;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)setImagesWithName:(NSString*)imageName {
ScrollBarLayer *ssbl = [[ScrollBarLayer alloc] init];
ssbl.frame = CGRectMake(0, 0, 30, 30);
[ssbl setImagesWithName:imageName];
[self.layer addSublayer:ssbl];
self.sbl = ssbl;
[ssbl release];
}
- (void) dealloc {
self.sbl = nil;
[super dealloc];
}
- (void)setPercentageWithFloat:(CGFloat)perc {
if(perc > 1.0){
perc = 1.0;
} else if(perc < 0) {
perc = 0;
}
[self.sbl setPercentage:perc];
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:#"percentage"];
ba.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
ba.duration = 0.8;
ba.toValue = [NSNumber numberWithFloat:perc];
[self.sbl addAnimation:ba forKey:nil];
}
#end
The View can be configured to work with different images (using the function setImagesWithName:) using different names (theName). Within this method the View adds the ScrollBarLayer to its layer property.
The Layer:
#import "ScrollBarLayer.h"
#implementation ScrollBarLayer
#synthesize filename;
#dynamic percentage;
- (id)init {
self = [super init];
if (self) {
}
return self;
}
- (void)setImagesWithName:(NSString*)imageName {
self.frame = CGRectMake(0, 0, 30, 30);
self.percentage = 0;
self.filename = imageName;
}
+ (BOOL) needsDisplayForKey:(NSString*)key {
if([key isEqualToString:#"percentage"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
- (void) drawInContext:(CGContextRef)ctx {
int imageIdx = (int)roundf((float)199 * self.percentage);
NSString *thisfilename = [self.filename stringByAppendingString:[NSString stringWithFormat:#"%03d.png", i+1]];
UIImage* c = [UIImage imageNamed:thisfilename];
CGImageRef img = [c CGImage];
CGSize sz = CGSizeMake(CGImageGetWidth(img), CGImageGetHeight(img));
UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);
CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
UIGraphicsEndImageContext();
}
- (void) dealloc {
self.pictures = nil;
self.filename = nil;
[super dealloc];
}
#end
I also would need light on this.
I think that the issue is between points and pixels... I think that you should check if the device has a retina display, and if so, you should replace the CGContextDrawImage(...) CGRect (in second parameter) for one with half width and height.
#define IS_RETINA_DISPLAY() ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00)
...
UIGraphicsBeginImageContextWithOptions(CGSizeMake(sz.width, sz.height), NO, 0.0);
if (IS_RETINA_DISPLAY())
{
CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width/2.0f, sz.height/2.0f), img);
} else
{
CGContextDrawImage(ctx, CGRectMake(0, 0, sz.width, sz.height), img);
}
UIGraphicsEndImageContext();
...
But I'm not sure this gill give you a "smooth" result...