Any "fundamentals-oriented" example of NSScroller out there? - objective-c

I'm looking for some kind of a basic, straightforward example of how to work with a pair of NSScrollers in an NSScrollView with a custom NSView.
There are sporadic examples out there, largely consisting of contrived examples using programatically created interfaces, or based on the assumption that the developer is working with a typical ImageView or TextView. Even the Sketch example is based on an NSView that uses the Page Setup in the Print dialog for the bounds, and everything is managed by Cocoa. So there's no real discussion or examples anywhere of how make it all work using a custom Model (though that may be part of the problem, because what does one base the Model on?). Even Apple's own documentation is dodgy here.
Essentially, I have a sub-classed NSView enbedded in an NSScrollView (per the Scoll View Guide), that a user can click in the view to create, edit and delete objects, much like an illustration program. The Model is those objects that are just data wrappers that simply record their position for drawRect: to use. The height and width are based on custom values that are being translated into pixels as needed.
My problem is that all of the examples I have found are based on either a text editor, an image viewer, or uses the standard document sizes in the Page Setup dialog. Because these are common document types, Cocoa basically manages for the developer, so the interaction code is more or less hidden (or I'm just not seeing it for what it is). My project doesn't fit any of those needs, and I have no need for printing. Thrusting my Model into the documentView property wouldn't work.
I'm just looking for a simple example on how to initialize the NSScrollers with a custom, object-oriented Model (the documentView), and handle scrolling and updating based on user action, such as when the user drags a smattering of objects off to the left or down or the window gets resized. I think I'm close to getting it all together, but I'm missing the jumping off point that ties the controls to document.
(Not that it matters in a Cocoa question, but when I did this in REALbasic, I would simply calculate and apply the MaxX, MaxY to a ScrollBar's Maximum value based on user actions, watch the position in the ScrollBar when the user clicks, and draw as needed. NSScrollers in the NSScrollView context aren't nearly as obvious to me, it seems.)
I appreciate the time taken by everyone, but I'm updating with more information in the hopes of getting an answer I can use. I'm sorry, but none of this is making sense, Apple's documents are obtuse, but perhaps I'm missing something painfully obvious here...
I have an array of objects sitting in a subclassed NSDocument which are data holders that tell drawRect what and where to draw. This is straight from the Sketch example. The Sketch example uses the document sizes in the Page Setup dialog for the size, so there's nothing to show here. I'm cool with Cocoa handling the state of the scroll bars, but how do I link up the ScrollView to see the initial editor's state held in the NSDocument and updates to those objects and the editor? Do I calculate my own NSRect and pass that to the NSScrollView? Where and how? Am I doing this in my custom NSView which has been embedded in the NSScrollView or my NSDocument in init? The NSScrollView isn't created programmatically (there's no easy way of doing that), so it's all sitting in Interface Builder waiting to be hooked up. I'm missing the hook up bit.
Perhaps I'm wearing my "I don't get it" cap this week, but this can't be this difficult. Illustration apps, MIDI Editors, and countless other similar custom apps do this all the time.
SOLVED (mostly):
I think I have this sorted out now, though it's probably not the best implementation.
My document class now has a NSRect DocumentRect property that looks at all of its objects and gives back a new NSRect based on their location. I call it in my subclassed NSView's mouse events with
[self setFrame:[[self EditorDocument] DocumentRect]];
This updates the size of the View based on user interaction, and the window now handles the scrolling where it didn't before. At this point I'm figuring out how to get the frame to expand while dragging, but at least I now have the fundamental concept I was missing.
The answer given pointed me in the direction I needed to go here (documentView requiring a view, which translated to looking at the NSView class), so Peter gets the credit. Thanks so much for help.

The document view isn't a model, it's a view. That's why it's called the document view.
The reason there are so few examples on working with NSScrollers directly is because you normally don't. You work with NSScrollView and let it handle the scrollers for you.
All you need to do is make a view big enough to show the entire model, then set that as the document view of the scroll view. From there, it should Just Work. You don't need to manage any of the scrolling-related numbers yourself; Cocoa handles them for you.
For details, see the Scroll View Programming Guide.

Related

Using an AVPlayerLayer as the backing layer for an NSView

I'm trying to create a view hierarchy similar to what you'd expect in a media playback viewer like the QuickTime Player:
+ Host View
+ Video Controls (NSView layer-backed)
+ Video View (NSView layer-hosted)
+ AVPlayerLayer
Since layer-hosted views cannot contain subviews, the video controls view is a sibling of the video view and simply ordered-front so that it's on top of the video view.
This current view hierarchy appears to be working fine for me, but I remain a bit confused over whether it's officially "supported" because of the overlapping, sibling views (the video controls view always overlaps the video view).
This Stack Overflow question: Is there a proper way to handle overlapping NSView siblings? offers conflicting information regarding overlapping sibling views.
I would assume the more 'correct' way to handle this is for the video controls to be a subview of the video view, which is only possible of I change the video view from being a layer-hosted view to a layer-backed view.
By default a layer-backed view uses a basic CALayer as its backing store, but NSView exposes makeBackingLayer to allow you to return a custom layer such as an AVPlayerLayer.
By doing that, and moving the controls view to be a subview of this layer-backed video view, things also appear to work properly but now there's an AVPlayer object that is directly modifying the contents of the AVPlayerLayer. That seems to be contrary to the requirement that in a layer-backed view, you should never modify the contents of the layer without going through the NSView using something like drawRect or updateLayer.
This seems to leave me with two choices, neither of which appears 'correct' based on my interpretation of the document:
Option 1:
Layer-hosted view for the AVPlayerLayer
Overlapping sibling view for the controls view.
Option 2:
Layer-back view with an AVPlayerLayer via makeBackingLayer
AVPlayer that directly modifies the contents of the AVPlayerLayer
Controls view as a subview of the video view
I'm inclined to think that option #2 is the more correct way and that in this scenario, it's OK for the AVPlayer to directly modify the contents of the AVPlayerLayer even though it's in a layer-backed view, but I'm not certain and would be curious if others have any thoughts or experiences with a setup like this.
Apple has some old (ancient in computer terms, 2007!) code that doesn't even compile in Xcode 6 without some tweaks. It shows some controls that are overlaying a QuickTime movie layer. Download it here: https://developer.apple.com/library/mac/samplecode/CoreAnimationQuickTimeLayer/Introduction/Intro.html .
It's hard to say that just because they provided source code that it's considered a best practice, but I would suggest you just build it the way you think is best. This is not one of those areas so heavily developed on that a best practice is likely to exist. For me personally it makes the most sense to use overlapping sibling views as to ensure that you don't mess with the video rendering. Whether that's the right way or not is probably somewhat subjective. You can maybe access one of the old quicktime developers mailing lists or even ask on the Apple developer forums. At the end of the day though you should probably just stick with the method that makes the most sense to you since you're likely to be the one to maintain or build upon it in the future.

Cocoa: Update views outside NSWindow's content view during live window resizing?

I have a standard NSWindow with a toolbar. One of the toolbar's items is a custom view -- specifically, an NSTextField. (More specifically, it's a timer app -- the timer's controls as well as the digital display are all within the toolbar, with other stuff in the window's content area. The NSTextField is the digital display.)
Ordinarily, I just update the timer every second by changing the 'stringValue' property of the NSTextField, which causes it to update itself. But during a live window resize, even though the code that updates the 'stringValue' property is running (which I have verified with NSLog), the NSTextField doesn't draw itself again until the window resizing is done. Meanwhile, the stuff inside the content area is updating itself just fine.
I've tried all the ways I know to tell the NSTextField to draw itself, but it just refuses to happen until the live resize is done. Any ideas? Obviously it must be possible somehow, as the toolbar gets resized along with the rest of the window -- so you'd think it would be possible to force it to redraw one or more of its subviews as it is moving them around. I'm assuming I can hack this together by subclassing something, but my Cocoa-fu is not yet strong enough to figure out the easiest/most proper way to do so.
Thanks in advance...
EDIT: I kind of figured out a solution -- it's not great but it mostly works for now. It's in my comments below.
Just invoke -[NSWindow displayIfNeeded] after marking the view as needing display. I encountered this problem when implementing the Mac driver for Wine (an open-source project for running Windows software on OS X and other Unix-like OSes).
http://source.winehq.org/source/dlls/winemac.drv/cocoa_window.m?v=wine-1.7.11#L1905
(That's LGPL code, so you want to consider before copying it. But you can learn implementation techniques from it without worry.)

What's the best method to implement swiping between views in OS X?

What's the best method to implement swiping between views in OS X? These views are initialized from nibs with their view controllers and they're the same class type. To give some background, each view displays relevant data for the current date.
I'd like to create a function that loads the data for the next (or previous) date. I could simply load the data into the current view, but can I do that with an animation that's similar to swiping between spaces in OS X? I imagine I'd have to initialize a new view, load the data there, and then initiate the swiping transition to the new view.
I'm worried that the performance of creating all these new views would be pretty bad. Here are some options I've considered to address this:
Create a dictionary of NSDate to MYViewController. Load and store each view from this hashmap, but this could take a lot of memory.
Create a doubly-linked list of MYViewController and load/store sequentially dated views. This could potentially take a lot of memory also and if the user jumps to a date, the caching would just be erased.
Any thoughts? If there's some way to load the data in the current view, I feel like that'd be the best option.
Thanks!
Have you considered just simply using UIScrollView and having a single UIViewController to manage all the views? Here's a good tutorial from Ray Wenderlich's site on UIScrollView :
http://www.raywenderlich.com/10518/how-to-use-uiscrollview-to-scroll-and-zoom-content
Alternatively, have you considered possibly using horizontal table view? Here's a third party implementation, which I've seen recommended on other SO posts:
https://github.com/TheVole/HorizontalTable
Edit
Sorry, I quickly read your question and assumed it was for iOS...
I think you might be looking for something like NSCollectionView perhaps (not sure if this would support offscreen views in a horizontal manner well or not...?)
Here's the docs on it:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSCollectionView_Class/Introduction/Introduction.html
Here's a tutorial on it:
http://andrehoffmann.wordpress.com/2009/08/29/nscollectionview-tutorial-for-dummies-xcode-3-1-3/
(Honestly haven't done much OSX development, so a bit out of my area of expertise here... I wish you luck though!)
Cocoa view transition animation is not an easy thing. This is much more complicated coding than view controller management.
You need to decide how to achieve the desired effects. Take a look this document and other references: Animation Programming Guide for Cocoa
I did similar work a few months ago. My final resolution was using single view and show animation effects only with graphics APIs (image drawing) because it was more simpler than Core Animation.

Custom Views or Custom Cells

I have a question about creating custom views. I wanted to implement an interface where I want to have different objects configured in one place, each of them with it's own controls. Kind of like in automator, on the right side view where the workflow is shown with different actions. Are those NSView or NSCell subclasses ?
Any example will be appreciated !
Ken
Here's how you can tell an NSCell and an NSView apart:
NSCells are basically stamps. Given a certain value/object, the NSCell knows how to draw that on the screen how you want it. Like NSTextCells know how to draw NSString's on the screen how you want them. NSCells don't have state, don't remember anything, they're just a one-shot set of drawing instructions that get executed with a given value/object. The idea is to reuse NSCells as much as possible to make drawing things on the screen super simple.
NSViews are sort of logical containers for what goes on on your screen. They can technically do their own drawing, but quite a few of them use NSCells to do their drawing. For example, NSTextField uses an NSTextCell to draw it's text on the screen, it also contains the extra logic necessary to toggle between editing and not-editing by displaying the text box you can type in during editing and using the NSTextCell when you're not editing. NSViews are also part of the responder chain and can respond to mouse-clicks, keyboard events, and the sort.
You're probably going to end up with both, NSViews to hold all the controls you want to use to configure each object and NSCells to draw custom UI elements (like if you use non-standard controls).

Looking for info on custom drawing of interface components (Cocoa)

It seems like more and more OS X apps these days are doing all kinds of fancy drawing stuff for custom controls. Apps like Twitterific, Things, EventBox, Versions just to name a few....
So basically I'm looking for any information on how to get started doing this kind of thing. Not sure if it is just done by subclassing controls and using custom drawing or if it is something entirely different.
Any help is greatly appreciated. THanks!
It depends entirely on what you want to do.
The "Show Raw Properties" button in Versions for instance is an NSButton subclass, because basically what we needed is standard button behavior with our own look.  One way to subclass a button is to simply implement your own -drawRect:(NSRect)rect method in the NSButton subclass, but we decided to stick with the way NSButton is implemented in Cocoa, meaning most drawing is done by the button's cell, so the implementation looks like this:
In the NSButton subclass:
+ (Class) cellClass
{
return [OurButtonCell class];
}
- (void)drawRect:(NSRect)rect
{
// first get the cell to draw inside our bounds
// then draw a focus ring if that's appropriate
}
In the NSButtonCell subclass (OurButtonCell):
- (void)drawInteriorWithFrame: (NSRect) rect inView: (NSView *) controlView
{
// a bunch of drawing code
}
The Timeline view in Versions is actually a WebView, the page that you see in it uses javascript to collapse headers you click on.
The rule of thumb I use for where to start out with a custom control is:
To customize the look of a standard Cocoa control:
subclass the appropriate control (like e.g. NSButton and NSButtonCell)
stick as close as makes sense to the way the default control is implemented (e.g. in a buttoncell, start from the existing attributedTitle instance method to draw the button title, unless you always want to draw with the same attributes regardless of what's set up in IB or if you need to draw with different attributes based on state, such as with the trial expiration button in Versions' main window)
Creating an entirely new UI element:
subclass NSView and implement pretty much all mouse and key event handling (within the view, no need to redo "hitTest:") and drawing code yourself.
To present something that's complex, of arbitrary height, but isn't a table:
See if you can do it in HTML, CSS and JS and present it in a WebView.  The web is great at laying out text, so if you can offload that responsibility to your WebView, that can be a huge savings in pain in the neck.
Recommended reading on learning how to draw stuff in your own custom view's drawing methods: Cocoa Drawing Guide
Customizing the look of for instance an NSTableView is an entirely other cup of tea, thanks to the complexity of a tableview, that can happen all over the place.  You'll be implementing your own custom cells for some things you want to do in a table, but will have to change the way rows are highlighted in a subclass of the actual NSTableView object itself.  See for instance the source code for iTableView on Matt Gemmell's site for a clear example of where to draw what.
Finally, I think Abizer's suggestion to go check out the code of BWToolkit is a great idea.  It might be a bit overwhelming at first, but if you can read and understand that code you'll have no trouble implementing your own custom views and controls.
Have a look at some excellent example code: BWToolkit