Cocoa: Remove NSView - cocoa

I'm doing an exercise in learning and at the same time making a game for my kid. He has one of those card games (like Pokemon) and we scanned a bunch in and are attempting to make a game where he can play against the "computer". So the game process is you need to start by selecting your cards. What I've done is have a class (Card (sub of NSView) that gets instantiated by an IBOutlet (button) and drops the first card on the screen as well as scroll buttons - each time a scroll button is clicked it determines what the next card should be and then calls a method (makeCard) which also instantiates a new Card.
I'm fuzzy about what cocoa is doing here. Card basically has, in its drawRect a call to a texture atlas and I pass in the coordinates of the current card to display. That means that each time I instantiate Card a new NSView is being made, correct? I am essentially building a stack of NSViews in my app (since the x, y, w h) of each card is the same I can't tell but that seems like logically what is happening. It doesn't have an effect on app speed but it seems like unnecessary clutter.
Is there a way that I can just update the image in one instance of a view rather than instantiating a Card for each one I want to show? And regardless of that answer, how do I then remove the view from the window once the set up process is complete? [view removeFromSuperview]?
To be clear, I do not want a visual representation of the card anymore. There just the eye candy for the set up part of the game as all the card data (including texture atlas coordinates) are stored in a dictionary.
Also, since I am asking questions here, how would I, without an NSImage, be able to scale the images from the texture atlas. They are 180x250px each but down the road they'll be represented in a holding area and I'd rather they can't be that size.

An answer to part of your question, since I can't figure out the rest of it:
CGImageCreateWithImageInRect will let you create a reference to a part of a larger image, as in your texture atlas. You can then create a NSImage from that (if you're using 10.6 or later, with -[NSImage initWIthCGImage:size:], otherwise you'll need to create a NSBitmapImageRep first). Then you can display the NSImage in a NSTableView cell, NSCollectionView or NSImageView.

Related

The internals of NSScrollView

When you gently scroll an NSScrollView the rectangle that Cocoa marks as dirty, and passes to drawRect, is often trivially small (perhaps as small as one or two pixels in height, for a vertical scroll view). The framework clearly already knows what the majority of the content is (because it's on screen) and where to redraw it (just the offset brought about by the scroll), so all it needs the developer to do is fill in the small rectangle that's about to appear. I was wondering what's happening behind the scenes to allow this to happen?
For example, if I wanted to implement my own super-smooth scroll view as a learning project, what kind of data would I be recording about the document view to enable me to just re-position - rather than redraw - the majority of it. Is Cocoa constantly generating images on background threads that it draws on screen when required, or is there something a bit more subtle going on?
There's lots going on. If you haven't already read it, you should read the Scroll View Programming Guide for Cocoa.
The copying of the existing rendering is accomplished by -[NSView scrollRect:by:]. It's only done if the NSClipView that's part of the NSScrollView architecture is set to copy-on-scroll (the copiesOnScroll property).
Also, there's "responsive scrolling". Since 10.9, if certain conditions are met, AppKit will speculatively render the document view beyond the visible rect so that, when the user scrolls, it can show the scrolled-in area without asking the document view to render.
You can set your views to be layer-backed. In that case, they are typically rendered to textures and composited by the window server. This means they don't necessarily have to re-draw to render in a new position. It's quite likely that responsive scrolling uses layers behind the scenes to hold the pre-rendered content.

Xcode GLKit printing Text on GLKView without using UIImages

I have an app, its a small game using opengles with GLKit.
No im wondering how it works when i want to draw text on
my screen (if it is possible).
How can i do it?
i draw all of my game objects using images (wrapped in some kind
of sprite). its possible to scale, to move, and to rotate.
everything works fine.
but finding out how it works to print text on that glkview
gets me deep inside of problems ^^
I dont want to use uiimages cause i also dont know how
to present uiimages on a glkview.
There are a number of ways to do what you want:
1) Have an image with all the text glyphs you need in it. For example, if your application is in English, you'd have the 26 uppercase and 26 lowercase letters in the image. Upload that texture to the GPU and use the proper texture coordinates or glSubTexImage2d() to pull out the glyphs you need. (It's not clear to me if this is what you meant by not wanting a UIImage. It doesn't have to be a UIImage, though that's probably easiest.)
2) Every time you need to display text, draw it on the CPU on the fly, and upload the entire word, phrase, or sentence as a texture. You could create a CGBitmapContext and use Core Graphics to draw text to it. Then upload it using glTexImage2D().
3) Get the individual glyphs out of the fonts and draw directly using the bezier curves that make up the glyphs. This allows for 3D extrusion, too. However, this option is the most time consuming to code and probably least performant. It also involves dealing with the many small problems that fonts have (like degenerate segments, and incorrect winding orders). IF you want to go down this path, I think maybe Core Text can help.
There are at least two clean ways to do this, depending on your requirements.
While documentation advises against compositing over a CAEAGLLayer (GLKView), it works quite well, at least in recent iOS versions, when transparent content is layered on top of the CAEAGLLayer. For example, try dropping a UITextView, with opaque set to false and a clear background color, on top of a GLKView in your Storyboard in Interface Builder in the Apple GLKit template or your app. In my test on an iPhone 5, frame rendering time remained around 1ms, even while scrolling in the text view. If your text needs are static, or you don't want the user to interact with the text, use CATextLayer as a child layer of your EAGLLayer instead of a view.
The second approach is to render the text into a texture. You can then composite the text onto your view by disabling the depth buffer and rendering the texture on a full screen rectangle. Look at UIGraphicsBeginImageContextWithOptions to see how to render to an offscreen image with Quartz. UIGraphicsGetImageFromCurrentImageContext allows you to retrieve the UIImage to use as a texture.

