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

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.)

Related

Using multiple windows with Storyboards (Mac OS X development)

I have two window controllers (with their own view controllers) on a storyboard.
In one window, I have the main program, a basic text editor with an NSTextView. In the other window, I have a single button.
I found out how to get the window to display by linking it to a menu item. It works.
The main window is linked to my ViewController class by default. The second window is also linked to the ViewController class and has its button linked to an IBAction in the ViewController class.
I have some simple code in the IBAction that basically tells the NSTextView to change its font size to a much bigger font. I have confirmed that the code itself works when called in other methods.
The button works, BUT it is using an entirely different instance of my ViewController class. So in result: the text size doesn't change.
So my main question here is how do I get an IBAction in one window to affect an object in another window.
I hope I did an alright job at explaining myself. Keep in mind this is my first Stack Overflow question:) I tried my best to research this question but mostly found information on iOS development and using XIB files.
It sounds like you have two windows with the same controller class but want what happens in one window to affect the other window. The easiest way is going to be with notifications. When the button is clicked in one window a notification gets posted that all instances of ViewController receive and respond to by changing the font size as needed. You could also look into setting a user default when the button is clicked and using bindings to keep the text field's font size tied to the current default.

OSX Cocoa using tab to navigate between child controls within a view not working

I am new to Xcode/OSX UI so there is probably something silly I'm overlooking. This is XCode 5.11 targeting OSX 10.10 desktop.
I have inherited some code with a few views where navigating among child controls within the view using the tab key does not work.
In researching this almost everything says to be sure to set the first responder and then chain your controls using nextKeyView.
I followed the steps in this video https://www.youtube.com/watch?v=SRrE8eqp0dU (XCode 4, but all the functionality seemed to be the same for 5.11) to no avail.
I also had a look at this solution How to make child controls of view will get focus on Tab and Shift+Tab in NSViewController which sounds like a similar issue to what I am seeing, but one of the classes I inherited uses NSWindowController vs. NSViewController as the base and there is no loadView to override and the other which did derive from NSViewController did not behave any differently with the changes made to loadView.
When my window launches my first responder control (NSTextField in this case) has focus (blue highlight) but tab key is ignored and focus will not change unless I use the mouse.
So it's really not a tab ordering issue initially, it seems like a tab ignored issue and who knows what the ordering is. I tried setting focus to a NSButtonCell and NSPopUpButton using the mouse and then tab navigate from those to see if there was some issue with my NSTextField but they exhibit the same behavior. None of the controls are set to "Refuses First Responder" which was another setting it was recommended to check.
I'm at a loss and looking for any other things to try or check.
The first view I am having an issue with is: Window / Child View / Multiple Child Boxes / Multiple controls per box in case that matters or complicates things. It is basically for setting application Preferences.
The second view seems like it may be more complicated in that there is a single Window that swaps out its child view in a next/back progression (wizard interface). The initial window nib is "blank" so I didn't see how to associate a first responder from IB like I did with the Preferences window since all the controls are on their own individual view nibs (these all show as "Custom View" vs. just "View" for Preferences).
The resolution for me was to ensure that the "Auto Recalculates View Loop" setting in the Attributes Inspector was enabled for the windows hosting these views. This corresponds to the autorecalculatesKeyViewLoop property of NSWindow.

Is it possible to observe -visibleRect

I would like to be notified whenever a certain NSView's - (NSRect)visibleRect changes because I want to do some fancy subview layout based on the visible rect. Frankly, right now I'm stumped; -visibleRect doesn't emit KVO notifications (which makes sense), and there doesn't seem to be way to find out if the visible rect changed or not without explicitly calling -visibleRect.
Is this at all possible? (or is it a terrible, terrible idea?)
I think you can either override -[NSView updateTrackingAreas] or listen for NSViewDidUpdateTrackingAreasNotification. Those may happen on more occasions than just a change of the visible rect, but they should happen for any change of the visible rect. I think.
That said, it may be a terrible idea. Hard to know. :)
Another option post 10.5 is the -viewWillDraw method which is called just prior to the view (and its subviews) being drawn. You can fetch the view's visible rectangle and perform layout prior to calling [super viewWillDraw].
Ken's suggestions of listening for the tracking area changes feels hacky but seems to work, although they only trigger after the resize is complete. If you need updates during resizing like I did, it overriding -[NSView resizeWithOldSuperviewSize:] will do that
The adjustTrackingArea solution does not appear viable in Mojave for NSScrollView at least.
Mojave does not appear to always call adjustTrackingArea while scrolling an NSScrollView.
Haven't tested other OS versions, other view types.

Landscape mode without a ViewController

I have a project based on a window-template, so it doesn't have a viewController. Now I'm trying to make this work in landscape only, and I have set the "supported device orientations" to "Landscape (right home button)" only, just the way I want it. The app actually launches in Landscape mode, but when I want to show an image which is btw a landscape-fullscreen one, iOS draws it in Portrait mode, cutting off what doesn't fit. Well I know the option of implementing a ViewController and editing the "shouldAutorotate..." method, but I want to do it without.
I have one solution in my mind, but I'm not sure whether it#s such a great idea: Rotate the whole coordinate-system on app-startup manually using "CGTransform..."
Down that path lies madness. Just because the template doesn't include a built-in UIViewController doesn't mean you can't add one yourself.
Add one, and save yourself a bazillion headaches.
From a more technical standpoint, a lot a UIKit expects the window to have a UIViewController, and not having one means you can lose out on a whole lot of behavior that you would otherwise get for free.

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

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.