NSCollectionView does not hide in osx - macos

I used NSCollectionView in my application. In certain condition i want to hide and Unhide the NSCollectionView. But it will not hide the NSCollectionView.
I used the following code
#IBOutlet weak var thumbnailView: NSCollectionView!
func applicationDidFinishLaunching(aNotification: NSNotification) {
thumbnailView.hidden = true
NSThread.sleepForTimeInterval(5)
thumbnailView.hidden = false
}
Note: Sleep the thread just for demonstration purpose
Also Hiding the NSScrollView is not working.
EDIT: I perform same code on Button Touch up Inside event i get the same result. It does not hide my CollectionView.
#IBAction func ButtonnISClick(sender: AnyObject) {
thumbnailView.hidden = true
NSThread.sleepForTimeInterval(5)
thumbnailView.hidden = false
}

Here are things that are wrong with your code (mostly because you did not provide enough information in your question):
Use lowercase strings when naming variables (so change ThumbnailView to thumbnailView;
I don't know where are you writing this code. Is it NSWindowController, NSViewController or NSWindow subclass? Depending on the location, you should write 2nd to 4th lines in different methods (either windowDidLoad(), viewDidLoad() or awakeFromNib()
Update: Given your code is in application did finish launching and you are sleeping in that method, you won't see any changes, since the window of your app is presented only after the method has returned (i.e. all code has been executed). I suggest you to move this code into a subclass of either NSWindowController's windowDidLoad (I'm not sure about that one) or NSViewController's viewDidAppear: method.

Related

How to exclude certain AppKit views from restorable NSWindow?

NSWindows can be made restorable so that their configuration is preserved between application launches.
https://developer.apple.com/documentation/appkit/nswindow/1526255-restorable
Windows should be preserved between launch cycles to maintain interface continuity for the user. During subsequent launch cycles, the system tries to recreate the window and restore its configuration to the preserved state. Configuration data is updated as needed and saved automatically by the system.
In a new macOS project, the NSWindow on a Storyboard is restorable by default:
My problem comes when embedding an NSTabViewController in the NSWindow.
The NSTabView is inheriting the window's restorable state automatically, with no added code.
This makes the selected tab persist between app launches. I don't want that. I want it to always default to index 0. If the selected tab is restored, attempting to select a tab programmatically in viewDidLoad has unexpected results.
How can I force certain AppKit UI elements to be excluded from NSWindow state restoration?
I want the Tab View to be un-restorable.
But I would like to keep other restorable benefits, such as restoring the previously-set window size.
How can single views be excluded from NSWindow state restoration?
AFAIK, you cannot exclude a certain part of the UI from being restorable. It is an either ON or OFF thing for all elements. That's why I rarely use Apple's own restorability APIs, as more often than not, they are unreliable. I always do the restoration myself to get that fine control that you need. For simpler windows, however, I let the system do the restoration.
After this preamble, and to really answer your question, I rarely use viewDidLoad() to set up any windows, because as you found out that has some nasty consequences (e.g., the window might not exist yet!). I always do that in viewWillAppear(). For that to happen, you need to set up the following:
You need to have an ivar (let's call it tabViewController) to your NSTabViewController instance in your parent NSViewController (let's call it NSViewMainController)
Override prepare(for segue: NSStoryboardSegue, sender: Any?) in NSViewMainController and set up the NSTabViewController and its NSViewController children like this:
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
// set up the tabViewController ivar
self.tabViewController = segue.destinationController as? NSTabViewController
// set up the child NSViewControllers if you need to access them via their parent (otherwise this step is not needed)
if let childControllers = tabViewController?.children {
for controller in childControllers {
if let controller = controller as? NSViewController1 {
childController1 = controller
}
else if let controller = controller as? NSViewController2 {
childController2 = controller
}
else if let controller = controller as? NSViewController3 {
childController3 = controller
}
}
}
}
Override viewWillAppear() of NSViewMainController and then set up the desired tabView:
guard let controller = tabViewController else { return }
controller.selectedTabViewItemIndex = 0
Major caveat: Beware of viewWillAppear(), though... Unlike viewDidLoad(), this override can be called multiple times, and thus you need to take that into account in your code and react appropriately.
The key to state restoration is the NSResponder method restoreStateWithCoder:
This method is part of the window restoration system and is called at launch time to restore the visual state of your responder object. The default implementation does nothing but specific subclasses (such as NSView and NSWindow) override it and save important state information. Therefore, if you override this method, you should always call super at some point in your implementation.
https://developer.apple.com/documentation/appkit/nsresponder/1526253-restorestate
So, to not restore a certain control, make this method a no-op.
It says that you "should always call super", but that restores the window state. So if you don't want window restoration, don't call super.
In the case of a Tab View, it evidently must be done in the NSTabView (subclass) itself. In other views, overriding this method on the View Controller may work.
class SomeTabView: NSTabView {
override func restoreState(with coder: NSCoder) {
// Do NOT restore state
}
}

Stop NSView from printing

I have a very basic Mac application in Xcode with a Split View that contains a PDFThumbnailView and a PDFView. (Just like Preview.)
There's hardly any Swift code beyond what is required to load a PDF into the views.
However, when I select Print from the Print menu, the app wants to print the ThumbnailView, unless I click on the PDFView first.
Is there any way to always make it print the PDFView and never the ThumbnailView? I presume it's a setting or parameter somewhere in the Storyboard, rather than a bit of code as such.
Xcode 10.2.1 on Mojhave 10.14.5.
An easy solution would be to provide your own print method (like printContent:) on your controller, and in your XIB file change the target method for the print menu item to printContent: instead of print:
// eg.
#IBOutlet weak var pdfThumbnails: PDFThumbnailView!
#IBOutlet weak var pdfContent: PDFView!
...
#IBAction func printContent(_ sender: Any) {
self.pdfContent.printView(sender)
}