how to translate and scale a NSImage?

I have built so far an application that allows the user to drag and drop images onto a NSImageView. However, I want to be able to move these images by simply clicking on any image and hold down the mouse button to move it's location.
How can I manipulate NSImageView to translate/scale after setting the images down? Is that possible? I've read about the NSAffineTransform, but it seems like that is moving the images before creating the image itself. I already have the images on the canvas, and simply want to click and hold the image and move it with my mouse. Please help anyone!
There are two sides to this.
NSImage is the model object, which you might want to display in different ways, save to disk/archive, etc. If you want to actually change the model (scaling, rotating, etc.), implying a permanent change, then you are going to probably want to look at NSAffineTransform, Quartz drawing, etc.
But you probably didn't mean that. Instead you probably are interested in NSImageView, which is a view object, displaying the contents of the NSImage model object using whatever display attributes are desired. If you only want to change how an image is displayed, not what the actual bytes in the image are, then you are going to manipulate the NSImageView at run-time. You can use NSAffineTransform here as well, but it's somewhat uncommon (and usually unnecessary).
The key thing to note that is the NSImageView inherits from NSView, so you have all its power at your disposal. Take a look at certain methods, such as:
-setFrameSize: - useful for changing the view size, and thus the image display scale
-setFrameOrigin: - useful for changing the view position, and thus the apparent image position
Note again that these have nothing to do with images per se, and apply to all Cocoa views. You may want to take a look at a book like Cocoa Programming for Mac OS X to get you past the basics. (You can then do more interesting things, like rotation, animation, etc.)

How to implement a timeline custom control with cocoa?

