Printing an invisible NSView - objective-c

Initially I created a simple program with a custom NSView. I drew a picture (certificate) and printed it! beautiful! Everything worked great!
I then moved my custom NSView to an existing application. My hope was that when a user hit print it would print this certificate. Simple enough. I figured a could have a NSView pointer in my controller code. Then at initialization I would populate the pointer. Then when someone wanted to print the certificate it would print. The problem is that all of my drawing code is in the "drawRect" method. This method doesn't get called because this view is never displayed in a window.
I have heard that others use non-visible NSView objects just for printing. What do I need to do? I really don't want to show this view to the screen.
Rodger

You don't have to create the view in advance, you can create it when needed.
If you have Document Based application and a view that you want to dump to printer, then in our MyDocument (or whatever you call it) that extends NSDocument you would implement:
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps
error:(NSError **)e
The view then uses standard drawRect: for drawing.
Example, here PeopleView just draws a table of people details, it takes a NSDictonary of people employees here:
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps
error:(NSError **)e
{
PeopleView * view = [[PeopleView alloc] initWithPeople:employees];
NSPrintInfo * printInfo = [self printInfo];
NSPrintOperation * printOp
= [NSPrintOperation printOperationWithView:view
printInfo:printInfo];
[view release];
return printOp;
}
You can look for more details in chapter 27, Printing, in Hillegass' Cocoa Programming For Mac OS X.

Related

iOS7 Keyboard Behavior with SearchBar/UITableView: Offset on secondary view appearances

Problem
I am having a rather big issue with the iOS7 keyboard appearance. I have a Searchbar on a UIViewController with TableView Delegation/Data Source setup (I am using the self.searchDisplayController delegates as well). I segue from this scene to a prototype tableview to show the results.
Here is the issue:
On first load I can see the keyboard being displayed when I tap into the text field of the UISearchBar. I can type and perform a search with the results being shown in the next scene.
I've added NSNotifications to view the keyboard properties in local methods keyboardWillShow and keyboardWasShown. I can see on the first scene appearance (after the view is completely loaded):
I segue to the result tableview at this point and when I navigate back and touch the text field, my keyboard shows up either fully or partially off-screen:
When I look at the keyboardWillShow notification at this point I can see that my keyboard values are incorrect:
I've researched and tried many possibilities including:
Added the following to my main view controller:
-(BOOL)canResignFirstResponder
{
return YES;
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
Configured the following in my view did load
self.searchDisplayController.searchBar.spellCheckingType = UITextSpellCheckingTypeNo;
self.searchDisplayController.searchBar.autocapitalizationType= UITextAutocapitalizationTypeNone;
self.searchDisplayController.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.searchDisplayController.searchBar.keyboardType = UIKeyboardTypeDefault;
Put in standard stubs for:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
I've noticed that if I choose a Partial Curl as my segue mode, the keyboard remains accessible when I roll back to the main view controller (but then it was never fully off screen in that case). However if I move from the results tableview to a detail scene and then navigate back to the main view controller, the keyboard appears off-screen again.
Question
Is there a method I can use to intercept the misplaced keyboard so that it displays in the default location?
NB: Along these lines, I have created a NSDictionary property to hold the initial userInfo values with the correct keyboard placement. I am not sure how to reassign these values to get the keyboard to return to it's original placement.
BTW - This seems a bit of a hack to get the keyboard fixed due to a bug in IB, is there some other way that I can try to remedy the situation?
Thanks in advance for any feedback!
Solution
This was such an obscure issue that I'm sharing the solution to save the next person some effort. Like most programming issues, it turns out this one was self-inflicted. In my original iteration of this project I had turned off rotational support as I am learning auto-layout and I wanted to ease into the transition from Springs and Struts. Somehow between the start of the project and the code release I ended up with this bit of code in the Main Scenes' View Controller.
//BAD
- (NSUInteger) supportedInterfaceOrientations
{
return !UIInterfaceOrientationPortraitUpsideDown;
}
instead of returning a valid enumeration like...
//OK
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}

Understanding Objective-C Overwrite Code

