I'm new to Obj-C/Cocoa programming and facing to a problem about NSImageView.
My goal is to create an app which allow the user to put an image on it with drag&drop.
The problem is the following : when we're dropping an image larger than the window, it resizes the window to the image size, I don't want that !
I put my NSImageView into a NSScrollView so only a certain part of the image MUST be showed and the user will move sliders to view the entire one (or simply resize the window), that's what I want..
Here is my XIB hierarchy (concerned part) :
View
NSScrollView
View
NSImageView
In the code of my NSImageView, I have (about that problem) :
[self setImageScaling:NSScaleProportionally];
[self setImage:newImage];
NOTE: The sliders seem to move the view and not the image
NOTE2: I can't resize the window to a lower size than the image
I have a horrible aversion to nib files and prefer to do things programmatically whenever possible so I'm not much help with that... but here's a little listing from a little NSScrollview test app I recently coded up that does something similar. The only non-self-contained thing mentioned in this code chunk is the window name, which refers to an NSWindow * for a window that was already created. And self here just refers to a controller object.
NSImage * myImage = [NSImage imageNamed:#"myfilename.png"];
NSRect imageRect = NSMakeRect(0.0,0.0,myImage.size.width,myImage.size.height);
self.myImageView = [[NSImageView alloc] initWithFrame:imageRect];
self.myImageView.bounds = imageRect;
self.myImageView.image = myImage;
self.myScrollView = [[NSScrollView alloc] initWithFrame:[window.contentView frame]];
self.myScrollView.hasVerticalScroller = YES;
self.myScrollView.hasHorizontalScroller = YES;
self.myScrollView.documentView = self.myImageView;
self.myScrollView.borderType = NSNoBorder;
self.myScrollView.scrollerStyle = NSScrollerStyleOverlay;
window.contentView = self.myScrollView;
I know that's perhaps not the solution you were looking for but maybe it helps you debug? In particular, I didn't do anything funny with my NSImageView besides give it a frame, a bounds rectangle, and an NSImage *, so I don't think you need to worry about the scaling parameter per se -- in fact, if you want the image to be full-sized and to scroll around it, you want your NSImageView's frame to be the same size as the image so that it doesn't HAVE to scale at all. Maybe that's your problem -- the frame is too small? It is OK for the NSImageView frame to be bigger than the window -- that's what lets you scroll around it.
To me it sounds like your views don't have the proper sizes. Because of bouncing and because your views don't clip their subviews, you can still scroll a bit, but it returns to its original position.
Check if the NSImageView is big enough to display your full image. (Check frame or bounds for width and height)
If it is not big enough do this (inside the method, where you set the new image):
self.myImageView.frame = NSMakeRect(0.0,0.0, self.myImageView.image.size.width, self.myImageView.image.size.height);
Adjust the size of your scroll views view accordingly.
self.myScrollView.view.frame = self.myImageView.frame;
Now your scrollable area should be big enough to cover the full image.
If this works for you, give Matt some credit as well, by up-voting his answer, since his answer does essentially the same thing - only code based, instead of initialising everything using a nib file - and the code I used is very similar to what he had already posted.
I suggest to put the "image view" inside of the custom view (edges to its superview).
The image view's constraints to the equal edges, except the bottom constraint, which to the "Less Than or Equal" relation to its superview.
Related
I am developing a iOS-6 app. I have a UIViewController with a view that needs fixed orientation (portrait mode). But when the phone is rotated, one control on that view needs to be moved and rotated (so that it will always be in the upper left corner, and its text will be readable).
I am achieving this by shifting the control(a UIView) using the frame-property of my control (it is a custom view, more on that later), and then using CGAffineTRansformMakeRotate() afterwards, since I know that it's not advisable to use the frame after rotating a view. Everything is fine so far, but here's the thing: That custom view has three UIButtons of type UIButtonTypeCustom as its subviews. Because I rotated the View, but cannot rotate the buttons inside the view (they are not squares), I need to rotate the titleLabels of the Buttons for the text to be readable in the new deviceOrientation.
But it won't work very well. The text will be rotated, as I intended, but it will be clipped by the titleLabel, because the titleLabel has the wrong frame. I checked this by applying borders to the label. So I need to change the titleLabels frame, right? But how can I do that? I tried setting it using [titleLabel setFrame: frameThatFits];, but to no avail. (frameThatFits is a CGRect I created). Also, calling [button.titleLabel sizeToFit]; has no effect that I could see.
I am using [button setTitle:title forControlState: UIControlStateNormal];to set the title.
TL;DR: I'm trying to change the frame/bounds of a UIButtons titleLabel after rotating it using an affine transformation. Any help?
Thanks.
PS: I can supply code when needed, but I wouldn't know what to show you. Tell me what you need, I'll post it.
OK, first of all, thanks to everyone who tried to help. Im posting an alternative solution for my problem, and although it doesnt really address the problem of changing the titleLabels dimensions, it will result in the proper display of my ViewController.
It turns out using the frame is a bad idea. I initially used the frame to reposition the view and i figured that this couldnt be a problem because i only ever applied transformations afterwards, but i was wrong. Because OBVIOUSLY i tried to change the titleLabels frame. AFTER the rotation. And that didnt work.
So the way to go here is using the center-property and the bouds of the view consistently throughout the code. It will result in properly rotated Buttons, that do not need any fidgeting afterwards.
My takeaway here is that i will never ever again use the frame-property outside of a NSLog-statement. But why [button sizeToFit];wouldnt yield any results is still beyond me. If i ever figure it out, i might post it if i remember.
EDIT:
#ZevEisenberg nailed it with this comment:
“Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.” So you are right to use the center and bounds here, but if you do not have a transform, the frame is perfectly safe to use.
NEXT EDIT:
Heres how i ended up repositioning the Buttons:
-(CGPoint)centerForView:(UIView *)view{
//calculate a suitableposition for the view
//depending on the current orientation and the device type (iphone 4S/5, etc)
return point;
}
Then, as a reaction to the deviceOrientation change notification, i apply CGAffineTransformIdentity to all the views, reposition them using my centerForView shown above, and apply the correct rotation transformation to the View. I do this for all the subviews every time the divice rotates, like so:
-(void)setRightRotationTransformations{
[self resetAllTransformations];
self.someSubview.transform = CGAffineTransformRotate(self.someSubview.transform, -M_PI_2);
}
In my case works such hack:
set Line Break mode to Word Wrap
Add extra line to title (even for one line title)
When making a photo viewer app found that our UIImageView controller is drawing its image outside its frame when the content mode is different neither ScaleToFill nor Aspect Fit.
Trying to understand why; I isolated the problem making a new project which only has a UIImageView with the following frame (50,50,100,100). The image size contained in it is (4592,3056).
After running the app, with the content mode set to ScaleToFill and AspectFit it all worked as expected:
But after setting the contentMode of the UIImageView to TopLeft, the image is drawn outside its frame, the odd thing is that the log from the frame after all has been drawn is still the original {50,50,100,100}.
I've try to understand the issue by moving the Autoresize, the clips and the content mode of the UIViewController but the result is the same.
set clipToBounds = YES on the view.
its NO by default because that makes drawing way cheaper
I can't find an answer for this one.
I would like to know how to have the image size in a calayer's to be lower than calayer's bound's size.
I've got several pawns in an iPad game, each is a CALayer and I have them resize simply with a contentsGravity=kCAGravityResizeAspect. Image is 128x128 inside of a CALayer of 30x30 so the image gets resized automatically to 30x30 and because of both being a box, aspect ratio maintains and works.
Here I set CALayer's bounds proportional relative to superview's size, so the Pawns always present the same relative size to the view. This one is inside my sprite class subclass of calayer:
-(void) setSpriteScaleToDice {
CGFloat newSize = [self superlayer].bounds.size.width * 0.066666667f;
self.bounds=CGRectMake(0.0f, 0.0f, newSize, newSize);
self.contentsGravity = kCAGravityResizeAspect;
}
Note that in my case the CALayer bounds gets a maximum of 30x30 which is small for a touch. That's the problem I'm facing, due to this small size it's difficult to "touch" them, sometimes touch fails...
One of the ideas that I'm thinking is to increase the "bounds" of the calayer, while keeping the image at its original size. The problem is that I've search a lot and tried several options with contentsGravity, contentsCenter, contentsScale, etc... without success.
In particular, as per apple docs looks like the way to go is with contentsCenter (and not using contentsGravity), however I get deformation in the bitmap...
Please, any idea is really welcome, and thanks in advance,
Luis
This is probably a silly question, but why are you using CALayers for this instead of UIViews? UIImageView has a contentMode property that lets you do this easily (not to mention being easier to use for touch event handling).
That said, CALayer has a contentsRect property that appears to let you define a sub-rectangle for contents to be drawn within, so that may let you do what you want.
Another option would be to place your image layer inside a larger layer and use that for the hit test.
CAlayer CGFloat contentsScale
/* Defines the scale factor applied to the contents of the layer. If
* the physical size of the contents is '(w, h)' then the logical size
* (i.e. for contentsGravity calculations) is defined as '(w /
* contentsScale, h / contentsScale)'. Applies to both images provided
* explicitly and content provided via -drawInContext: (i.e. if
* contentsScale is two -drawInContext: will draw into a buffer twice
* as large as the layer bounds). Defaults to one. Animatable. */
If you want your image drawn in the CALayer at a size other than the CALayer you need to create your own drawInContext: method and draw the image rather than setting the CALayer's contents property. Do not set the contents property, create your own to track the image you want to draw.
I am putting an image into an IKImageView, and immediately sizing it to fit. Whenever I do this, the image originally appears at 1-1 size (huge) and then resizes down, which would be fine if the animation was smooth. However, the animation looks ... fluttery? There are big blocks, like 2 inches square, of the image that appear and shrink independently of each other. The effect is a little annoying, and almost to the level where it might give an epileptic a seizure... (I'm exaggerating a little).
Is this a bug in IKImageView? Is it a bug in the animation? Will it go away if I turn off the animation (How do I do that? setAnimates: NO doesn't seem to do anything, nor does overloading animates to return NO in my subclass...
EDIT: addedcode:
NSImage* image = [doc currentImage];
[imageView setImage: image];
[imageView zoomImageToFit: self];
This is in the app controller, so self is the application (or plugin, depending on which version I'm looking at)
If you tell the IKImageView to autoresize, then it won't animate the new image to the new size and the artefacts will not be a problem. The image will be sized to fit in the image view immediately.
imageView.autoresizes = YES;
I've written a screen saver that displays a web page. It works exactly as I want it to on my main display, but in the preview and secondary displays, the web view is hanging off the top of the screen.
Example (from preview):
screen saver bug http://img.skitch.com/20081212-nk5cqrgfds1funr1a3p72aw25q.preview.jpgUploaded with plasq's Skitch!
My code is pretty straightforward. From within initWithFrame:isPreview: I have the following code:
webview = [[WebView alloc] initWithFrame:frame
frameName:#"main"
groupName:#"main"];
[self addSubview:webview];
Does anyone have any idea what's happening?
If anyone wants to play with the project, the code is on github.
I looked at your code and you're using the superview's (WebSaverView) frame rectangle as the WebView's frame. You should instead use it's bounding rectangle ([self bounds]). The bounding rectangle represents the area "inside" the superview. As you've seen, the two may not always have the same origin, which is causing this issue. You should not be adjusting the frame you're passed in initWithFrame:, since the origin probably has a very good reason for not being 0,0.
I think the frame you're giving the WebView is in the wrong coordinates. Remember that a view's frame is expressed in terms of the superview's coordinate system (bounds).
You should be making the view as big as the superview's -bounds, if I get correctly what you want to do: usually a rect from (0,0) to (width,height).