My Cocoa application collects events (instances of NSManagedObject) that need to be displayed in a timeline. My initial approach was to use an existing Javascript based widget (I tried using Simile Timeline and Timeglider) and display the timeline using a WebView control. This works in principle, however unfortunately both these widgets do not handle BC dates very well, which is an important requirement for my app.
The events in my app have date ranges from 500.000BC up to recent dates. Event dates are only expressed with a year. Their day, month and time attributes are irrelevant.
After discarding the Javascript approach, I remain with the option to display the timeline using a custom Cocoa control. As I found none suitable, I will have to develop that myself.
This would be my first custom Cocoa control and after thinking about this for a while I've come up with the following rough design:
I need a custom control to render the actual time line. This control is probably based on an NSView. This control should calculate its size based on the number of tick marks on the time line multiplied by the width (pixels) between each mark. For example the timeline is made up of centuries, each century 100 pixels wide. A time line of events between 10.000BC and 5.000BC would then be 5000 pixels wide (10000 - 5000 = 5000 years, equals 50 centuries).
I need a ScrollView to wrap the timeline to allow it to support scrolling behaviour. There's only need for scrolling horizontally.
I need something to represent an actual event. I'm thinking of using existing controls for this, probably the standard round button and a label wrapped together as a single control.
I need a custom control to render a tick mark on the time line.
Taking this as the basic design for my time line component in Cocoa, would that work or am I completely missing the point?
The basic approach sounds fine.
Apple has a good example of creating a custom NSView called "TreeView". It's a good sample to understand.
https://developer.apple.com/library/mac/#samplecode/TreeView/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010131
“TreeView” presents an example of a creating an entirely new custom
view from scratch (by directly subclassing NSView). Its implementation
illustrates many of the considerations involved in creating a custom
view, including issues of content layout, drawing, handling user
interaction, and providing Accessibility support.
Another thing you may want to consider is zoom in and out. If you have a long timeline, I imagine you may want to zoom out and then zoom in on a cluster of activity. If you have one event in 10k BC and then a cluster of events much later, the user could scroll through tons of empty space trying to find events. Another approach would be to have a mini timeline above that's fit to/static size which is sort of like an index with lines showing activity points - then clicking on that would auto scroll to that point. That may be a nice to have depending on your data.
Some thoughts:
For something this custom drawn, you'll want to override drawRect to draw your lines and layout your subControls.
If you're drawing your background or any part of the views, ensure you enable layer backed views:
[self setWantsLayer:YES];
If you can, as you noted, try leverage existing controls that you add and layout. In my custom controls, I maintained data structures independent of the views/controls that represented the state of all the objects. The in drawRect, I detected the view changing and I called my layoutSubviews function. My layoutSubViews function would do the math from my data structures and create or move the frame of existing controls. That worked well for resize and zooming. If you zoom, your labels ad markers will need to react well to being zoomed really small - perhaps text drops out at some point etc...
if ([self dataSource] &&
!NSEqualRects(_prevRect, [self bounds]))
{
// layoutViews is my custom function that worked over the data structures
// and moved the frame
[self layoutViews];
}
_prevRect = [self bounds];
Hope that helps.

Cocoa: Custom control not limited to window frame - how to start?

I want to build a custom control that would work like this:
You have a kind of NSButton with an image.
You click the button and than appears a big square with a grid of photos.
You click one of the photos and it is set up as new image for the button. (square dissapears)
Now, how to draw this big square with photos if I want it not to be limited to window frame?
I mean, if the button was close to window border the square is going to be partially outside window. I would also like to add some shadow to the square and an animation for opening/closing.
One important thing: I want to be able to draw not only a square but any other simple shape (circle)!
This isn't really a drawing question so much as a general custom views question. It's important to make that distinction.
I'll describe this in terms of rectangles to give you the general idea*. You should make sure you understand the view hierarchy and view geometry in Cocoa. Without this important requisite knowledge, you'll remain dead in the water.
It's easy to set an NSButton's image, so I'll leave that to you. Your button's action, however, would tell some controller to show the "image picker" for the given button. Your image picker would be some type of borderless window with an image list inside. The image picker could be an IKImageBrowserView (you'll have to enable Image Kit in Interface Builder for this control to appear), which gives you an iPhoto-like grid of images (with/without titles, different border types, etc.).
An explanation of the operation of this controller and how it creates the window, manages the selection, and sets the button's image is very broad so if you get hung up on any of those steps, you'll need to create a separate question for each problem, otherwise this answer would have to be an instruction manual for writing your app for you.
* Your problem is a little more difficult because of your desire to have differently-shaped "popup windows" ... you'd have to make sure your available photos fit neatly within the shape so none of them are cut off. Armed with the basic knowledge of view geometry, I'll leave this to you as an exercise. A hint: you can use a borderless, transparent window to host a view that draws itself in any shape you please.

Resources