Disclaimer: The program I wrote works. I just need help understanding it.
Today I just started learning XCode and Objective-C. I have tons of experience with Java and I must admit this is very different. I'm currently following a book that has us dealing with two scenes in our story board. The Main View Controller Scene and the Flipside View Controller Scene.
In the main scene I have one label outlet that says Hello World. It's name is label. In the flipside scene I have a text outlet. When the user flips from the flipside scene to the main scene the text in the text outlet is applied to the label outlet. So if I type in Hello StackOverflow in the flipside, and then flip it I will see Hello StackOverflow in the main scene.
We did this by going to the method that controls the flip and is in the main scene .m class and added this code.
self.label.text = controller.labelText.text;
Can anyone explain this code please? I understand that label and labelText are my names. And text is looking for the text. But I have no idea where self and controller came from and it is not explained in the book. Thank you.
EDIT
Here is the full code with the function that has controller in the function heading. I don't get what is going on in this method. Any explanation would be great.
- (void)flipsideViewControllerDidFinish:(HWFlipsideViewController *)controller
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else
{
[self.flipsidePopoverController dismissPopoverAnimated:YES];
self.flipsidePopoverController = nil;
}
self.label.text = controller.labelText.text;
}
self is the java this pointer and therefore points to your object
text is a property and would be getText()
so self.label.text is this.getLabel().getText() BUT
as it sets text=something it is setText!
=> java-bean style getter and setter
controller is likely a local variable.. PASSED to the method as an argument
OR it is a member variable on the object of the this instance
so it'd be
this.getLabel().setText(controller.getLabelText().getText());
this = the object you're in and that the main view I think
controller is the flipview
the 'overwrite' is setting the String of our label to the Controller's textfield string
You described, that you are in the context, where the flip is controlled. So the instance of the second controller (named controller) must be available there. So you may refer to it.
self is, as Xono stated in the comment already the pointer to the current object like this in java.

How to display a view from a NSViewController?

