background of UIView gets scaled incorrectly and is offset - objective-c

I saw other posts but still can't get my background image to scale correctly. I use a UIView to set a background image because i need the background image to tile vertically. I set the frame of the UIView to be the same size as my pixel width and height of the png, but the image gets offset and scaled incorrectly. Could it be because the image has some transparent pixels? I tried the following:
_view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
_view.opaque = NO;
_view.contentMode = UIViewContentModeScaleAspectFit;
//_view.layer.contentsGravity = kCAGravityTop;

You might find it easier to just make a subclass of UIView. In the subclass, override drawRect: to draw your image using drawAsPatternInRect:. That method will fill the view by tiling the image:
- (void)drawRect:(CGRect)dirtyRect {
[self.tileImage drawAsPatternInRect:self.bounds];
}

I think it is because you are using a Image as a background colour.
it would be much better if you used an imageview, and added that as a subview, like this.
//Edit for vertical tiling
int y = 0;
int imgsize = 100; //in pixels
for (i =0; i<max; i++) { //max is the number of tiles you want
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
img.frame = CGRectMake(0,y+imgsize,100,100);
y+=imgsize; //increase y each time(new starting point)
[_view addSubview:img];
}
Might not be the best way to do it, but it will work.

Related

NSImageView tiles inside NSScrollView draw unwanted borders when zoomed out

