macOS Printing in Swift - macos

Please note this is not an iOS question.
I have an NSView-based app (i.e. not document-based), and I’d like to bolt on a printing subsystem. I can get NSViews in my main controller to print ok. However, I want to have a special view constructed just for printing. The view should not show in the app’s window. The view contains two NSTextFields, two NSTextViews, and 5 labels.
I cannot seem to figure out a way to do this. I have tried various forms of these examples:
Add an NSView to my main view window? Seems logical, but it’s awkward in a storyboard, (I can’t position the view in the storyboard).
Programmatically create a custom NSView with a xib?
For this, I’ve tried:
#IBOutlet weak var printView: NSView!
….
let printOperation = NSPrintOperation(view: printView!)
This results in the comprehensive "fatal error: unexpectedly found nil while unwrapping an Optional value” message.
The outlets are configured correctly (I think)
A seperate ViewController? If so, how can I avoid having two print buttons — one to call the print controller, and the second, to print the PrintController’s view.
I’ve tried reading the Apple docs, but they are not the way I learn best. There are also no Print documents in Swift that I've found. I’ve waded through many SE questions, but have come up blank. Could you point me towards a solution please.

I think the specific problem here is that you're never causing the view to be loaded.
You can double check whether this is the case by overriding the viewDidLoad method on the controller. If it's never called, your view is never loaded from the nib.
Normally the UI machinery takes care of all that when you display a view controller, which is why it can be so confusing when it doesn't happen.
You should be able to trigger the view load by accessing the view property on the controller. e.g.
_ = self.view // Touch the view to force it to load
https://developer.apple.com/documentation/appkit/nsviewcontroller/1434401-view has some additional information.
You can also call loadView() directly although that's usually frowned upon.

Related

Leaving inputAccessoryView visible after keyboard is dismissed iOS8?

