Background view not scaling correctly in Landscape mode - ios

that for some reason, the background image on each page is about 3/4 of the way across and stretched at the top, almost like it's portrait instead of landscape, this was fine before iOS 10 / XCode 8
I am calling a lot of values from the Util file, here;
static float getDevicePosX( float PadPosX )
{
AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
CGRect f = appDelegate.window.frame;
float width = f.size.width > f.size.height ? f.size.width : f.size.height;
return PadPosX / 2.0f * width / 1024.0f;
}
static float getDevicePosY( float PadPosY )
{
AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
CGRect f = appDelegate.window.frame;
float height = f.size.width < f.size.height ? f.size.width : f.size.height;
return PadPosY / 2.0f * height / 768.0f;
}
static float getDevicePosW( float PadPosW )
{
AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
CGRect f = appDelegate.window.frame;
float height = f.size.width < f.size.height ? f.size.width : f.size.height;
return PadPosW / 2.0f * height / 768.0f;
}
static float getDevicePosH( float PadPosH )
{
AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
CGRect f = appDelegate.window.frame;
float height = f.size.width < f.size.height ? f.size.width : f.size.height;
return PadPosH / 2.0f * height / 768.0f;
}
The other buttons are generally all in the correct places, it just seems to be the background image files that are the problem.
An example of how I initialise one of the backgrounds is this;
UIImage* imageBackground = [UIImage imageNamed: #"logo_background"];
UIImageView *viewBackground = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
[viewBackground setImage:imageBackground];
[self.view addSubview: viewBackground];
Exact calling is;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIImage* imageBackground = [UIImage imageNamed: #"logo_background"];
UIImageView *viewBackground = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
[viewBackground setImage:imageBackground];
[self.view addSubview: viewBackground];
UIImage* imageNameText = [UIImage imageNamed: #"logo_name_textfield"];
UIImage* imageNameLabel = [UIImage imageNamed: #"logo_namelabel"];
UIImage* imageNextButton = [UIImage imageNamed: #"logo_next_button"];

I managed to fix this using a combination of the answer posted above by norders, but what to do is this;
Swap
UIImageView *viewBackground = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
With
UIImageView *viewBackground = [[UIImageView alloc] initWithFrame:self.view.bounds];
But add the following to the top level view,
+ (CGRect)screenBounds {
CGRect bounds = CGRectMake(0, 0, 0, 0);
bounds.size = [self screenSize];
return bounds;
}
+ (CGSize)screenSizeForOrientation:(UIInterfaceOrientation)orientation {
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) {
UIInterfaceOrientation current = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(current)) {
return (UIInterfaceOrientationIsLandscape(orientation)) ? screenSize : CGSizeMake(screenSize.height, screenSize.width);
} else {
return (UIInterfaceOrientationIsLandscape(orientation)) ? CGSizeMake(screenSize.height, screenSize.width) : screenSize;
}
} else {
return (UIInterfaceOrientationIsLandscape(orientation)) ? CGSizeMake(screenSize.height, screenSize.width) : screenSize;
}
}
+ (CGRect)screenBoundsForOrientation:(UIInterfaceOrientation)orientation {
CGRect bounds = CGRectMake(0, 0, 0, 0);
bounds.size = [self screenSizeForOrientation:orientation];
return bounds;
}
+ (CGSize)screenSize {
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if ((NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
return CGSizeMake(screenSize.height, screenSize.width);
} else {
return screenSize;
}
}
I credit this fix to a website, I found whilst searching the answer, here;
http://perrymitchell.net/article/ios-screen-size-bounds-normalisation/
Thank you to norders also.

Related

UIScrollView - when is contentSize set

I have a UIViewController and it's view hierarchy looks like this:
UIView
UIScrollView
UIImageView
I have code that positions the image view in the middle of the scroll view's frame, like so:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
This works fine when zooming the content, but when the view controller first loads it does not center. This is because the scrollView.contentSize is always 0. So my question is - when should I call this method after the scrollView.contentSize is set? When does that get set?
I have tried in viewDidLayoutSubviews, and the bounds of the scroll view is set then, but not the content size. Is there some method that I can use where the scroll view will be guaranteed to have the content size set?
Or is there a better way to keep the image centered when it is smaller than the scroll view? What I am trying to accomplish is to have it so the image view is not at the top of the scroll view and what I am using works, except when the scroll view's content size is not set. But if there is a better way of doing this without having to adjust the contentInset, I would be fine with that too.
Update
Here is what I have currently.
It is almost working, but no matter what I try, I cannot get it to look correct when the view loads. The way it works now is that it starts out off-center because when it calls the recenterContent method, before the view is displayed the content size of the scroll view is CGSizeZero, so the calculations are wrong. But if I try to recenter the content after the view has been displayed, then there is a visible delay before it gets centered.
I am just confused as to when the contentSize of the scroll view is set if I am using AutoLayout constraints to specify the size.
Here is my code. Can anyone see anything wrong with it?
#interface MyImageViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *imageView;
#property (assign, nonatomic) BOOL needsZoomScale;
#end
#implementation MyImageViewController
- (void)loadView {
self.view = [[UIView alloc] init];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.imageView];
self.needsZoomScale = YES;
[NSLayoutConstraint activateConstraints:#[
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[self.imageView.leadingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.leadingAnchor],
[self.imageView.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor],
[self.imageView.trailingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.trailingAnchor],
[self.imageView.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor]
]];
}
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapZoom:)];
doubleTapGesture.numberOfTapsRequired = 2;
[self.imageView addGestureRecognizer:doubleTapGesture];
}
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(CGFloat)scale withCenter:(CGPoint)center {
CGRect zoomRect;
//the zoom rect is in the content view's coordinates. At a zoom scale of 1.0, the zoom rect would be the size
//of the scroll view's bounds. As the zoom scale decreases, so more content is visible, the size of the rect
//grows.
zoomRect.size.width = scrollView.frame.size.width / scale;
zoomRect.size.height = scrollView.frame.size.height / scale;
//choose an origin so as to get the right center
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}
- (void)doubleTapZoom:(UITapGestureRecognizer *)sender {
UIView *tappedView = sender.view;
CGPoint tappedPoint = [sender locationInView:tappedView];
if (tappedPoint.x <= 0) {
tappedPoint.x = 1;
}
if (tappedPoint.y <= 0) {
tappedPoint.y = 1;
}
if (tappedPoint.x >= tappedView.bounds.size.width) {
tappedPoint.x = tappedView.bounds.size.width - 1;
}
if (tappedPoint.y >= tappedView.bounds.size.height) {
tappedPoint.y = tappedView.bounds.size.height - 1;
}
CGFloat zoomScale;
if (self.scrollView.zoomScale < 1) {
zoomScale = 1;
} else if (self.scrollView.zoomScale < self.scrollView.maximumZoomScale) {
zoomScale = self.scrollView.maximumZoomScale;
} else {
zoomScale = self.scrollView.minimumZoomScale;
}
CGRect zoomRect = [self zoomRectForScrollView:self.scrollView withScale:zoomScale withCenter:tappedPoint];
[self.scrollView zoomToRect:zoomRect animated:YES];
}
- (UIScrollView *)scrollView {
if (!self->_scrollView) {
self->_scrollView = [[UIScrollView alloc] init];
self->_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self->_scrollView.minimumZoomScale = 0.1f;
self->_scrollView.maximumZoomScale = 4.0f;
self->_scrollView.bounces = YES;
self->_scrollView.bouncesZoom = YES;
self->_scrollView.delegate = self;
self->_scrollView.backgroundColor = [UIColor blackColor];
}
return self->_scrollView;
}
- (UIImageView *)imageView {
if (!self->_imageView) {
self->_imageView = [[UIImageView alloc] init];
self->_imageView.translatesAutoresizingMaskIntoConstraints = NO;
self->_imageView.userInteractionEnabled = YES;
}
return self->_imageView;
}
- (UIImage *)image {
return self.imageView.image;
}
- (void)setImage:(UIImage *)image {
self.imageView.image = image;
self.needsZoomScale = YES;
[self updateZoomScale];
}
- (void)updateZoomScale {
if (self.needsZoomScale && self.image) {
CGSize size = self.view.bounds.size;
if (size.width == 0.0f || size.height == 0.0f) {
return;
}
UIImage *image = self.image;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
if (imageSize.width > 0 && imageSize.height > 0) {
CGFloat widthScale = size.width / imageSize.width;
CGFloat heightScale = size.height / imageSize.height;
CGFloat minScale = MIN(widthScale, heightScale);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.zoomScale = minScale;
self.needsZoomScale = NO;
}
}
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self updateZoomScale];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self recenterContent:self.scrollView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self recenterContent:self.scrollView];
}
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
#end
The problem is that a UIImageView has an intrinsic content size of 0,0 -- so your code is initially putting the a 0x0 image view at the center of the scroll view.
I've made a few changes to the code you posted... see comments (I "wrapped" the changes in
// ---------------------------------
comment lines:
#interface MyImageViewController : UIViewController <UIScrollViewDelegate>
#end
#interface MyImageViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *imageView;
#property (assign, nonatomic) BOOL needsZoomScale;
#end
#implementation MyImageViewController
- (void)loadView {
self.view = [[UIView alloc] init];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.imageView];
self.needsZoomScale = YES;
// ---------------------------------
// respect safe area
UILayoutGuide *g = [self.view safeAreaLayoutGuide];
// saves on a little typing
UILayoutGuide *sg = [self.scrollView contentLayoutGuide];
// ---------------------------------
[NSLayoutConstraint activateConstraints:#[
[self.scrollView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor],
[self.scrollView.topAnchor constraintEqualToAnchor:g.topAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:g.bottomAnchor],
[self.imageView.leadingAnchor constraintEqualToAnchor:sg.leadingAnchor],
[self.imageView.topAnchor constraintEqualToAnchor:sg.topAnchor],
[self.imageView.trailingAnchor constraintEqualToAnchor:sg.trailingAnchor],
[self.imageView.bottomAnchor constraintEqualToAnchor:sg.bottomAnchor]
]];
}
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapZoom:)];
doubleTapGesture.numberOfTapsRequired = 2;
[self.imageView addGestureRecognizer:doubleTapGesture];
}
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(CGFloat)scale withCenter:(CGPoint)center {
CGRect zoomRect;
//the zoom rect is in the content view's coordinates. At a zoom scale of 1.0, the zoom rect would be the size
//of the scroll view's bounds. As the zoom scale decreases, so more content is visible, the size of the rect
//grows.
zoomRect.size.width = scrollView.frame.size.width / scale;
zoomRect.size.height = scrollView.frame.size.height / scale;
//choose an origin so as to get the right center
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}
- (void)doubleTapZoom:(UITapGestureRecognizer *)sender {
UIView *tappedView = sender.view;
CGPoint tappedPoint = [sender locationInView:tappedView];
if (tappedPoint.x <= 0) {
tappedPoint.x = 1;
}
if (tappedPoint.y <= 0) {
tappedPoint.y = 1;
}
if (tappedPoint.x >= tappedView.bounds.size.width) {
tappedPoint.x = tappedView.bounds.size.width - 1;
}
if (tappedPoint.y >= tappedView.bounds.size.height) {
tappedPoint.y = tappedView.bounds.size.height - 1;
}
CGFloat zoomScale;
if (self.scrollView.zoomScale < 1) {
zoomScale = 1;
} else if (self.scrollView.zoomScale < self.scrollView.maximumZoomScale) {
zoomScale = self.scrollView.maximumZoomScale;
} else {
zoomScale = self.scrollView.minimumZoomScale;
}
CGRect zoomRect = [self zoomRectForScrollView:self.scrollView withScale:zoomScale withCenter:tappedPoint];
[self.scrollView zoomToRect:zoomRect animated:YES];
}
- (UIScrollView *)scrollView {
if (!self->_scrollView) {
self->_scrollView = [[UIScrollView alloc] init];
self->_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self->_scrollView.minimumZoomScale = 0.1f;
self->_scrollView.maximumZoomScale = 4.0f;
self->_scrollView.bounces = YES;
self->_scrollView.bouncesZoom = YES;
self->_scrollView.delegate = self;
self->_scrollView.backgroundColor = [UIColor blackColor];
}
return self->_scrollView;
}
- (UIImageView *)imageView {
if (!self->_imageView) {
self->_imageView = [[UIImageView alloc] init];
self->_imageView.translatesAutoresizingMaskIntoConstraints = NO;
self->_imageView.userInteractionEnabled = YES;
}
return self->_imageView;
}
- (UIImage *)image {
return self.imageView.image;
}
- (void)setImage:(UIImage *)image {
self.imageView.image = image;
// ---------------------------------
// set the frame here
self.imageView.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
// ---------------------------------
// not needed ... unless maybe changing the image while view is showing?
//self.needsZoomScale = YES;
//[self updateZoomScale];
}
- (void)updateZoomScale {
if (self.needsZoomScale && self.image) {
CGSize size = self.view.bounds.size;
if (size.width == 0.0f || size.height == 0.0f) {
return;
}
UIImage *image = self.image;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
if (imageSize.width > 0 && imageSize.height > 0) {
CGFloat widthScale = size.width / imageSize.width;
CGFloat heightScale = size.height / imageSize.height;
CGFloat minScale = MIN(widthScale, heightScale);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.zoomScale = minScale;
self.needsZoomScale = NO;
}
}
}
// ---------------------------------
// Don't need this
//- (void)viewWillLayoutSubviews {
// [super viewWillLayoutSubviews];
// [self updateZoomScale];
//}
// ---------------------------------
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// ---------------------------------
// update zoom scale here
[self updateZoomScale];
// ---------------------------------
[self recenterContent:self.scrollView];
}
// ---------------------------------
// Don't need this
//- (void)viewDidAppear:(BOOL)animated {
// [super viewDidAppear:animated];
// [self recenterContent:self.scrollView];
//}
// ---------------------------------
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self recenterContent:scrollView];
}
- (void)recenterContent:(UIScrollView *)scrollView {
//this centers the content when it is smaller than the scrollView's bounds
CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}
#end
and here's how I call it:
MyImageViewController *vc = [MyImageViewController new];
UIImage *img = [UIImage imageNamed:#"bkg"];
if (nil == img) {
NSLog(#"Could not load image!!!!");
return;
}
[vc setImage:img];
[self.navigationController pushViewController:vc animated:YES];

__block modifier creates unreadable memory

Now Im developing a banner showing feature in iOS
It's a singleton which shows banner on the upper part of screen when user logs in.
It's basically a shared view with a class method showWithName...
#interface XXUserWelcomeBanner ()
{
UIImageView *logoView;
UILabel *textLabel;
CGFloat _width;
}
#end
When user calls, it creates a UIImageView and a UILabel to add on self. And animates itself onto the screen.
+ (XXUserWelcomeBanner *)shared {
static dispatch_once_t onceToken;
static XXUserWelcomeBanner *userWelcomBanner;
dispatch_once(&onceToken, ^{
userWelcomBanner = [[XXUserWelcomeBanner alloc] init];
});
return userWelcomBanner;
}
+ (void)showWithUserName:(NSString *)userID andLogo:(UIImage * _Nullable)logo {
dispatch_async(dispatch_get_main_queue(), ^{
[[self shared] createWithName:userID andLogo:logo];
});
}
So there's this other bug I just found that causes this method to be called twice and on the second time it shows only the UIImageView.
And I don't understand why that's happening.
Because the UIImageView and UILabel won't be created twice.
Here's more code.
#pragma mark - private
- (void)createWithName:(NSString *)name andLogo:(UIImage * _Nullable)logo {
self.opaque = NO;
self.alpha = 0.9;
self.backgroundColor = COLOR(0xfcfcfc);
// if (#available(iOS 13.0, *)) {
// self.backgroundColor = UIColor.secondarySystemBackgroundColor;
// }
CGSize labelSize = CGSizeZero;
if (logoView == nil) {
if (logo != nil) {
logoView = [[UIImageView alloc] initWithImage:logo];
[self addSubview:logoView];
} else {
NSString *imagePath = [XXUtility pathForResourceNamed:#"Logo" withExtension:#"png"];
UIImage *imageToAdd = [[UIImage alloc] initWithContentsOfFile:imagePath];
logoView = [[UIImageView alloc] initWithImage:imageToAdd];
[self addSubview:logoView];
}
}
if (textLabel == nil) {
textLabel = [[UILabel alloc] init];
NSString * labelString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(#"welcom banner", #"userInterface", [XXUtility bundleForStrings], #"Dear %#, welcome into game", #"user welcome banner text"), name];
labelSize = [labelString sizeWithAttributes:#{NSFontAttributeName : textLabel.font}];
textLabel.text = labelString;
[self addSubview:textLabel];
}
NSLog(#"XXUserWelcomeBanner Label size is %f x %f", labelSize.height, labelSize.width);
[self bannerSizeWithLabelSize:labelSize];
CGFloat bannerHeight = self.frame.size.height;
CGFloat bannerWidth = self.frame.size.width;
CGFloat logoHeight = bannerHeight * 0.45;
CGFloat logoWidth = logoHeight;
CGFloat logoXPos = (bannerWidth - logoWidth - labelSize.width) / 2;
CGFloat logoYPos = (bannerHeight - logoHeight) / 2;
CGFloat labelHeight = labelSize.height;
CGFloat labelWidth = labelSize.width;
CGFloat labelXPos = logoXPos + logoWidth + 5;
CGFloat labelYPos = (bannerHeight - labelHeight) / 2;
logoView.frame = CGRectMake(logoXPos, logoYPos, logoWidth, logoHeight);
textLabel.frame = CGRectMake(labelXPos, labelYPos, labelWidth, labelHeight);
[self bannerShow];
}
- (void)bannerSizeWithLabelSize:(CGSize)lSize {
_width = 40 + lSize.width + 5;
CGFloat height = 40;
CGFloat xPosition = ScreenWidth * 1/2 - _width * 1/2;
CGFloat yPosition = - height;
self.frame = CGRectMake(xPosition, yPosition, _width, height);
}
- (void)bannerShow {
UIViewController *vc;
if ([TOP_VIEWCONTROLLER respondsToSelector:#selector(topViewController)]) {
vc = [TOP_VIEWCONTROLLER topViewController];
} else {
vc = TOP_VIEWCONTROLLER;
}
[vc.view addSubview:self];
[UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGFloat height = 40;
CGFloat xPos = ScreenWidth * 1/2 - self->_width * 1/2;
CGFloat yPos = height * 1/3 + KiPhoneXSafeAreaDValue;
self.frame = CGRectMake(xPos, yPos, self->_width, height);
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.6 delay:0.2 options:UIViewAnimationOptionCurveEaseIn animations:^{
CGFloat height = 40;
CGFloat xPosition = ScreenWidth * 1/2 - self->_width * 1/2;
CGFloat yPosition = - height;
self.frame = CGRectMake(xPosition, yPosition, self->_width, height);
} completion:^(BOOL finished) {
[self bannerDestroy];
self.alpha = 0;
}];
});
}];
}
- (void)bannerDestroy {
[logoView removeFromSuperview];
[textLabel removeFromSuperview];
logoView = nil;
textLabel = nil;
[self removeFromSuperview];
}
- (void)dealloc
{
//
NSLog(#"uiview dealloc");
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
#end
Thanks in advance.
It is expected that createWithName:andLogo: is calling more than once in your app. I guess you call it with different arguments, that's why it should not be inside dispatch_once(&onceToken, ^{ });. But you can optimise XXUserWelcomeBanner's init:
- (instancetype)init {
self = [super init];
if (self) {
logoView = [[UIImageView alloc] init];
[self addSubview:logoView];
textLabel = [[UILabel alloc] init];
[self addSubview:textLabel];
}
return self;
}
In createWithName:andLogo: you will have the following:
if (logo != nil) {
logoView.image = logo;
} else {
NSString *imagePath = [XXUtility pathForResourceNamed:#"Logo" withExtension:#"png"];
UIImage *imageToAdd = [[UIImage alloc] initWithContentsOfFile:imagePath];
logoView.image = imageToAdd;
}
NSString * labelString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(#"welcom banner", #"userInterface", [XXUtility bundleForStrings], #"Dear %#, welcome into game", #"user welcome banner text"), name];
CGSize labelSize = [labelString sizeWithAttributes:#{NSFontAttributeName : textLabel.font}];
textLabel.text = labelString;

Zooming UIImageView in a UIScrollView acts weirdly

I have the following problem, guys. I have an app that is pretty much like Apple's PhotoScroller. I want to jump from image to image by swiping the screen. I can do that, but I can't zoom the images. Here's the problem - I have an array with images. If the array has only one object inside, there's no problem with zooming. But if there are more images in the array, they acts weirdly when I try to zoom. The image is not being zoomed and it goes where it wants off the screen. Here is my code:
int pageWidth = 1024;
int pageHeight = 768;
int counter = 0;
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, pageWidth, pageHeight)];
CGRect containerFrame = self.view.frame;
scrollView = [[UIScrollView alloc] initWithFrame:containerFrame];
[self.view addSubview:scrollView];
NSMutableArray *all = [[NSMutableArray alloc] init];
[all addObject:[UIImage imageNamed:#"222.jpg"]];
CGSize scrollSize = CGSizeMake(pageWidth * [all count], pageHeight);
[scrollView setContentSize:scrollSize];
for (UIImage *image in all)
{
UIImage *pageImage = [[UIImage alloc] init];
pageImage = [all objectAtIndex:counter];
CGRect scrollFrame = CGRectMake(pageWidth * counter, 0, pageWidth, pageHeight);
miniScrollView = [[UIScrollView alloc] initWithFrame:scrollFrame];
[scrollView addSubview:miniScrollView];
CGSize miniScrollSize = CGSizeMake(pageImage.size.width, pageImage.size.height);
[miniScrollView setContentSize:miniScrollSize];
UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, pageImage.size.width, pageImage.size.height)];
tempImageView.image = [all objectAtIndex:counter];
self.imageView = tempImageView;
miniScrollView.maximumZoomScale = 3.0;
miniScrollView.minimumZoomScale = 1.0;
miniScrollView.clipsToBounds = YES;
miniScrollView.delegate = self;
miniScrollView.showsHorizontalScrollIndicator = NO;
miniScrollView.showsVerticalScrollIndicator = NO;
miniScrollView.bouncesZoom = YES;
[miniScrollView addSubview:imageView];
counter ++;
}
[scrollView setPagingEnabled:YES];
[scrollView setScrollEnabled:YES];
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView;
}
Do you have any ideas what's wrong? Because I am trying to get this work almost 2 weeks.
I also worked on such sort of App. The first thing that you can do is to take a separate subclass of your ScrollView so that all the paging and zooming operations can be handled easily. In your scrollView Class, You can take reference from the following code snippet.
#interface PhotoScrollView : UIScrollView<UIScrollViewDelegate,UIGestureRecognizerDelegate>
{
int finalPhotoWidth;
int finalPhotoHeight;
}
// to contain image
#property (strong, nonatomic) UIImageView *imageView;
- (id)initWithFrame:(CGRect)frame andImage:(UIImage *)photo
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
[self initializeScrollViewWithImage:photo];
//setting gesture recognizer for single tap
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewSingleTapped:)];
singleTapRecognizer.delegate = self;
singleTapRecognizer.numberOfTapsRequired = 1;
[self addGestureRecognizer:singleTapRecognizer];
//setting gesture recognizer for Double tap
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.delegate = self;
doubleTapRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:doubleTapRecognizer];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
singleTapActivated = FALSE;
self.backgroundColor = [UIColor blackColor];
self.minimumZoomScale = 1.0f;
self.maximumZoomScale = 15.0f;
self.zoomScale = 1.0f;
self.delegate = self;
}
return self;
}
//for sizing the frame by giving height and width
-(void)initializeScrollViewWithImage:(UIImage*)photo
{
finalPhotoWidth = photo.size.width;
finalPhotoHeight = photo.size.height;
//Pre-checking of frame and setting the height and width accordingly
if (finalPhotoHeight > self.frame.size.height)
{
// if photo height is bigger than frame height, re-adjust the photo height and width accordingly
finalPhotoHeight = self.frame.size.height;
finalPhotoWidth = (photo.size.width/photo.size.height) * finalPhotoHeight;
}
if (finalPhotoWidth > self.frame.size.width)
{
// if photo width is bigger than frame width, re-adjust the photo height and width accordingly
finalPhotoWidth = self.frame.size.width;
finalPhotoHeight = (photo.size.height/photo.size.width) * finalPhotoWidth;
}
if (finalPhotoHeight < self.frame.size.height && finalPhotoWidth < self.frame.size.width)
{
// if the photo is smaller than frame, increase the photo width and height accordingly
finalPhotoWidth = self.frame.size.width;
finalPhotoHeight = self.frame.size.height;
}
//initialising imageView with the thumbnail photo
self.imageView = [[UIImageView alloc] initWithImage:photo];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
//setting frame according to the modified width and height
if(!isnan(finalPhotoWidth) && !isnan(finalPhotoHeight))
{
self.imageView.frame = CGRectMake( (self.frame.size.width - finalPhotoWidth) / 2, (self.frame.size.height - finalPhotoHeight)/2, finalPhotoWidth, finalPhotoHeight);
}
// setting scrollView properties
self.contentSize = self.imageView.frame.size;
// add image view to scroll view
[self addSubview:self.imageView];
}
//to deny the simultaneous working of single and double taps
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return NO;
}
// Gesture handleer for single tap gesture
-(void)scrollViewSingleTapped:(UITapGestureRecognizer *)recognizer
{
if(!singleTapActivated)
{
//do as per requirement
singleTapActivated = TRUE;
}
else
{
//do as per requirement
singleTapActivated = FALSE;
}
}
//for zooming after double tapping
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer
{
//to check whether image is zoomed
if (self.zoomScale != 1.0f)
{
//if image is zoomed, then zoom out
[self setZoomScale:1.0 animated:YES];
self.imageView.frame = CGRectMake( (self.frame.size.width - finalPhotoWidth) / 2, (self.frame.size.height - finalPhotoHeight)/2, finalPhotoWidth, finalPhotoHeight);
[self.observer photoZoomStopped];
}
else
{
// get the point of image which is double tapped
CGPoint pointInView = [recognizer locationInView:self.imageView];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.zoomScale * 4.0f;
newZoomScale = MIN(newZoomScale, self.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.imageView.frame.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self zoomToRect:rectToZoomTo animated:YES];
}
}
// To re-center the image after zooming in and zooming out
- (void)centerScrollViewContents
{
CGSize boundsSize = self.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;
}
//for zooming in and zooming out
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if (self.zoomScale > 1.0f)
{
[self.observer photoZoomStarted];
[self centerScrollViewContents];
}
else
{
self.bouncesZoom = NO;
[self.observer photoZoomStopped];
// for zooming out by pinching
[self centerScrollViewContents];
}
// The scroll view has zoomed, so we need to re-center the contents
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
Please let me know if it helps. Thanks :)