I have an NSScrollView, whose documentView is a huge NSView, made of many many sub-NSImageViews, who act as tiles in a map. (The entire map is the NSView, and since it is way bigger than the screen size, its embedded in a scrollview).
I'm able to display the map with correct tile positions, and scroll around with the bars/gestures. However.. when I enable magnification to be able to zoom, the following happens:
Somehow I'm assuming auto-layout adds the tile borders below, and I don't know how to disable them. These are surely borders, since I have checked thousands of times that my tiles and subviews are the same size.. so where does this come from?
I have quite some experience with iOS development, but am completely lost with NSScrollView (Where are my delegate methods?). How do I disable this behavior of the scroll view?
Here's my subview code:
- (void)setupSubViews
{
NSLog(#"--------Creating subviews!-------");
//first we create the subviews..
//This is the key part, we traverse from top Left, and since OS X coordinates start at bottom left, we need to invert the rows!
for (int i=0; i< NUMBER_OF_COLUMNS; i++) {
for (int j=NUMBER_OF_ROWS-1; j>=0; j--) {
CGRect frame = CGRectMake(i*256, j*256, 256, 256);
NSImageView *newView = [[NSImageView alloc] initWithFrame:frame];
newView.focusRingType = NSFocusRingTypeNone; //I gave this focusRing a try, it didn't work :(
[self addSubview:newView];
}
}
}
And this is where I connect the subviews to the actual images..
-(void)updateMap:(id)tilesPassed{
if (downloadFinished) {
NSLog(#"--------DRAW RECT-------------");
NSImageView *subView;
NSInteger idx = 0;
for (int i =0; i<[self.subviews count]; i++) {
subView = [self.subviews objectAtIndex:i];
[subView setAllowsCutCopyPaste:NO];
[subView setImageFrameStyle:NSImageFrameNone]; //This doesnt work either :(
MapTile *tile = [tilesArray objectAtIndex:idx];
subView.image = tile.image;
idx++;
}
}
}
You probably don't want to use subviews for this. There is a CALayer subclass for exactly this purpose: CATiledLayer: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CATiledLayer_class/Introduction/Introduction.html
With it you can even load different image tiles based on how far you are zoomed in, just like Google Maps. It will render without borders and the performance will be WAY better than using lots of subviews. Subviews are expensive, layers are (generally) cheap.
Update: This example will get you up and running: http://bill.dudney.net/roller/objc/entry/catiledlayer_example
As a quick work around without using CATiledLayer you can stitch all images as one image and add it to main view
Below is the sample code to stitch 2 images:
UIImage *bottomImage = [UIImage imageNamed:#"bottom.png"]; //first image
UIImage *image = [UIImage imageNamed:#"top.png"]; //foreground image
CGSize newSize = CGSizeMake(209, 260); //size of image view
UIGraphicsBeginImageContext( newSize );
// drawing 1st image
[bottomImage drawInRect:CGRectMake(0,0,newSize.width/2,newSize.height/2)];
// drawing the 2nd image after the 1st
[image drawInRect:CGRectMake(0,newSize.height/2,newSize.width/2,newSize.height/2)] ;
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
and directly add this newImage formed by stitching all your tiled images to the main view.

UIView changing the size of the image?

I am trying to add UIImage to UIView .
The image is exactly in the size of the view -as i defined the view rect .
Somehow i see that the images is displayed wider,and taller(its stretched ) .
Why does my view is change the image size ?
UIImage *strip=[UIImage imageNamed:#"albumStrip.png"];
UIImageView *imageView=[[UIImageView alloc]initWithImage:strip];
UIView * aView = [ [UIView alloc] initWithFrame:CGRectMake(0.03*winSize.width, 0.85*winSize.height , 0.95*winSize.width, winSize.width/10) ];
aView.backgroundColor = [UIColor whiteColor];
aView.tag = 31000;
aView.layer.cornerRadius=1;
[aView addSubview:imageView];
EDIT :
I can see that my image is 640x960. is it possible that for the iPhone4 the UIImage dont know how to take it and div it by factor 2 ?
Try setting the UIImageView's ContentMode (http://developer.apple.com/library/ios/#documentation/uikit/reference/UIImageView_Class/Reference/Reference.html)
imageView.contentMode = UIViewContentModeScaleAspectFit;
use
imageView.layer.masksToBounds = YES;
which will restrict image to be wide and taller
If the masksToBounds property is set to YES, any sublayers of the layer that extend outside its boundaries will be clipped to those boundaries. Think of the layer, in that case, as a window onto its sublayers; anything outside the edges of the window will not be visible. When masksToBounds is NO, no clipping occurs, and any sublayers that extend outside the layer's boundaries will be visible in their entirety (as long as they don't go outside the edges of any superlayer that does have masking enabled).
of course your image is going to be stretched and can not be shown in your view.
Because its frame is a lot bigger than the size of the UIView.
you should set the frame of the UIImageView to be the same size as your UIView, the add it as a subView:
UIImage *strip=[UIImage imageNamed:#"albumStrip.png"];
CGRect frame = CGRectMake(0.03*winSize.width, 0.85*winSize.height , 0.95*winSize.width, winSize.width/10);
// create the uiimageView with the same frame size as its parentView.
UIImageView *imageView=[[UIImageView alloc] initWithFrame:frame];
// set the image
imageView.image = strip;
// create the view with the same frame
UIView * aView = [ [UIView alloc] initWithFrame:frame];
aView.backgroundColor = [UIColor whiteColor];
aView.tag = 31000;
aView.layer.cornerRadius=1;
[aView addSubview:imageView];
and of course if you can make the size of the uiimageview smaller ;)

setting view boundaries

I have a scrollview with an image as a subview. I would like to set the boundaries of the scrollview to be the size of the image view, so that you wouldn't be able to see any of the background.
I don't want this happening anymore.
The weird part is, that after you zoom in or out on the image, then the boundaries seem to fix themselves, and you can no longer move the image out of the way and see the background.
This is what I have going for code:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// return which subview we want to zoom
return self.imageView;
}
-(void)viewDidLoad{
[super viewDidLoad];
[self sendLogMessage:#"Second View Controller Loaded"];
//sets the initial view to scale to fit the screen
self.imageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
//setes the scrollview's delegate to itself
self.scrollView.delegate = self;
//sets the maximum zoom to 2.0, meaning that the picture can only become a maximum of twice as big
[self.scrollView setMaximumZoomScale : 2.5];
//sets the minimum zoom to 1.0 so that the scrollview can never be smaller than the image (no matter how far in/out we're zoomed)
[self.scrollView setMinimumZoomScale : 1.0];
[imageView addSubview:button];
}
I thought that this line would solve my problem
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
But like I said, it only works after I zoom in or out.
EDIT: When I switch
self.scrollView.contentSize = self.imageView.image.size;
to
self.scrollView.frame = self.imageView.frame;
It works like I want it to (you can't see the background), except the toolbar on the top is covered by the image.
imageView.image.size isn't necessarily the frame of the imageView itself, try setting the
scrollview.frame = imageView.frame
and then
scrollView.contentSize = imageView.image.size
Then you won't see any border. If you want the image to be the maximum size to start with,
do
imageView.frame = image.size;
[imageView setImage:image];
scrollView.frame = self.view.frame; //or desired size
[scrollView addSubView:imageView];
[scrollView setContentSize:image.size]; //or imageView.frame.size
To fix this, I ended up declaring a new CGRect , setting its origin to my scrollView's origin, setting its size with the bounds of my view, and then assigning this CGRect back to my scrollview frame
CGRect scrollFrame;
scrollFrame.origin = self.scrollView.frame.origin;
scrollFrame.size = CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
self.scrollView.frame = scrollFrame;

UIView.center = UIImageView.center but image view doesn't seem to be in centre in landscape

I am making application for iPad, only landscape mode will be supported. I am having a UIView and later I am dynamically adding UIImageView as a subview. However my goal is to add images in centre of the UIView. So I used this code,
[imageView setCenter:dynamicMainView.center];
where imageView is UIImageView(Obvious :)) and dynamicMainView is UIView, However final outcome docent seem to be in centre,
Visual Representation
Full method code for adding UIImageView in UIView is,
-(void) addImageIntoMainDynamicView:(UIImage *) image
{
[self clearImageFromMainDynamicView];//Always clear Dynamic main view before adding new views
imageView = [[UIImageView alloc] initWithImage:image];
if(imageView.bounds.size.width > dynamicMainView.bounds.size.width || imageView.bounds.size.height > dynamicMainView.bounds.size.height)
{
[imageView setFrame:[dynamicMainView bounds]];
}
[imageView setCenter:dynamicMainView.center];
NSLog(#"Image : X = %f and Y = %f", imageView.center.x,imageView.center.y );
NSLog(#"UIView : X = %f and Y = %f", dynamicMainView.center.x,dynamicMainView.center.y );
[dynamicMainView addSubview:imageView];
[imageView release];
}
And above log value is,
2011-12-21 21:07:11.850 Map1TestApp[94645:11603] Image : X = 512.000000 and Y = 371.500000
2011-12-21 21:07:11.853 Map1TestApp[94645:11603] UIView : X = 512.000000 and Y = 371.500000
Any clue about why it's not adding in centre? Am I doing something wrong?
FOR FUTURE VIEWER THE ANWSER WAS :
[imageView setCenter:CGPointMake(CGRectGetMidX([dynamicMainView bounds]), CGRectGetMidY([dynamicMainView bounds]))];
The center of a view is expressed in it's superview's coordinate system. So you are centering your image view in the wrong coordinate system. (dynamic view's superview as opposed to dynamic view)
To centre view A within view B, the centre point of view A has to be the centre of the bounds rectangle of view B. You can get this using (from memory!) CGRectGetMidX and CGRectGetMidY.
You should check the size of the image. Frame of the image may not be the frame of the UIImageView. Set the background color of the UIImageView to something like [UIColor redColor] and clipToBounds to true and see what'll happen. If there are red parts that are visible the problem is with the contentMode property of your imageView object.

UIScrollView setZoomScale not working?

I am struggeling with my UIScrollview to get it to zoom-in the underlying UIImageView. In my view controller I set
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return myImageView;
}
In the viewDidLoad method I try to set the zoomScale to 2 as follows (note the UIImageView and Image is set in Interface Builder):
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
myScrollView.delegate = self;
NSLog(#"zoomScale: %.1f, minZoolScale: %.3f", myScrollView.zoomScale, myScrollView.minimumZoomScale);
}
I tried a few variations of this, but the NSLog always shows a zoomScale of 1.0.
Any ideas where I screw this one up?
I finally got this to work. what caused the problem was the delegate call being at the end. I now moved it up and .... here we go.
New code looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.delegate = self;
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
}
Here is another example I made. This one is using an image that is included in the resource folder. Compared to the one you have this one adds the UIImageView to the view as a subview and then changes the zoom to the whole view.
-(void)viewDidLoad{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"random.jpg"];
imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
[(UIScrollView *) self.view setContentSize:[image size]];
[(UIScrollView *) self.view setMaximumZoomScale:2.0];
[(UIScrollView *) self.view setMinimumZoomScale:0.5];
}
I know this is quite late as answers go, but the problem is that your code calls zoomScale before it sets the delegate. You are right the other things in there don't require the delegate, but zoomScale does because it has to be able to call back when the zoom is complete. At least that's how I think it works.
My code must be completely crazy because the scale that I use is completely opposite to what tutorials and others are doing. For me, minScale = 1 which indicates that the image is fully zoomed out and fits the UIImageView that contains it.
Here's my code:
[self.imageView setImage:image];
// Makes the content size the same size as the imageView size.
// Since the image size and the scroll view size should be the same, the scroll view shouldn't scroll, only bounce.
self.scrollView.contentSize = self.imageView.frame.size;
// despite what tutorials say, the scale actually goes from one (image sized to fit screen) to max (image at actual resolution)
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat minScale = 1;
// max is calculated by finding the max ratio factor of the image size to the scroll view size (which will change based on the device)
CGFloat scaleWidth = image.size.width / scrollViewFrame.size.width;
CGFloat scaleHeight = image.size.height / scrollViewFrame.size.height;
self.scrollView.maximumZoomScale = MAX(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
// ensure we are zoomed out fully
self.scrollView.zoomScale = minScale;
This works as I expect. When I load the image into the UIImageView, it is fully zoomed out. I can then zoom in and then I can pan the image.