Detecting When App Enters Full Screen Mode (Swift/Mac)

I'm hoping this is a simple question, but I am trying to discern a way to figure out when a user has selected to enter Full Screen mode in an app. Effectively, I have a table in a Cocoa app that looks rather silly when the app enters full-screen mode. I would like to, programmatically, adjust the height of my table rows once the app enters full screen mode, but I cannot seem to figure out how to do so.
I recognize the need to use windowWillEnterFullScreen: and windowDidEnterFullScreen:, or find a way for my Window to conform to my App Delegate file, though I'm struggling to figure this out. Are there any resources that could be provided that may be able to point in the right direction?
Thank you!
Edit: Here's what I've tried to do;
AppDelegate.swift
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
var window: NSWindow!
func windowDidResize (notification: NSNotification) {
window.delegate = self
print("resized")
}
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
windowWillEnterFullScreen: and windowDidEnterFullScreen: are NSWindowDelegate methods — to be able to use them, you just need to be the NSWindow's delegate. Your app delegate object or any other object could serve this purpose.
If you want to use custom animations during the transition, there are some other delegate methods such as window:startCustomAnimationToEnterFullScreenOnScreen:withDuration: that you could use.
You can also check window.styleMask & NSFullScreenWindowMask != 0 to check whether the window is currently fullscreen.

windowDidLoad() never called using storyboards for mac

I've just played around with the new mac storyboard-feature included in Xcode 6. I've set up a new OS X-project using storyboards and swift, then I've created a new file MainWindowController.swift, created the initializer init(coder: NSCoder!) (because otherwise the compiler warns me) and hooked everything up in the Main.storyboard file (set the MainWindowController-class for the WindowController in the inspector).
Everything compiles fine, my Window with the specified window content-view opens. But the code I've written in the windowDidLoad-function is never be called. Let it just something like:
override func windowDidLoad() {
super.windowDidLoad()
println("Executed")
}
I've also tested if my initializer is called - it is.
Does anybody has a clue? I've never used storyboards intensively on iOS before, maybe I miss something substantial.
In Yosemite, NSViewController has been promoted with powerful new features to make it work with Storyboards. Meanwhile, NSWindowController got demoted. With Storyboards, windows are no longer loaded from a nib, so windowDidLoad() doesn't get called anymore.
It makes sense for the window itself to become less important, in favor of a more powerful view it actually contains. My other answer on this page shows how to set up an AppDelegate to customize the window appearance. There's more detail on another page here, about using an AppDelegate to implement some of the things you might previously have done in an NSWindowController.
However, if you only wanted to catch windowDidLoad() as a way to customize the appearance options of the window, it is very easy to do that in Interface Builder, by simply adding them as User Defined Runtime Attributes to the NSWindow object. You don't need to subclass NSWindowController or write any code at all. Just plug in these values to the NSWindow object via the Identity Inspector pane to achieve the same effect shown in the AppDelegate example code:
Keypath: titlebarAppearsTransparent, Type: Boolean, Value: Checked
Keypath: titleVisibility, Type: Number, Value: 1
Keypath: styleMask, Type: Number, Value: 32783
Look in the headers to determine the actual numeric values of the constants.
( for example: NSWindowTitleVisibility.Hidden = 1 )
Of course, you can't specify individual bits of the styleMask, but it's easy enough to add them all together and get a single number to specify the style.
Remove everything except the Application scene from the Main.storyboard file, and instead, create a new Application.storyboard for the application window. Implement an application delegate class and connect it to the Application object in Main.storyboard. Use this class to instantiate the window controller and set up custom options for the application window.
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidBecomeActive(notification: NSNotification) {
let storyboard = NSStoryboard(name: "Application", bundle: nil)
applicationController = storyboard.instantiateInitialController() as? NSWindowController
if let window = applicationController?.window {
window.titlebarAppearsTransparent = true
window.titleVisibility = NSWindowTitleVisibility.Hidden
window.styleMask |= NSFullSizeContentViewWindowMask
applicationController!.showWindow(self)
}
}
}
Rather than subclassing NSWindowController, use the ViewController.swift subclass of NSViewController that Xcode creates for you automatically with the project.
In the storyboard, notice how there's a Relationship that connects the "window content" to the ViewController. So, the ViewController can now do things that you might previously have done in a window controller.
The ViewController.swift file will already have a default override of viewDidLoad() that will be called when the window loads, just as you were expecting windowDidLoad() to be called if it were an NSWindowController subclass.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
println("Executed")
}

