I have an instance of UIScrollView with images inside it.
It is possible to scroll the scroll view horizontally, and view more images.
I want to magnify the images, according the their distance from the middle of the scroll view, so it should look like this.
I thought about the simplest way to do that, using the following code:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
//Change the frames of all the visible images here.
}
This way does not work smoothly on some of the preceding devices, such as iPod 2G.
Is there a better way to perform the image magnification than the one I described?
If so, please let me know.
Thanks!
EDIT:
This is the implementation of the body of the method above:
-(void) magnifyImages {
//This method magnifies all the images.
for (NSString *name in [imagesDict allKeys]) {
UIImageView *imageView = [imagesDict objectForKey:name];
[self magnifyImageView:imageView];
}
}
static float kMaxMagnification = 1.5;
-(void) magnifyImageView:(UIImageView *)imageView {
CGRect frame = imageView.frame;
float imageMiddleLine = frame.origin.x+frame.size.width/2-scrollView.contentOffset.x;
//Check if the image's middle line is visible = whether it is needed to magnify the image.
if (imageMiddleLine +frame.size.width/2>0
&& imageMiddleLine-frame.size.width/2<scrollView.frame.size.width) {
float magnification = fabs(160-imageMiddleLine);
//Mathematical formula that calculates the magnification.
magnification = (kMaxMagnification-1)*(kDeviceWidth/2-magnification)/160+1;
//'normalImageSize' is a property of the image that returns the image's normal size (when not magnified).
CGSize imgSize = imageView.normalImageSize;
frame=CGRectMake(frame.origin.x-(imgSize.width*magnification-frame.size.width)/2,
frame.origin.y-(imgSize.height*magnification-frame.size.height)/2,
imgSize.width*magnification, imgSize.height*magnification);
imageView.frame=frame;
}
}
Related
I just try to create my own layout. I used UITableView for my UIViewController. I have some JSON response from server. This is like detailed publication. Also i calculate my UITableViewCell height because my publication contain mix of images and text. I wrote own layout and recalculate when device in rotation.
for (UIView * sub in contentSubviews) {
if([sub isKindOfClass:[PaddingLabel class]])
{
PaddingLabel *textPart = (PaddingLabel *)sub;
reusableFrame = textPart.frame;
reusableFrame.origin.y = partHeight;
partHeight += textPart.frame.size.height;
[textPart setFrame:reusableFrame];
} else if([sub isKindOfClass:[UIImageView class]])
{
UIImageView * imagePart = (UIImageView *)sub;
reusableFrame = imagePart.frame;
reusableFrame.origin.y = partHeight;
reusableFrame.origin.x = screenWidth/2 - imageSize.width/2;
reusableFrame.size.width = imageSize.width;
reusableFrame.size.height = imageSize.height;
[imagePart setFrame:reusableFrame];
partHeight += imagePart.frame.size.height;
}
}
But I have some issue.
When device change orientation state UIScrollView offset is same as was. I don't know how to change it.
Before rotation:
After rotation:
I want to save visible elements in rect.
Suggest pls.
There are a couple of choices, depending on what you wish to show after rotation. One possible solution is get contentOffset.y/contentSize.height before rotation, then multiple it to new contentSize.height to derive the new contentOffset.y. Or if you wish to keep a specific element visible before and after rotation, get the difference between element_view.frame.origin.y and contentOffset.y, then set new contentOffset.y to be element_view.frame.origin.y + difference.
Typically, the scrollView's content view is a rectangle. But I would like to implement that is not a rectangle.... For example....
The yellow, Grid 6 is the current position...Here is the example flow:
User swipe to left. (cannot scroll to left) Current: 6.
User swipe to right. (scroll to right) Current: 7.
User swipe to down. (scroll to down) Current: 8.
User swipe to down. (cannot scroll to down) Current: 8.
As you can see, the Content view of the scrollView is not rectangle. Any ideas on how to implement it? Thanks.
This is an interesting idea to implement. I can think of a few approaches that might work. I tried out one, and you can find my implementation in my github repository here. Download it and try it out for yourself.
My approach is to use a normal UIScrollView, and constrain its contentOffset in the delegate's scrollViewDidScroll: method (and a few other delegate methods).
Preliminaries
First, we're going to need a constant for the page size:
static const CGSize kPageSize = { 200, 300 };
And we're going to need a data structure to hold the current x/y position in the grid of pages:
typedef struct {
int x;
int y;
} MapPosition;
We need to declare that our view controller conforms to the UIScrollViewDelegate protocol:
#interface ViewController () <UIScrollViewDelegate>
#end
And we're going to need instance variables to hold the grid (map) of pages, the current position in that grid, and the scroll view:
#implementation ViewController {
NSArray *map_;
MapPosition mapPosition_;
UIScrollView *scrollView_;
}
Initializing the map
My map is just an array of arrays, with a string name for each accessible page and [NSNull null] at inaccessible grid positions. I'll initialize the map from my view controller's init method:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
[self initMap];
}
return self;
}
- (void)initMap {
NSNull *null = [NSNull null];
map_ = #[
#[ #"1", null, #"2"],
#[ #"3", #"4", #"5" ],
#[ null, #"6", #"7" ],
#[ null, null, #"8" ],
];
mapPosition_ = (MapPosition){ 0, 0 };
}
Setting up the view hierarchy
My view hierarchy will look like this:
top-level view (gray background)
scroll view (transparent background)
content view (tan background)
page 1 view (white with a shadow)
page 2 view (white with a shadow)
page 3 view (white with a shadow)
etc.
Normally I'd set up some of my views in a xib, but since it's hard to show xibs in a stackoverflow answer, I'll do it all in code. So in my loadView method, I first set up a “content view” that will live inside the scroll view. The content view will contain a subviews for each page:
- (void)loadView {
UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [map_[0] count] * kPageSize.width, map_.count * kPageSize.height)];
contentView.backgroundColor = [UIColor colorWithHue:0.1 saturation:0.1 brightness:0.9 alpha:1];
[self addPageViewsToContentView:contentView];
Then I'll create my scroll view:
scrollView_ = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kPageSize.width, kPageSize.height)];
scrollView_.delegate = self;
scrollView_.bounces = NO;
scrollView_.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleRightMargin
| UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleBottomMargin);
I add the content view as a subview of the scroll view and set up the scroll view's content size and offset:
scrollView_.contentSize = contentView.frame.size;
[scrollView_ addSubview:contentView];
scrollView_.contentOffset = [self contentOffsetForCurrentMapPosition];
Finally, I create my top-level view and give it the scroll view as a subview:
UIView *myView = [[UIView alloc] initWithFrame:scrollView_.frame];
[myView addSubview:scrollView_];
myView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
self.view = myView;
}
Here's how I compute the scroll view's content offset for the current map position, and for any map position:
- (CGPoint)contentOffsetForCurrentMapPosition {
return [self contentOffsetForMapPosition:mapPosition_];
}
- (CGPoint)contentOffsetForMapPosition:(MapPosition)position {
return CGPointMake(position.x * kPageSize.width, position.y * kPageSize.height);
}
To create subviews of the content view for each accessible page, I loop over the map:
- (void)addPageViewsToContentView:(UIView *)contentView {
for (int y = 0, yMax = map_.count; y < yMax; ++y) {
NSArray *mapRow = map_[y];
for (int x = 0, xMax = mapRow.count; x < xMax; ++x) {
id page = mapRow[x];
if (![page isKindOfClass:[NSNull class]]) {
[self addPageViewForPage:page x:x y:y toContentView:contentView];
}
}
}
}
And here's how I create each page view:
- (void)addPageViewForPage:(NSString *)page x:(int)x y:(int)y toContentView:(UIView *)contentView {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectInset(CGRectMake(x * kPageSize.width, y * kPageSize.height, kPageSize.width, kPageSize.height), 10, 10)];
label.text = page;
label.textAlignment = NSTextAlignmentCenter;
label.layer.shadowOffset = CGSizeMake(0, 2);
label.layer.shadowRadius = 2;
label.layer.shadowOpacity = 0.3;
label.layer.shadowPath = [UIBezierPath bezierPathWithRect:label.bounds].CGPath;
label.clipsToBounds = NO;
[contentView addSubview:label];
}
Constraining the scroll view's contentOffset
As the user moves his finger around, I want to prevent the scroll view from showing an area of its content that doesn't contain a page. Whenever the scroll view scrolls (by updating its contentOffset), it sends scrollViewDidScroll: to its delegate, so I can implement scrollViewDidScroll: to reset the contentOffset if it goes out of bounds:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint contentOffset = scrollView_.contentOffset;
First, I want to constrain contentOffset so the user can only scroll horizontally or vertically, not diagonally:
CGPoint constrainedContentOffset = [self contentOffsetByConstrainingMovementToOneDimension:contentOffset];
Next, I want to constrain contentOffset so that it only shows parts of the scroll view that contain pages:
constrainedContentOffset = [self contentOffsetByConstrainingToAccessiblePoint:constrainedContentOffset];
If my constraints modified contentOffset, I need to tell the scroll view about it:
if (!CGPointEqualToPoint(contentOffset, constrainedContentOffset)) {
scrollView_.contentOffset = constrainedContentOffset;
}
Finally, I update my idea of the current map position based on the (constrained) contentOffset:
mapPosition_ = [self mapPositionForContentOffset:constrainedContentOffset];
}
Here's how I compute the map position for a given contentOffset:
- (MapPosition)mapPositionForContentOffset:(CGPoint)contentOffset {
return (MapPosition){ roundf(contentOffset.x / kPageSize.width),
roundf(contentOffset.y / kPageSize.height) };
}
Here's how I constrain the movement to just horizontal or vertical and prevent diagonal movement:
- (CGPoint)contentOffsetByConstrainingMovementToOneDimension:(CGPoint)contentOffset {
CGPoint baseContentOffset = [self contentOffsetForCurrentMapPosition];
CGFloat dx = contentOffset.x - baseContentOffset.x;
CGFloat dy = contentOffset.y - baseContentOffset.y;
if (fabsf(dx) < fabsf(dy)) {
contentOffset.x = baseContentOffset.x;
} else {
contentOffset.y = baseContentOffset.y;
}
return contentOffset;
}
Here's how I constrain contentOffset to only go where there are pages:
- (CGPoint)contentOffsetByConstrainingToAccessiblePoint:(CGPoint)contentOffset {
return [self isAccessiblePoint:contentOffset]
? contentOffset
: [self contentOffsetForCurrentMapPosition];
}
Deciding whether a point is accessible turns out to be the tricky bit. It's not enough to just round the point's coordinates to the nearest potential page center and see if that rounded point represents an actual page. That would, for example, let the user drag left/scroll right from page 1, revealing the empty space between pages 1 and 2, until page 1 is half off the screen. We need to round the point down and up to potential page centers, and see if both rounded points represent valid pages. Here's how:
- (BOOL)isAccessiblePoint:(CGPoint)point {
CGFloat x = point.x / kPageSize.width;
CGFloat y = point.y / kPageSize.height;
return [self isAccessibleMapPosition:(MapPosition){ floorf(x), floorf(y) }]
&& [self isAccessibleMapPosition:(MapPosition){ ceilf(x), ceilf(y) }];
}
Checking whether a map position is accessible means checking that it's in the bounds of the grid and that there's actually a page at that position:
- (BOOL)isAccessibleMapPosition:(MapPosition)p {
if (p.y < 0 || p.y >= map_.count)
return NO;
NSArray *mapRow = map_[p.y];
if (p.x < 0 || p.x >= mapRow.count)
return NO;
return ![mapRow[p.x] isKindOfClass:[NSNull class]];
}
Forcing the scroll view to rest at page boundaries
If you don't need to force the scroll view to rest at page boundaries, you can skip the rest of this. Everything I described above will work without the rest of this.
I tried setting pagingEnabled on the scroll view to force it to come to rest at page boundaries, but it didn't work reliably, so I have to enforce it by implementing more delegate methods.
We'll need a couple of utility functions. The first function just takes a CGFloat and returns 1 if it's positive and -1 otherwise:
static int sign(CGFloat value) {
return value > 0 ? 1 : -1;
}
The second function takes a velocity. It returns 0 if the absolute value of the velocity is below a threshold. Otherwise, it returns the sign of the velocity:
static int directionForVelocity(CGFloat velocity) {
static const CGFloat kVelocityThreshold = 0.1;
return fabsf(velocity) < kVelocityThreshold ? 0 : sign(velocity);
}
Now I can implement one of the delegate methods that the scroll view calls when the user stops dragging. In this method, I set the targetContentOffset of the scroll view to the nearest page boundary in the direction that the user was scrolling:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (fabsf(velocity.x) > fabsf(velocity.y)) {
*targetContentOffset = [self contentOffsetForPageInHorizontalDirection:directionForVelocity(velocity.x)];
} else {
*targetContentOffset = [self contentOffsetForPageInVerticalDirection:directionForVelocity(velocity.y)];
}
}
Here's how I find the nearest page boundary in a horizontal direction. It relies on the isAccessibleMapPosition: method, which I already defined earlier for use by scrollViewDidScroll::
- (CGPoint)contentOffsetForPageInHorizontalDirection:(int)direction {
MapPosition newPosition = (MapPosition){ mapPosition_.x + direction, mapPosition_.y };
return [self isAccessibleMapPosition:newPosition] ? [self contentOffsetForMapPosition:newPosition] : [self contentOffsetForCurrentMapPosition];
}
And here's how I find the nearest page boundary in a vertical direction:
- (CGPoint)contentOffsetForPageInVerticalDirection:(int)direction {
MapPosition newPosition = (MapPosition){ mapPosition_.x, mapPosition_.y + direction };
return [self isAccessibleMapPosition:newPosition] ? [self contentOffsetForMapPosition:newPosition] : [self contentOffsetForCurrentMapPosition];
}
I discovered in testing that setting targetContentOffset did not reliably force the scroll view to come to rest on a page boundary. For example, in the iOS 5 simulator, I could drag right/scroll left from page 5, stopping halfway to page 4, and even though I was setting targetContentOffset to page 4's boundary, the scroll view would just stop scrolling with the 4/5 boundary in the middle of the screen.
To work around this bug, we have to implement two more UIScrollViewDelegate methods. This one is called when the touch ends:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[scrollView_ setContentOffset:[self contentOffsetForCurrentMapPosition] animated:YES];
}
}
And this one is called when the scroll view stops decelerating:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGPoint goodContentOffset = [self contentOffsetForCurrentMapPosition];
if (!CGPointEqualToPoint(scrollView_.contentOffset, goodContentOffset)) {
[scrollView_ setContentOffset:goodContentOffset animated:YES];
}
}
The End
As I said at the beginning, you can download my test implementation from my github repository and try it out for yourself.
That's all, folks!
I'm assuming you're using the UIScrollView in paged mode (swipe to show an entire new screen).
With a bit jiggery-pokery you can achieve the effect you want.
The trick is to ensure that whatever square you're currently viewing, you have the UIScrollView configured so that only the visible central view, and the surrounding view that you could scroll too, are added to the scroll view (and at the correct offset). You also must ensure that the size of the scrollable content (and the current offset) is set correctly, to prevent scrolling in a direction that would take you to no content.
Example: suppose you're viewing square 6 currently. At that point, your scroll view would just have 4 views added to it: 4, 5, 6 and 7, in the correct relative offsets. And you set the content size of the scroll view to be equivelant to 2 x 2 squares size. This will prevent scrolling down or to the left (where there are no tiles) but will allow scrolling in the correct direction.
You'll need your delegate to detect scrollViewDidEndDecelerating:. In that instance, you then have to set up your views, content offset, and content size as described above, for the new location.
I want to implement iPhone Photo App (default iPhone app). I faced difficulties, when I want to load big image (2500 * 3700). When I want to scroll from one image to another, I see something like stuttering. To display images I use ImageScrollView from apple site It has displaying method: ImageScrollView.m
- (void)displayImage:(UIImage *)image
{
// clear the previous imageView
[imageView removeFromSuperview];
[imageView release];
imageView = nil;
// reset our zoomScale to 1.0 before doing any further calculations
self.zoomScale = 1.0;
self.imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[self addSubview:imageView];
self.contentSize = [image size];
[self setMaxMinZoomScalesForCurrentBounds];
self.zoomScale = self.minimumZoomScale;
}
- (void)setMaxMinZoomScalesForCurrentBounds
{
CGSize boundsSize = self.bounds.size;
CGSize imageSize = imageView.bounds.size;
// calculate min/max zoomscale
CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
// on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
// maximum zoom scale to 0.5.
CGFloat maxScale = 1.0 / [[UIScreen mainScreen] scale];
// don't let minScale exceed maxScale. (If the image is smaller than the screen, we don't want to force it to be zoomed.)
if (minScale > maxScale) {
minScale = maxScale;
}
self.maximumZoomScale = maxScale;
self.minimumZoomScale = minScale;
}
For my app I have 600*600 images, I show them first. When user scrolls to next image he sees only 600*600 image. Then in background I load 3600 * 3600 image
[operationQueue addOperationWithBlock:^{
UIImage *image = [self getProperBIGImage];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
ImageScrollView *scroll = [self getCurrentScroll];
[scroll displayImage:newImage];
}];
}];
I see, that when the dimensions of image are 3600 * 3600 and I want to display image in 640 * 960 screen, iPhone waste 1 second of main queue time to scale the image, and that's why I can't scroll to next image during this 1 second.
I want to scale image, because I need user to be able to zoom this image. I tried to use this approach, but this didn't help.
I see some possible solutions:
1) to provide scaling of image in UIImageView in background (but I know, that UI should be changed only in main thread)
2) to use - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollViewCalled to show only 600 * 600 image at the beginning and then load big image, when user tries to zoom (but I tried this, and I will loose 1 second, when I try to init UIImageView with bigImage and then return this UIImageView; And I can't even implement it, because I see bad scroll view, where scrolling behavior is wrong (difficault to explain), when I try to return different view for different scales)
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollViewCalled
{
if (!zooming)
{
ImageScrollView *scroll = (ImageScrollView *)scrollViewCalled;
UIImageView *imageView = (UIImageView *)[scroll imageView];
return imageView;
}
else
{
UIImageView *bigImageView = [self getBigImageView];
return bigImageView;
}
}
Unfortunately, that image is too large for the iPhone to handle. I know on the iPad, the limit is roughly the size of the device screen. Any bigger than that and you'll have to use a CATiledLayer.
I would take a look at the WWDC UIScrollView presentations over the last three year, starting with 2010. In that, they discuss ways to handle large images on the iPhone. The sample code will also get you on your way.
Good luck!
I've got a UIView which I'm rendering to a UIImage via the typical UIGraphicsBeginImageContextWithOptions method, using a scale of 2.0 so the image output will always be the "retina display" version of what would show up onscreen, regardless of the user's actual screen resolution.
The UIView I'm rendering contains both images and text (UIImages and UILabels). The image is appearing in the rendered UIImage at its full resolution, and looks great. But the UILabels appear to have been rasterized at a 1.0 scale and then upscaled to 2.0, resulting in blurry text.
Is there something I'm doing wrong, or is there some way to get the text to render nice and crisp at the higher scale level? Or is there some way to do this other than using the scaling parameter of UIGraphicsBeginImageContextWithOptions that would have better results? Thanks!
The solution is to change the labels's contentsScale to 2 before you draw it, then set it back immediately thereafter. I just coded up a project to verify it, and its working just fine making a 2x image in a normal retina phone (simulator). [If you have a public place I can put it let me know.]
EDIT: the extended code walks the subviews and any container UIViews to set/unset the scale
- (IBAction)snapShot:(id)sender
{
[self changeScaleforView:snapView scale:2];
UIGraphicsBeginImageContextWithOptions(snapView.bounds.size, snapView.opaque, 2);
[snapView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
imageDisplay.image = img; // contentsScale
imageDisplay.contentMode = UIViewContentModeScaleAspectFit;
[self changeScaleforView:snapView scale:1];
}
- (void)changeScaleforView:(UIView *)aView scale:(CGFloat)scale
{
[aView.subviews enumerateObjectsUsingBlock:^void(UIView *v, NSUInteger idx, BOOL *stop)
{
if([v isKindOfClass:[UILabel class]]) {
v.layer.contentsScale = scale;
} else
if([v isKindOfClass:[UIImageView class]]) {
// labels and images
// v.layer.contentsScale = scale; won't work
// if the image is not "#2x", you could subclass UIImageView and set the name of the #2x
// on it as a property, then here you would set this imageNamed as the image, then undo it later
} else
if([v isMemberOfClass:[UIView class]]) {
// container view
[self changeScaleforView:v scale:scale];
}
} ];
}
Try rendering to an image with double size, and then create the scaled image:
UIGraphicsBeginImageContextWithOptions(size, NO, 1.0);
// Do stuff
UImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
newImage=[UIImage imageWithCGImage:[newImage CGImage] scale:2.0 orientation:UIImageOrientationUp];
Where:
size = realSize * scale;
I have been struggling with much the same oddities in the context of textview to PDF rendering. I found out that there are some documented properties on the CALayer objects which make up the view. Maybe setting the rasterizationScale of the relevant (sub)layer(s) helps.
I'm implementing a subclass of UIView that displays a gauge dial with a sprite for the indicator. It has angle property that I can vary to make the needle point to different angles. It works, but on the same values for the position of the needle make it show up in different locations on the phone and the simulator. It's an iPhone 4, so I'm sure the double resolution thing is behind this, but I don't know what to do about it. I tried setting the UIView's layer's contentScaleFactor but that fails. I thought UIView got the resolution thing for free. Any suggestions?
I should note that the NSLog statements report 150 for both .frame.size. dimensions, in both the simulator and the device.
Here's the .m file
UPDATE: In the simulator, I found how to set the hardware to iPhone 4, and it looks just like the device now, both are scaling and positioning the sprite at half size.
UPDATE 2: I made a workaround. I set the .scale of my sprite equal to the UIView's contentScaleFactor and then use it to dived the UIView in half if it's a lo-res screen and the full width if it's hi-res. I still don't see why this is necessary, as I should be working in points now, not pixels. It must have something to do with the custom drawing code in the Sprite or VectorSprite classes.
I'd still appreciate some feedback if anyone has some...
#import "GaugeView.h"
#implementation GaugeView
#synthesize needle;
#define kVectorArtCount 4
static CGFloat kVectorArt[] = {
3,-4,
2,55,
-2,55,
-3,-4
};
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
needle = [VectorSprite withPoints:kVectorArt count:kVectorArtCount];
needle.scale = (float)self.contentScaleFactor; // returns 1 for lo-res, 2 for hi-res
NSLog(#" needle.scale = %1.1f", needle.scale);
needle.x = self.frame.size.width / ((float)(-self.contentScaleFactor) + 3.0); // divisor = 1 for hi-res, 2 for lo-res
NSLog(#" needle.x = %1.1f", needle.x);
needle.y = self.frame.size.height / ((float)(-self.contentScaleFactor) + 3.0);
NSLog(#" needle.y = %1.1f", needle.y);
needle.r = 0.0;
needle.g = 0.0;
needle.b = 0.0;
needle.alpha = 1.0; }
}
self.backgroundColor = [UIColor clearColor];
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGAffineTransform t0 = CGContextGetCTM(context);
t0 = CGAffineTransformInvert(t0);
CGContextConcatCTM(context, t0);
[needle updateBox];
[needle draw: context];
}
- (void)dealloc {
[needle release];
[super dealloc];
}
#end
I believe the answer is that iOS takes care of the resolution scaling automatically in drawRect methods, but in custom drawing code, you have to do it yourself.
In my example, I used the UIView's contentsScaleFactor to scale my sprite. In the future, in my custom draw method (not shown) I'll query [UIScreen mainScreen] scale and scale accordingly there.