I'm new in Mac programming. The first thing I've noticed is that the (0, 0) coordinate in a NSView is the bottom-left corner.
I'm going to use a NSView as a canvas, to draw objects on it. My objects will be positioned from the top-left corner. I can convert Y coordinates using a geometrical transformation.
But the problem is: I'm not sure how to proceed when the view is resized. I don't want to calculate the layout every resize event, because calculating it takes between 150 and 250 ms.
So, to Mac experts:
Do you know a good practice calculating the layout based on the top-left corner?
How should I manage the resize behavior?
Do you know other techniques, flipping the graphics object, or something like that, to draw this kind of stuff?
Just implement
- (void)isFlipped {
return YES;
}
In your NSView subclass.
Related
I have an image view that looks like a wheel. This view detects touch events on each of the colored sections.
The problem I have is that when I rotate this wheel, the UILabels on top of the view need to also be rotated so that the text is still horizontal and human readable.
What is the best way to rotate the labels while the parent view is being rotated?
This is what I am trying now and it does not rotate correctly...
CGAffineTransform textTransform = CGAffineTransformRotate(newTransform, newAngle * -1);
Presumably you are applying a rotation transform to rotate the wheel. If the labels are subviews of the wheel view, their centers are pinned in the right places to the wheel (because a view is located in its superview by its center), and they will travel around with it. At the same time, apply the inverse rotation transform to the labels. The rotation takes place around the center of each label. So each label stays in the right place and stays upright.
An afterthought - also make sure you're not using autolayout on these labels. Autolayout breaks view transforms.
I have a UIImageView that I've added a PinchGestureRecognizer to. Currently, the image is resized nicely when pinching, but I want to be able to resize the image without maintaining the aspect ratio. So if the user pinches horizontally, the image view's width would enlarge; if they pinch vertically, the height would enlarge and so forth.
Can anyone give me a hint on how I could do that please?
Write a custom gesture recognizer that requires two fingers to be on screen.
Once both fingers are on screen store their offset to the imageView's border in some UIEdgeInsets.
In touchesMoved, check if both fingers are onscreen: if so, calculate the new frame by applying the edgeInsets in the current touch position.
Header: click
Implementation: click
Works well and feels more natural than other implementations I've seen.
You would need to do the touch handling yourself as UIPinchGestureRecognizer only supports one scale which has no concept of being pinched horizontally or vertically.
You could create your own subclass of UIGestureRecognizer (see here for docs) which looked at the horizontal and vertical separation of the touch points to determine 2 different scales. It should be fairly straightforward to create I would have thought. Just look at the initial touch points and then when they move, calculate the difference in the current separation of the touches to the initial separation of the touches, in both the vertical and horizontal directions.
Cocoa's NSScrollView is horribly under-explained. I hope someone here knows what it's all about and can spare me a few seconds.
So I have a custom NSView. I implement -drawRect: for it to draw something, fill itself with colour, whatever. Then I have an NSScrollView wrapping it (set up through the interface Builder).
Now the inner, custom, view must have a size larger than that which fits in the outer scroll view—for it to scroll. That much I realise. I have incidentally configured it so that the scroll view adjusts to the surrounding window’s size, but that shouldn’t matter.
I override my inner view’s -frame method to return a frame sized at least 1000x1000.
- (NSRect)frame {
CGFloat w = 1000;
CGFloat h = 1000;
if (self.superview.bounds.size.width > w)
w = self.superview.bounds.size.width;
if (self.superview.bounds.size.height > h)
h = self.superview.bounds.size.height;
return NSMakeRect(0, 0, w, h);
}
Here’s the outcome, which I have trouble interpreting:
I can scroll when the scroll view encloses an area smaller than 1000x1000
BUT
The only area filled with colour (i.e. that my -drawRect: method has any effect on) is
as large as the scroll view’s bounds
located at (0,0. I use flipped, so that’s top left, and it ends up being outside the visible area after scrolling.
The visible area that lies outside this irrelevant rectangle is not painted at all.
I don’t know anything beyond this point. It seems like the rect for drawing is clipped to the scroll view’s position in the window, and size, or something—but it does not take the scrolled "location" into account.
It should be noted that I don't really expect anything else to happen. I feel I am missing a piece, but can't find which. Sorry for the wall of text, but I can’t explain better right now. I hope it is easier to answer than it is to ask.
Regards and hope,
Not Rick Astley
It's a very very very bad idea to overwrite -frame. There is so much that depends on the actual instance variable having a correct value. Instead try to set the frame to the one you want using setFrame:, that might fix all your problems if you're lucky...
I agree with Max's warning that you shouldn't override -frame. If you want to constrain the set frame, override its setter ( -setFrame: ) and the designated initializer ( -initWithFrame: ) and adjust the proposed frame as desired.
Regarding your overall problem, I wonder if your problem is conceptual. The argument for -drawRect: (the dirty rectangle you're asked to redraw) is useful if you're drawing something that you can redraw incrementally in parts (like a grid - any grid blocks intersecting dirtyRect can be redrawn and the rest can be ignored). If you're doing something that has to be completely redrawn, you should use [self bounds] and not the dirty rect passed at drawRect.
For example, if you have just a standard gradient background, it's difficult to tell from dirtyRect which part of the gradient to redraw and infinitely easier just to redraw the whole view with the gradient, ignoring dirtyRect altogether.
You're right in assuming that only the area of your view exposed by the scroll view's clip rect will normally be asked to redraw when scrolling. There're also interactions with the scroll view's -copiesOnScroll to consider.
I hope this helps.
Use of the NSScroller really relies on a solid understanding of the MVC paradigm. Apple's docs really focus on showing a photo and a set of text, but not much else. The use of NSScrollView is something that I've struggled with in the past.
First off, do not override frame. Use setFrame to tell the scrollView how large the working area is, and then just simply draw in the area the frame encompasses. As I understand it, a custom NSView and the encompassing NSScrollView takes care of the rest, such as what to draw where when. In other words, ignore the bounds of the rect passed into drawRect and instead draw within the bounds of the frame you sent to scrollView; don't worry about what is visible and what isn't because that is the job of the framework.
Here is where the MVC paradigm comes in: setFrame should be used when your Model is updated. So, if an object falls outside of the current bounds of the frame, then use setFrame to set the newly expanded bounds, and then draw within that area.
I'm reading pixels from an area of the main screen via NSOpenGLContext. Now I would like to draw a rect around that area to indicate where it actually is.
How would I do this?
My first thought was the "Cocoa way": create a transparent fullscreen NSWindow and a custom NSView to draw the rectangle path. But that feels a bit too complicated. Isn't it possible to draw directly on the NSOpenGLContext?
If you want to draw over elements not inside your application the floating window is the only correct way. There’s really no complication except mapping positions properly, which is easy to do with the coordinate-space conversions available on NSView and NSWindow.
I have an NSView in a ScrollView and I'm trying to draw an image in it. The problem is that I want the upper left corner of the image locked to the upper left corner of the frame, instead of the lower left corner, which is what the View wants me to do. I will need to be able to zoom in and out, and rotate.
currently, I have a kludge of a system where I calculate how much I have to translate my image based on the size of the image and the size of the window. In order to do this, I needed to create an extra view outside the scrollview, so that I could get the size of the window, not including decorations. Then I can calculate the size of the view based on the size of the image and the size of the window, and based on THAT, I can figure out where to translate the image to.
My only other thought was to use the isFlipped: method, but that ends up reversing my image L-R which is bad.
Is there another way I should be doing this?
If you want 0,0 to be in the upper-left corner, then overriding -isFlipped to return YES is the way to go. It should not affect the coordinate systems of any subviews (I think!), but images drawn directly into the flipped view will appear upside-down unless you apply a transform to them.
View Programming Guide for Cocoa: View Geometry