NSTextField - notifications when individual keys are pressed

I am making an app that will add sound to keypresses as the user types in an NSTextField. I need to capture keystrokes and know what each individual keypress is (like "d" or "space" or "6"). The app depends on this. There is no other way around it.
Each window is an NSDocument File Owner, and it has a single NSTextField in it, which is where the document data is parsed, and the user will type.
After hours of parsing the Internet for answers and hacking away at code, the four most commonly repeated answers are:
"that is not how things work, here is (irrelevant answer)"
"you are new to Cocoa, that is a bad idea, use control:textView:doCommandSelector:" that doesn't give me individual keys, and some keys need their own unique sound trigger.
"use controlTextDidChange: or textView:shouldChangeTextInRange:replaceString:" controlTextDidChange doesn't give me individual keys, and the second one only works for textViews or UIKit.
People get confused and answer with recommendations for UIKit instead of AppKit, which is iOS-only.
The weird thing is that if I subclass NSTextField, it receives -keyUp. I don't know where -keyDown is going.
So my ultimate question is: can you tell me some kind of step-by-step way to actually capture the keyDown that is sent to NSTextField? Even if it's a hack. Even if it's a terrible idea.
I would love to solve this problem! I am very grateful for your reading.
controlTextDidChange is quite a good solution, but don't forget this 2 important things:
Set the delegate binding of the textField to the object where you define the controlTextDidChange method. Commonly, in document based apps it is the window controller, otherwise your app delegate.
Set the textField's control to "continous" in the attribute inspector section
If you miss those points, you will have no result.
This is a pretty old question, but as I was trying to implement a NSTextField that could react to keyDown so that I could create a hotkey preferences control I found I wanted the answer to this question.
Unfortunately this is a pretty non-standard use and I didn't find any places that had a direct answer, but I've come up with something that works after digging through the documentation (albeit in Swift 4) and I wanted to post it here in case it helps someone else with a non-standard use case.
This is largely based off of the information gleaned from the Cocoa Text Architecture Guide
There are three components to my solution:
Creating your NSWindowController and setting a NSWindowDelegate on your NSWindow:
guard let windowController = storyboard.instanciateController(withIdentifier:NSStoryboard.SceneIdentifier("SomeSceneIdentifier")) as? NSWindowController else {
fatalError("Error creating window controller");
}
if let viewController = windowController.contentViewController as? MyViewController {
windowController.window?.delegate=viewController;
}
Your NSWindowDelegate
class MyViewController: NSViewController, NSWindowDelegate {
// The TextField you want to capture keyDown on
var hotKeyTextField:NSTextField!;
// Your custom TextView which will handle keyDown
var hotKeySelectionFieldEditor:HotKeySelectionTextView = HotKeySelectionTextView();
func windowWillReturnFieldEditor(_ sender: NSWindow, to client: Any?) -> Any? {
// If the client (NSTextField) requesting the field editor is the one you want to capture key events on, return the custom field editor. Otherwise, return nil and get the default field editor.
if let textField = client as? NSTextField, textField.identifier == hotKeyTextField.identifier {
return hotKeySelectionFieldEditor;
}
return nil;
}
}
Your custom TextView where you handle keyDown
class HotKeySelectionTextView: NSTextView {
public override func keyDown(with event: NSEvent) {
// Here you can capture the key presses and perhaps save state or communicate back to the ViewController with a delegate pattern if you prefer.
}
}
I fully admit that this feels like a workaround somewhat, but as I am experimenting with Swift at the moment and not quite up to speed with all of the best practices yet I can't make an authoritative claim as to the "Swift-i-ness" of this solution, only that it does allow a NSTextField to capture keyDown events indirectly while maintaining the rest of the NSTextField functionality.
Try like this if you print nslog you will get individual character record for example you pressd "A" you will get the same in console:-
-(void)controlTextDidChange:(NSNotification*)obj
{
NSLog(#"%#",[yourTextfield stringValue]);
}
Also, not sure this is only your requirement.
Text editing for an NSTextField is handled by an NSTextView provided by the window, called the field editor. See the NSWindow method fieldEditor:forObject: and the NSWindowDelegate method windowWillReturnFieldEditor:toObject:. I suppose you could use one of these to provide your own subclassed NSTextView as the field editor. Or, could you simply use NSTextView instead of NSTextField?

Resources