I'm new at objective-c so please bear with me. I have a class that returns a picture from a webcam and I am trying to display that to the screen. I subclassed NSViewController to get the image from the camera class and set it to an instance NSImageView and set the NSViewController's view to be the NSImageView. I have created a custom view in Interface Builder, dragged a NSViewController object into MainMenu.xib set it's class to be PhotoGrabberController, and control click-dragged from the custom view to the PhotoGrabberController to set it's outlet binding to be the view. (I really don't know how that works behind the scenes, it seems like magic to me). Nothing shows up on the screen and I've been playing around with this forever.
In PhotoGrabberController.h
#interface PhotoGrabberController : NSViewController {
PhotoGrabber * grabber;
NSImageView* iView;
}
#property (nonatomic, retain) NSImageView* iView;
In PhotoGrabberController.m
#implementation PhotoGrabberController
#synthesize iView,grabber;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void) awakeFromNib
{
self = [super init];
if (self) {
NSRect rect = NSMakeRect(10, 10, 200, 100);
iView = [[NSImageView alloc] initWithFrame:rect];
[iView setImageScaling:NSScaleToFit];
[iView setImage:[NSImage imageNamed:#"picture.png"]];
grabber = [[PhotoGrabber alloc] init];
grabber.delegate = (id)self;
//[grabber grabPhoto]; (This usually sets iView but I removed it and manually set iView to be an image from file above for simplicity)
[self setView:iView];
}
}
I toyed around with putting a bunch of different things in the AppDelegate's applicationDidFinishLaunching but the way it's set up right now I think I shouldn't need to put anything in there.
Question
How do I get the main window to show the image?
Is it ok to use PhotoGrabberController.view instead of creating a view subclass?
What is wrong with my understanding of how things work here? I've spent a lot of time trying to figure this out and have gotten nowhere.
Can you direct me to a resource where I can fully understand how this works? I've found Apple documentation too detailed and thick. I just want to understand how windows, views, and view controllers are loaded and interact.
How do I get the main window to show the image?
That's a view's job.
As you already have a view, you just need to get it into the window. A window only holds one view directly, which is called its content view; most probably, you will want to add the view controller's view as a subview of the view that is already the content view, or as a subview of some other grandchild view.
Is it ok to use PhotoGrabberController.view instead of creating a view subclass?
These are orthogonal. The VC's view is an instance of a view class. Creating a class has nothing to do with using an instance.
I don't think creating a view subclass will help you here. You already have two working views: a plain NSView as the VC's immediate view, and a NSImageView within it. No subclass is necessary to add these to the view—with the image view within it—to the window.
What is wrong with my understanding of how things work here?
You've missed the entire concept of the view hierarchy, and the fact that such a hierarchy is how every window displays its content.
Also, you began your question with “I'm new at objective-c” (sic), but your question is about the Cocoa framework (specifically, the AppKit framework), not the Objective-C language.
Can you direct me to a resource where I can fully understand how this works?
The Window Programming Guide (particularly, “How Windows Work”) and the View Programming Guide (particularly, “Working with the View Hierarchy”).

Replacing Storyboard Segue with pushViewController causes strange behaviour

I can't seem to figure this out for the life of me. I have a custom table view cell, in that cell I have a few buttons configured. Each button connects to other view controllers via a storyboard segue. I've recently removed these segues and put a pushViewController method in place. Transition back and forth across the various views works as expected however the destination view controller is not displaying anything! I have some code below as an example.
Buttons have this method set:
[cell.spotButton1 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
[cell.spotButton4 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
showSpotDetails Method contains this code:
- (void)showSpotDetails:(id)sender
{
// determine which button (spot) was selected, then use its tag parameter to determine the spot.
UIButton *selectedButton = (UIButton *)sender;
Spot *spot = (Spot *)[spotsArray_ objectAtIndex:selectedButton.tag];
SpotDetails *spotDetails = [[SpotDetails alloc] init];
[spotDetails setSpotDetailsObject:spot];
[self.navigationController pushViewController:spotDetails animated:YES];
}
The details VC does receive the object data.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"spotDetailsObject %#", spotDetailsObject_.name);
}
The NSLog method below does output the passed object. Also, everything in the details view controller is as it was. Nothing has changed on the details VC. It just does not render anything ever since I removed the segue and added the pushViewController method. Perhaps I am missing something on the pushViewController method? I never really do things this way, I try to always use segues...
Any suggestions?
Welcome to the real world. Previously, the storyboard was a crutch; you were hiding from yourself the true facts about how view controllers work. Now you are trying to throw away that crutch. Good! But now you must learn to walk. :) The key here is this line:
SpotDetails *spotDetails = [[SpotDetails alloc] init];
SpotDetails is a UIViewController subclass. You are not doing anything here that would cause this UIViewController to have a view. Thus you are ending up a with blank generic view! If you want a UIViewController to have a view, you need to give it a view somehow. For example, you could draw the view in a nib called SpotDetails.xib where the File's Owner is an SpotDetails instance. Or you could construct the view's contents in code in your override of viewDidLoad. The details are in the UIViewController documentation, or, even better, read my book which tells you all about how a view controller gets its view:
http://www.apeth.com/iOSBook/ch19.html
The reason this problem didn't arise before is that you drew the view in the same nib as the view controller (i.e. the storyboard file). But when you alloc-init a SpotDetails, that is not the same instance as the one in the storyboard file, so you don't get that view. Thus, one solution could be to load the storyboard and fetch that SpotDetails instance, the one in the storyboard (by calling instantiateViewControllerWithIdentifier:). I explain how to do that here:
http://www.apeth.com/iOSBook/ch19.html#SECsivc

Custom control in NSCollectionViewItem

I want to put a custom control inside the view for my NSCollectionViewItem.
Lets say I have a custom NSView called BoxesView.BoxesView is just a view that draws a predetermined number of boxes in its view. That number of boxes is set in the init method. Lets say I set it to 8.
When I load up the collection view, all the other controls in the view work fine, (buttons, sliders, etc.) but my view won't draw.
If I set a breakpoint in the drawRect method of BoxesView it shows that the number of boxes to draw is 0! If I set a breakpoint in my init method where I set numBoxes to 8, it shows that numBoxes does actually get set to 8. Also, the init method only gets called 1 time even though there are multiple rows in the collection view.
What am I doing wrong?
UPDATE
I was able to get this working by setting the itemPrototype to load from a xib instead of being in the same xib as the NSCollectionViewItem. This is great, except it only works on 10.6 and not 10.5.
UPDATE 2
What I'm trying to do, is stick my custom view inside the view that already existed for the NSCollectionViewItem that already exists. What happens is the member variable mBoxWidth gets blown away and is zero so when it goes to draw it, nothing happens.
#implementation DumbView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
mBoxWidth = 3;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
NSRect bounds = self.bounds;
[[NSColor redColor]set];
[NSBezierPath fillRect:NSMakeRect(bounds.origin.x, bounds.origin.y, mBoxWidth, mBoxWidth)];
}
#end
I didn't implement initWithCoder. That fixes everything.
NSCollectionViewItem uses a prototype view, which is duplicated and wired up for each item in the collection's represented objects.
You could go through all the trouble to make an IBPlugin for your custom view (one that exposes the numberOfBoxesToDraw binding), but that's a pain in the ass and there's an easier way: manual bindings.
Using Manual Bindings with NSCollectionView/Item
First, subclcass NSCollectionViewItem, tell IB to use this new subclass, and make sure you have an outlet in it (like boxView) that is connected to your custom view.
Next, subclass NSCollectionView (set IB to use this subclass) and override -newItemForRepresentedObject:. In it, you'll first call super (storing the result to a local variable), then manually bind your "boxView"'s number of boxes to the represented object with the "numberOfBoxes" key you're using in your model.
Have you tried overloading copyWithZone?
I'm guessing your item is getting copied and not directly init'd.