UIScrollView squashes UIImageView image

I have the following code in ViewDidLoad and it squashes my UIImage/ UIImageView. I want the image to fill the UIScrollView.
[super viewDidLoad];
_imageHolder = [[UIImageView alloc]initWithFrame:self.view.frame];
_scrollView = [[UIScrollView alloc]initWithFrame:self.view.frame];
[_scrollView setBackgroundColor:[UIColor purpleColor]];
// Tell the scroll view the size of the contents
self.scrollView.contentSize = self.view.frame.size;
self.scrollView.delegate = self;
self.scrollView.scrollEnabled = YES;
FfThumbnailData* thumbData = [self.arrayOfImages objectAtIndex:self.thisImageIndex];
UIImage* fullSizedImage = [[UIImage alloc]initWithContentsOfFile:thumbData.path];
[self.imageHolder setImage:fullSizedImage];
float x = fullSizedImage.size.width;
float y = fullSizedImage.size.height;
CGRect rect = CGRectMake(0, 0, x, y);
NSLog(#"Size of image: %#", NSStringFromCGRect(rect));
//
//[self.imageHolder setImage:[UIImage imageNamed:#"P1020486.png"]];
[self.scrollView addSubview:_imageHolder];
[self.view addSubview:self.scrollView];
_imageHolder.contentMode = UIViewContentModeScaleAspectFit;
I set the backgound of the UIScrollView to purple for clarity:
http://i1219.photobucket.com/albums/dd427/Dave_Chambers/IMG_0315.png
I should add that after I zoom in and then zoom out, the image IS correctly placed in the scrollview and not squashed.
Strangely, the commented line [self.imageHolder setImage:[UIImage imageNamed:#"P1020486.png"]]; correctly displays the dummy image P1020486.png.
Also, when I download my app data, the image looks right and is indeed the correct size of an iPad image - {1936, 2592} - the size reported by my NSLog line NSLog(#"Size of image: %#", NSStringFromCGRect(rect));
Further, before I needed a UIScrollView (for zooming the photo in the full size View Controller) I access the same image and display it via an animation from one View Controller to another with the following code and it displays correctly. Crucially thought, this second section of code lacks a UIScollview.
-(void)animateTransition:(int)buttonInPositon {
#try {
FfThumbnailData* thumbData = [self.images objectAtIndex:buttonInPositon];
UIImage* fullSizedImage = [[UIImage alloc]initWithContentsOfFile:thumbData.path];
fullSize = [[FullSizeViewController alloc]init];
fullSize.imageHolderGhost.contentMode = UIViewContentModeScaleAspectFit;
fullSize.arrayOfImages = self.images;
fullSize.imageToPresent = fullSizedImage;
fullSize.numberOfImages = self.images.count;
fullSize.thisImageIndex = buttonInPositon;
[fullSize.imageHolderGhost setImage:fullSizedImage];
float screenWidth = self.view.bounds.size.width;
float screenHeight = self.view.bounds.size.height;
NSLog(#"ButtonFrame: %#", NSStringFromCGRect(buttonFrame));
[fullSize.view setFrame:CGRectMake(buttonFrame.origin.x, buttonFrame.origin.y, buttonFrame.size.width, buttonFrame.size.height-44)];
NSLog(#"ButtonFrame: %#", NSStringFromCGRect(fullSize.view.frame));
[self.view addSubview:fullSize.view];
fullSize.view.backgroundColor = [UIColor clearColor];
[UIView animateWithDuration:0.9 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
[fullSize.view setFrame:CGRectMake(0, 0, screenWidth, screenHeight)];
} completion:^(BOOL finished){
[[self navigationController] pushViewController:fullSize animated:NO];
self.navigationController.navigationBarHidden = YES;
}];
}
#catch (NSException *exception) {
NSLog(#"%#", exception);
}
}
For completeness, the code with the UIScrollView has the following method in one VC:
-(void)presentWithScrollview:(int)buttonInPositon {
FfThumbnailData* thumbData = [self.images objectAtIndex:buttonInPositon];
UIImage* fullSizedImage = [[UIImage alloc]initWithContentsOfFile:thumbData.path];
fullSize = [[FullSizeViewController alloc]init];
fullSize.arrayOfImages = self.images;
fullSize.imageToPresent = fullSizedImage;
fullSize.numberOfImages = self.images.count;
fullSize.thisImageIndex = buttonInPositon;
[[self navigationController] pushViewController:fullSize animated:NO];
self.navigationController.navigationBarHidden = YES;
}
and in the destination VC the following methods may be relevant to my problem (ViewDidLoad pasted at the top of this question):
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Set up the minimum & maximum zoom scales
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
//
self.scrollView.minimumZoomScale = minScale;
//Can set this bigger if needs be - match iPhoto
self.scrollView.maximumZoomScale = 2.0f;
[self centerScrollViewContents];
}
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
//NSLog(#"boundsSize: %#", NSStringFromCGSize(boundsSize));
CGRect contentsFrame = self.imageHolder.frame;
//NSLog(#"contentsFrame: %#", NSStringFromCGRect(contentsFrame));
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.imageHolder.frame = contentsFrame;
}
#pragma mark - UIScrollViewDelegate
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that we want to zoom
return self.imageHolder;
//return self.scrollViewBar;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so we need to re-center the contents
[self centerScrollViewContents];
}
Any help on this would be great. Thanks
So, I found the solution myself. For some reason, as I wrote in my update to the question, any zooming in, followed by zooming out of the image fixed the image problem and presented it properly. I added these two lines to viewWillAppear, above [self centerScrollViewContents];
self.scrollView.zoomScale = 2.0;
self.scrollView.zoomScale = 1.0;
Problem solved

Adding subviews to a UIView at initialization

In the initialization method for a UIView subclass I am adding several UIImageViews. When the view is rendered they are present in the subviews array and have valid frame rects, but are not drawn on the screen. Does anyone know why this happens?
- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self)
return nil;
[self commonInit];
return self;
}
- (void) commonInit {
_padding = 128.0f;
// Add the chord views here
_chordImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"fn_maj_x_x_x_x_1_e_1_fn"], [UIImage imageNamed:#"en_maj_x_x_x_x_1_e_1_en"], [UIImage imageNamed:#"dn_maj_x_x_x_x_1_d_1_f#"], [UIImage imageNamed:#"an_min_x_x_x_x_1_a_1_en"], nil];
CGFloat containerWidth = [self bounds].size.width;
NSLog(#"containerWidth: %f",containerWidth);
CGFloat chordWidth = [[_chordImages objectAtIndex:0] size].width;
NSLog(#"chordWidth: %f",chordWidth);
CGFloat chordMarkerWidth = [[[GVChordMarkerView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)] frame].size.width;
NSLog(#"chordMarkerWidth: %f",chordMarkerWidth);
// [self setFrame:CGRectMake(chordXPosition, 0, [self bounds].size.width, [self bounds].size.height]);
CGFloat chordXPosition = 0;
CGFloat chordXSpacing = (containerWidth/4 - (chordMarkerWidth + chordWidth));
chordXPosition += containerWidth/4;
for (NSUInteger i = 0; i < [_chordImages count]; i++) {
chordXPosition += chordXSpacing;
CGRect chordImageViewFrame = CGRectMake(chordXPosition, 0, [[_chordImages objectAtIndex:0] size].width, [[_chordImages objectAtIndex:0] size].height);
UIImageView *chordImageView = [[UIImageView alloc] initWithFrame:chordImageViewFrame];
[self addSubview:chordImageView];
chordXPosition += (chordMarkerWidth + chordWidth);
}
NSLog(#"Finished");
}
Could it be that your UIImageview is just empty? Where did you add the image?

Resources