I want to make behavior like messaging app. I have been browsing Stack Overflow for solutions for this, and indeed there are plenty:
Leaving inputAccessoryView visible after keyboard is dismissed
This was the one that I found. But it seems things are a little different in iOS8. If I do the same thing in new iOS8 sdk, i get error:
'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<UICompatibilityInputViewController: 0x7fdcb3441b10> should have parent view controller:<ViewController: 0x7fdcb3b1e9f0> but requested parent is:<UIInputWindowController: 0x7fdcb684c000>'
In order to test this more I made a sample project, just one controller with view on the bottom:
Outlet is connected to bottom view, that only has UITextField on it. Am I missing something and how do i get the desired behvior?
iOS8 has a retain cycle with the inputAccessoryView. Here's a good post that seems to have a good workaround:
http://derpturkey.com/uitextfield-docked-like-ios-messenger/
You are adding the someView to multiple superViews, which leads to inconsistent hierarchies (which it is telling you).
When the keyboard gets activated, it calls the inputAccessoryView() method to see if it needs to stick anything on top of the keyboard, and adds that to its own superView. But you already added it to the view through your storyboard.
Now there are 2 ways you can solve this:
Make a .xib with your view and return that one in your inputAccessoryView(), not adding it to any superview yourself (the keyboard will.
Or make it completely in code using NSLayoutConstraint.
You can add the following code to your ViewController which will persist the view even when the keyboard is hidden.
override func canBecomeFirstResponder() -> Bool {
return true
}
Look at this GitHub repo for an example.

How does one display a new view controller in the same Mac window?

I'm fairly new to Mac development and am slightly confused by the new "storyboard" feature in Xcode 6. What I'm trying to do is segue from one view controller to another in the same window. As of right now, all the different NSViewControllerSegues present the view controller in a new window, be it a modal or just another window. What I'd like to do is just segue within the same window, much in the same way one would on iOS (though an animated transition is not crucial). How would this be achieved?
If you provide a custom segue (subclass of NSStoryboardSegue) you can get the result you are after. There are a few gotchas with this approach though:
the custom segue will use presentViewController:animator so you will need to provide an animator object
because the presented view is not backed by a separate Window object, you may need to provide it with a custom NSView just to catch out mouse events that you don't want to propagate to the underlying NSViewController's view
there's also a Swift-only glitch regarding the custom segue's identifier property you need to watch out for.
As there doesn't seem to be much documentation about this I have made a small demo project with custom segue examples in Swift and Objective-C.
I also have provided some more detail in answer to this question.
(Reviving this as it comes up as first relevant result on Google and I had the same problem but decided against a custom segue)
While custom segues work (at least, the code given in foundry's answer worked under Swift 3; it needs updating for Swift 4), the sheer amount of work involved in writing a custom animator suggests to me that their main use case is custom animations.
The simple solution to changing the content of a window is to create an NSWindowController for your window, and to set its contentViewController to the desired viewController. This is particularly useful if you are following the typical pattern of storyboards and instantiate a new ViewController instance every time you switch.
However.
The NSStoryboard documentation says, quite clearly in macOS, containment (rather than transition) is the more common notion for storyboards which led me to look again at the available tools.
You could use a container view for this task, which adds a NWViewController layer instead of the NSWindowController outlined above. The solution I've gone with is to use an NSTabViewController. In the attributes inspector, set the style to 'unspecified', then select the TabView and set its style to 'tabless'.
To change tabs programatically, you set the selectedTabViewItemIndexof your TabViewController.
This solution reuses the same instance of the ViewControllers for the tab content, so that any data entered in text fields is preserved when the user switches to the other 'tab'.
Simple way with no segues involved to replace the current view controller in the same window:
if let myViewController = self.storyboard?.instantiateController(withIdentifier: "MyViewController") as? MyViewController {
self.view.window?.contentViewController = myViewController
}

What is causing NSNull length unrecognized selector keyCommand error

I have this class/storyboard scene in a project that up to last night worked fine for the past 4 weeks i worked on it.
I have managed to comment out practically everything and I still get the crash when tapping on the UITextField and typing a number. It only crashes when I type in a value, otherwise it doesn't crash.
Here is the class as I am running it now:
import Foundation
import UIKit
import HealthKit
import CoreData
class WorkoutViewController: UITableViewController {
//Properties
#IBOutlet var numberOfLapsTextField: UITextField?
#IBOutlet var metersPerLapTextField: UITextField?
#IBOutlet var workoutDurationTextField: UITextField?
#IBOutlet var paceTextField: UITextField?
var healthStore:HKHealthStore?
override func viewDidLoad() {
super.viewDidLoad()
}
Originally it had the IBOutlets as ! instead of ? And it has a CoreData stack property, some blurred background effects for the tableview background, fetches user's weight from health store on viewDidLoad, a predicate helper method for health store fetches and a cancel and done button. The done button captured data from the textfields, made some computations and saved data to the health store and to coredata. But ALL of this has been commented out leaving only what is seen above.
I did managed to get a weird stack trace in the console one time (can't seem to get it anymore) that read:
[UIPhysicalKeyboardEvent _matchesKeyCommand:]
and a few others like it just before it.
Im stumped, any ideas? Exception breakpoints is one but it just throws me to the AppDelegate class declaration line where UIResponder is adopted. Ive learned and rebuilt. I don't know what else to look for.
Im thinking its a corrupt storyboard file because Ive added and removed scenes with textfields and they all behave the same. Here is my storyboard.xml file:
http://www.santiapps.com/iOS/Main.storyboard.xml
I had a crash with the same cryptic NSNull length message. It cropped up occasionally on only one screen when testing in the simulator and by trial and error I discovered it only happened when I pressed the cmd key.
Looking in my storyboard source I found the following:
<keyCommands>
<keyCommand/>
</keyCommands>
Which I think lets you define keyboard shortcuts for anyone using a bluetooth keyboard. However - this is an empty definition, so it seems pressing cmd caused whatever this executes to fail because I don't have any valid shortcuts defined.
I have absolutely no idea how this happened, but if you use the graphical Interface Builder UI to look at your Storyboard, go to the view controller giving you the problem and click on the root view controller. Under the Attributes inspector there is a Key Commands section. I reckon I must have accidentally hit the + button here at some point. You can select the first 'item' and hit - to get rid of it and the problem should disappear.
Alternatively, delete the keyCommands section from the Storyboard source directly.
I see this question has already been marked as answered, but I include this in case the additional information on top of the original answer (and comment) is useful to someone else.
Ok I deleted the navigation controllers and tab bar controller and re-added them, and the problem is gone! There are no more crashes when I type in data. It was obviously a corrupted scene but the weird thing is that all of a sudden, all scenes with uitextfields got corrupted in the same way.
Click on view controller and in attributes inspector under Key commands remove "Enter action below" entry

Can't get NSTableView to display any text whatsoever

I've been developing on iOS for some time, but am very new to Cocoa development, and something seemingly very simple is stumping me.
I have an NSTableView, hooked up to a subclass of NSWindowController as both datasource and delegate. I have an array of "File" objects (my model class) and want to populate one column of my tableview with file types, and another with timestamps.
The dataSource methods are definitely being called, as verified by setting breakpoints. In fact, I end up with an appropriate number of rows that are selectable...but none of them display anything. I even tried returning an arbitrary string literal in objectValueForTableColumn, for all rows and columns, and still nothing.
I think I'm probably stuck on how tableViews work in iOS, but obviously they are very different here...I am used to configuring and returning a cell myself, but here we just pass AnyObject??? How exactly does the tableView know how to display AnyObject? I'm really struggling with the conceptual understanding here. Appreciate any help.

IBOutlets in Swift are nil

(Developing on OSX 10.9, Xcode 6.1b)
If I declare IBOutlets in my AppController, everything is fine. I instantiate an object in InterfaceBuilder, drag it across to form an outlet, and by the time I have reached applicationDidFinishLaunching, my IBOutlets are populated and everything is great.
If I go a step further in abstraction and instantiate a custom controller object in InterfaceBuilder (a subclass of NSObject), and declare one of my objects as an IBOutlet in that class, they're nil, each and every one.
I can set the connection just fine, IB seems convinced it exists, the 'referenced outlets' list is correct, but it doesn't take, and I haven't been able to find anything in the documentation beyond
It is implicitly unwrapped because after your class is initialized from a storyboard or xib file, you can assume that the outlet has been connected.
Well, I was assuming that.
All of my code is boilerplate Xcode offerings:
#IBOutlet weak var sceneController: NSArrayController!
and I've checked and double-checked and triple-checked the connections. I've looked at dozens of iOS tutorials (although I cannot find the equivalent for OSX) all of which seems to be variations on the theme of 'yes, you can totally declare an outlet in a file other than the AppController, just make sure that every involved instance exists'.
(At the time of writing, the 'mac' documentation uses examples featuring UIButton etc.)
I'm out of ideas. It's obvious that the connection is not formed, presumably because the objects are instantiated in an order other than 'controller class first, IBOutlets later', but how can I force this connection?
#IBOutlet weak var sceneController: NSArrayController!
The weak keyword is your problem. If, after the system finishes decoding your nib, nothing else references the NSArrayController, then the system will immediately set the outlet to nil and deallocate the NSArrayController.
Try removing the weak keyword from your outlets.
UPDATE
Add this code:
#IBOutlet var sceneController: NSArrayController! {
didSet {
NSLog("sceneController set to %#", sceneController);
}
}
What's the output? Put a breakpoint on the NSLog. What's the stack trace when it's hit? Is it hit repeatedly?
I had a similar problem
I have a button that takes me from one view controller to another. Inside the event handler for this button I was trying to set values for some of the GUI components in the second view. Unfortunately, I was getting a nil value for those components.
The solution was to wait until the button handler exited then make changes to the GUI components in the second view (try the viewDidLoad() function on the second view controller). Presumably those GUI components were not allocated until I left the handler and switched views.
yuen helbig
I had the issue when I was configuring the corner radius of the buttons in viewWillAppear. Moving this part to viewDidLoad solved the issue.

Resources