I have an app with a main window and a secondary window with an NSScannerDeviceView in it.
The idea is to scan an image to memory and place it into an NSImageView in the main window.
So far so good. When the scanner window is opened I can scan an image and it is placed correctly in to the NSImageView.
The problem is this, I can scan again and again but if I close the scan window and re-open it the NSScannerDeviceView no longer works. I'm sure the solution is relatively simple but as I'm still learning obj-c / cocoa it's got me stumped.
The code I'm using is as follows: ScanWindowController.h
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#import <ImageCaptureCore/ImageCaptureCore.h>
#interface ScanWindowController : NSWindowController <IKScannerDeviceViewDelegate, ICScannerDeviceDelegate, ICDeviceBrowserDelegate> {
ICDeviceBrowser *mDeviceBrowser;
}
#property (strong) IBOutlet IKScannerDeviceView *ScannerView;
#end
and: ScannerWindowController.m
#import "ScanWindowController.h"
#import "AppDelegate.h"
#interface ScanWindowController ()
#end
#implementation ScanWindowController
#synthesize ScannerView;
- (id)init
{
if ( ! (self = [super initWithWindowNibName: #"ScanWindowController"]) ) {
return nil;
}
mDeviceBrowser = [[ICDeviceBrowser alloc] init];
mDeviceBrowser.delegate = self;
mDeviceBrowser.browsedDeviceTypeMask = ICDeviceLocationTypeMaskLocal|ICDeviceLocationTypeMaskRemote|ICDeviceTypeMaskScanner;
[mDeviceBrowser start];
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
}
- (void)windowWillClose:(NSNotification *)notification {
[mDeviceBrowser stop];
}
- (void)scannerDeviceView:(IKScannerDeviceView *)scannerDeviceView didScanToURL:(NSURL *)url fileData:(NSData *)data error:(NSError *)error
{
if(!error) {
[(AppDelegate*)[[NSApplication sharedApplication] delegate] recieveScannedData:data];
}
}
- (void)scannerDeviceDidBecomeAvailable:(ICScannerDevice*)scanner;
{
[scanner requestOpenSession];
}
- (void)deviceBrowser:(ICDeviceBrowser*)browser didAddDevice:(ICDevice*)addedDevice moreComing:(BOOL)moreComing
{
if ( (addedDevice.type & ICDeviceTypeMaskScanner) == ICDeviceTypeScanner )
{
[ScannerView setScannerDevice:(ICScannerDevice*)addedDevice];
}
}
-(void)deviceBrowser:(ICDeviceBrowser *)browser didRemoveDevice:(ICDevice *)device moreGoing:(BOOL)moreGoing
{
[device requestCloseSession];
}
-(void)didRemoveDevice:(ICDevice*)removedDevice
{
[removedDevice requestCloseSession];
}
#end
I have solved this problem (after talking to Apple), and the result is this:
The NSScannerDeviceView is activated when the window is initialised, however as this doesn't get called again and the NSScannerDeviceView is closed when the window is closed, it never gets reactivated.
The solution was to create a new method (scannerReopen) and call it each time the window is opened as follows:
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#import <ImageCaptureCore/ImageCaptureCore.h>
#interface ScanWindowController : NSWindowController <IKScannerDeviceViewDelegate, ICScannerDeviceDelegate, ICDeviceBrowserDelegate> {
ICDeviceBrowser *mDeviceBrowser;
}
#property (strong) IBOutlet IKScannerDeviceView *ScannerView;
-(void)scannerReopen;
#end
and:
#import "ScanWindowController.h"
#import "AppDelegate.h"
#interface ScanWindowController ()
#end
#implementation ScanWindowController
#synthesize ScannerView;
- (id)init
{
if ( ! (self = [super initWithWindowNibName: #"ScanWindowController"]) ) {
return nil;
}
if(!mDeviceBrowser) {
mDeviceBrowser = [[ICDeviceBrowser alloc] init];
[mDeviceBrowser setDelegate:self];
mDeviceBrowser.browsedDeviceTypeMask = ICDeviceLocationTypeMaskLocal|ICDeviceLocationTypeMaskRemote|ICDeviceTypeMaskScanner;
[mDeviceBrowser start];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
}
- (void)windowWillClose:(NSNotification *)notification {
[mDeviceBrowser stop];
}
- (void)scannerDeviceView:(IKScannerDeviceView *)scannerDeviceView didScanToURL:(NSURL *)url fileData:(NSData *)data error:(NSError *)error
{
//process the scanned data...
if(!error) {
[(AppDelegate*)[[NSApplication sharedApplication] delegate] receiveScannedData:data];
}
}
- (void)scannerDeviceDidBecomeAvailable:(ICScannerDevice*)scanner;
{
[scanner requestOpenSession];
}
- (void)deviceBrowser:(ICDeviceBrowser*)browser didAddDevice:(ICDevice*)addedDevice moreComing:(BOOL)moreComing
{
if ( (addedDevice.type & ICDeviceTypeMaskScanner) == ICDeviceTypeScanner )
{
[ScannerView setScannerDevice:(ICScannerDevice*)addedDevice];
}
}
-(void)deviceBrowser:(ICDeviceBrowser *)browser didRemoveDevice:(ICDevice *)device moreGoing:(BOOL)moreGoing
{
[device requestCloseSession];
}
-(void)didRemoveDevice:(ICDevice*)removedDevice
{
[removedDevice requestCloseSession];
}
-(void)scannerReopen
{
[mDeviceBrowser start];
}
#end
Related
I've been having problems calling on a block, I keep getting the error EXC_BAD_ACCESS code=2.
.h file
#interface TheClass : UIView
typedef void (^AnotherBlock)();
#property (copy) AnotherBlock block;
- (void) testMethod;
#end
.m file
#import "TheClass.h"
#implementation TheClass
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self testMethod];
}
return self;
}
- (void) testMethod {
self.block();
}
#end
And in the main view controller
.m file
#import "MainViewController.h"
#import "TheClass.h"
#interface MainViewController ()
#property TheClass *mClass;
#end
#implementation MainViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)button:(id)sender {
self.mClass = [[TheClass alloc] init];
[self.mClass testMethod];
self.mClass.block = ^ {
NSLog(#"Remote Block Worked");
};
}
#end
The button is linked up and working correctly, when touched I get the error message.
In the debugger there is a message that says:
_block AnotherBlock <parent is NULL>
Any help would be great. Thanks
You are calling testMethod before there's anything assigned to the block variable. Make sure there's something in it before calling testMethod.
I am very new to MAC OSX app development.
In my application I have three NSViewControllers, which are PracticeController, NoteController and QuestionController. I have to navigate to NoteViewController from PracticeController and QuestionController and comeback to the viewController from which NoteController has navigated.
For example: when we navigate to NoteController from PracticeController, then when we tap on back button from NoteController I have to come to PracticeController, and when we navigate to NoteController from QuestionController, then when we tap on back button from NoteController I have to come to QuestionController.
Please help me how to do this? I am badly searching for it. Thanks.
well, after a long time search, I found a open source library which ports the UIKit to MacOSX.
https://github.com/BigZaphod/Chameleon.git
But it's too complicated for me, so I wrote my own Navigation controller.
NSNavigationController.h
#import <Cocoa/Cocoa.h>
#class BaseViewController;
#interface NSNavigationController : NSResponder
#property (nonatomic, strong) BaseViewController *rootViewController;
- (id)initWithRootViewController:(BaseViewController *)rootViewController;
- (NSView*)view;
- (void)pushViewController:(BaseViewController *)viewController animated:(BOOL)animated;
- (BaseViewController *)popViewControllerAnimated:(BOOL)animated;
#end
NSNavigationController.m
#import "NSNavigationController.h"
#import "AppDelegate.h"
#import "BaseViewController.h"
#interface NSNavigationController ()
#property (nonatomic, strong) NSMutableArray *viewControllerStack;
#end
#implementation NSNavigationController
- (id)initWithRootViewController:(BaseViewController *)rootViewController
{
self = [super init];
if (self) {
self.rootViewController = rootViewController;
self.rootViewController.navigationController = self;
self.viewControllerStack = [[NSMutableArray alloc] initWithObjects:self.rootViewController, nil];
}
return self;
}
- (NSView*)view
{
BaseViewController *topViewController = [self.viewControllerStack objectAtIndex:[self.viewControllerStack count] - 1];
return topViewController.view;
}
- (void)pushViewController:(BaseViewController *)viewController animated:(BOOL)animated
{
if (viewController != nil) {
[self removeTopView];
[self.viewControllerStack addObject:viewController];
viewController.navigationController = self;
[self addTopView];
}
}
- (BaseViewController *)popViewControllerAnimated:(BOOL)animated
{
BaseViewController *topViewController = [self.viewControllerStack objectAtIndex:[self.viewControllerStack count] - 1];
[self removeTopView];
[self.viewControllerStack removeLastObject];
[self addTopView];
return topViewController;
}
- (void)removeTopView
{
BaseViewController *topViewController = [self.viewControllerStack objectAtIndex:[self.viewControllerStack count] - 1];
[topViewController.view removeFromSuperview];
}
- (void)addTopView
{
BaseViewController *topViewController = [self.viewControllerStack objectAtIndex:[self.viewControllerStack count] - 1];
AppDelegate *delegate = (AppDelegate*)[NSApp delegate];
[delegate.window.contentView addSubview:topViewController.view];
}
#end
BaseViewController.h
#import <Cocoa/Cocoa.h>
#class NSNavigationController;
#interface BaseViewController : NSViewController
#property (nonatomic, weak) NSNavigationController *navigationController;
#end
BaseViewController.m
#import "BaseViewController.h"
#interface BaseViewController ()
#end
#implementation BaseViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
#end
It's the simplest NavigationController. I didn't implement the view animation. Hope it can help.
What does this Expected expression error mean on this line of code:
[super setNilValueForKey:key];
Code:
#import "Person.h"
#implementation Person
- (id) init
{
self = [super init];
expectedRaise = 5.0;
personName = #"New Person";
return self;
}
- (void)dealloc
{
[personName release];
[super dealloc];
}
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqual:#"expectedRaise"])
{
[self setExpectedRaise:0.0];
}
else
{
[super setNilValueForKey:key];
}
}
#synthesize personName;
#synthesize expectedRaise;
#end
.h:
#import <Foundation/Foundation.h>
#interface Person : NSObject
{
NSString *personName;
float expectedRaise;
}
#property (readwrite, copy) NSString *personName;
#property (readwrite) float expectedRaise;
#end
If you really got
[super setNilValueForKey:<#key#>];
in your code, the solution should be as simple as
[super setNilValueForKey:key];
isEqual is the wrong method since I wanted to compare to a string value isEqualToString is correct.
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqualToString:#"expectedRaise"])
{
[self setExpectedRaise:0.0];
}
else
{
[super setNilValueForKey:key];
}
}
MainController.h
#import <Cocoa/Cocoa.h>
#interface MainController : NSWindowController {
NSWindowController *sc;
IBOutlet NSTextField *txt1;
}
-(IBAction)executeButtonClick:(id)sender;
-(void)setTxt1Text:(NSString *)txt;
#end
MainController.m
#import "MainController.h"
#import "SecondController.h"
#implementation MainController
-(void)awakeFromNib
{
sc = nil;
}
-(IBAction)executeButtonClick:(id)sender;
{
if (sc == nil)
{
sc = [[SecondController alloc] initWithMController:self];
}
[sc showWindow:self];
[[sc window] makeKeyAndOrderFront:sender];
}
-(void)setTxt1Text:(NSString *)txt;
{
[txt1 setStringValue:txt];
}
#end
SecondController.h
#import <Cocoa/Cocoa.h>
#interface SecondController : NSWindowController {
NSWindowController *mController;
}
-(id)initWithMController:(NSWindowController *)mctrl;
-(IBAction)testButtonClick:(id)sender;
#end
Here's the problem reside:
SecondController.m
#import "SecondController.h"
#implementation SecondController
-(id)initWithMController:(NSWindowController *)mctrl;
{
self = [super initWithWindowNibName:#"SecondWindow"];
mController = mctrl;
NSLog(#"%#",mController);
return self;
}
-(IBAction)testButtonClick:(id)sender;
{
NSLog(#"%#",mController);
[mController setTxt1Text:#"Test Success"];
}
#end
Logs:
2011-05-09 15:41:10.337 MultiWindow[4334:a0f] <MainController: 0x1005295b0>
2011-05-09 15:41:11.336 MultiWindow[4334:a0f] (null)
Why mController became null ? Can anyone help ?
Thanks
Try logging self in both places as well (in addition to mController). My guess is you will find that you have more than one SecondController object (perhaps one created in code through -[MainController executeButtonClick:] and the other in your SecondWindow nib?).
I’m learning Cocoa programming, and I’m unable to figure out how to
archive and unarchive a custom NSView subclass
I’ve made a toy application that presents a window. This window
contains an instance of my custom BackgroundView class (archived in
the xib file). Clicking anywhere in this BackgroundView creates and
displays a blue square whose origin is the click point. This square is
an instance of my Square class. All of this works as I expect.
The Square class implements the NSCoding protocol. I’ve added
dataOfType: typeName: error: and readfromData: ofType: error: methods
to the MyDocument class. As far as I can tell from log statements, the
Squares are being archived to the file, and unarchived when the file
is loaded. But I’m unable to make the squares display themselves on
the window. The Square class’s drawWithRect: method is never called,
even though I’ve called setNeedsDisplay:YES on each square.
The code is as follows:
Square.h
#import <Cocoa/Cocoa.h>
#interface Square : NSView <NSCoding> {
}
#end
Square.m
#import "Square.h"
#implementation Square
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
return self;
}
- (void)drawRect:(NSRect)rect {
[[NSColor blueColor] set];
NSBezierPath *newPath = [NSBezierPath bezierPathWithRect:[self bounds]];
[newPath fill];
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeRect:[self frame] forKey:#"frame"];
}
-(id)initWithCoder:(NSCoder *)coder
{
NSRect theRect = [coder decodeRectForKey:#"frame"];
self = [super initWithFrame:theRect];
return self;
}
#end
-----
BackgroundView.h
#import <Cocoa/Cocoa.h>
#interface BackgroundView : NSView {
}
#end
BackgroundView.m
#import "BackgroundView.h"
#import "Square.h"
#implementation BackgroundView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)rect {
for (Square *subview in self.subviews)
[subview setNeedsDisplay:YES];
}
-(void)mouseDown:(NSEvent *)theEvent {
NSPoint unsetClick = [theEvent locationInWindow];
NSPoint theClick = [self convertPoint:unsetClick fromView:nil];
NSRect theRect;
theRect.origin = theClick;
theRect.size.width = 100.00;
theRect.size.height = 100.00;
Square *newSquare = [[Square alloc] initWithFrame:theRect];
[self addSubview:newSquare];
[newSquare setNeedsDisplay:YES];
}
#end
------
MyDocument.h
#import <Cocoa/Cocoa.h>
#class BackgroundView;
#interface MyDocument : NSDocument
{
IBOutlet BackgroundView *theBackgroundView;
}
#end
MyDocument.m
#import "MyDocument.h"
#implementation MyDocument
- (id)init
{
self = [super init];
return self;
}
- (NSString *)windowNibName
{
return #"MyDocument";
}
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
[super windowControllerDidLoadNib:aController];
}
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
return [NSKeyedArchiver
archivedDataWithRootObject:[theBackgroundView subviews]];
if ( outError != NULL ) {
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain
code:unimpErr userInfo:NULL];
}
return nil;
}
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
error:(NSError **)outError
{
[theBackgroundView setSubviews:[NSKeyedUnarchiver
unarchiveObjectWithData:data]];
[theBackgroundView setNeedsDisplay:YES];
if ( outError != NULL ) {
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain
code:unimpErr userInfo:NULL];
}
return YES;
}
#end
Since Square is a subclass of NSView, and NSView also implements NSCoding, you need to invoke super's implementation of both encodeWithCoder: and initWithCoder:.
#implementation Square
- (id)initWithFrame:(NSRect)frame
{
return [super initWithFrame:frame];
}
-(id)initWithCoder:(NSCoder *)coder
{
if ((self = [super initWithCoder:coder]))
{
NSRect theRect = [coder decodeRectForKey:#"frame"];
self = [super initWithFrame:theRect];
return self;
}
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeRect:[self frame] forKey:#"frame"];
}
- (void)drawRect:(NSRect)rect
{
[[NSColor blueColor] set];
[[NSBezierPath bezierPathWithRect:[self bounds]] fill];
}
#end
As I can see you don't add any of the initWithXXX and encodeWithXXX methods since you don't have custom member variables.
I suggest to check out Apple's Sketch Example.