Print of multi page document cocoa - objective-c

I am developing a document-based application. This document can have multiple pages. so I have an array of NSView objects available with me. Now I want to provide print functionality in this app, but NSPrintOpertion takes only one NSView object so I am not able to generate print preview as well as print off multiple pages of the document.
Is there any way in cocoa to print a multi-page document?

Printing in Cocoa does not come for free, unfortunately. You have to implement your own NSView subclass that handles the drawing. It has to have access to your data and to draw the correct data onto each page, depending on the page number.
Here is a sample source code that can print any NSTableView. I used it here to show how you have to calculate the position of things to draw on the page:
MyPrintView.h:
#import <Cocoa/Cocoa.h>
#interface MyPrintView : NSView {
NSString *printJobTitle;
}
#property (copy, readwrite) NSString *printJobTitle;
- (id)initWithTableView:(NSTableView *)tableToPrint andHeader:(NSString *)header;
#end
MyPrintView.m:
#import "MyPrintView.h"
#interface MyPrintView ()
#property (nonatomic, weak) NSTableView *tableToPrint;
#property (nonatomic, strong) NSString *header;
#property (nonatomic, strong) NSDictionary *attributes;
#property (nonatomic, strong) NSFont *listFont;
#property (nonatomic) float headerHeight;
#property (nonatomic) float footerHeight;
#property (nonatomic) float lineHeight;
#property (nonatomic) float entryHeight;
#property (nonatomic) NSRect pageRect;
#property (nonatomic) int linesPerPage;
#property (nonatomic) int currentPage;
#end
#implementation MyPrintView
#synthesize printJobTitle;
- (id)initWithTableView:(NSTableView *)tableToPrint andHeader:(NSString *)header
{
// Initialize with dummy frame
self = [super initWithFrame:NSMakeRect(0, 0, 700, 700)];
if (self) {
self.tableToPrint = tableToPrint;
self.header = header;
self.listFont = [NSFont fontWithName:#"Helvetica Narrow" size:10.0];
CGFloat x = self.listFont.capHeight;
x = self.listFont.ascender;
x = self.listFont.descender;
self.lineHeight = self.listFont.boundingRectForFont.size.height;
self.entryHeight = [self.listFont capHeight] * 3;
self.headerHeight = 20 + self.entryHeight;
self.footerHeight = 20;
if (self.listFont) {
self.attributes = #{ NSFontAttributeName: self.listFont };
} else {
self.attributes = nil;
}
printJobTitle = #"My Print Job";
}
return self;
}
#pragma mark Pagination
- (BOOL)knowsPageRange:(NSRangePointer)range
{
NSPrintInfo *printInfo = [[NSPrintOperation currentOperation] printInfo];
self.pageRect = [printInfo imageablePageBounds];
NSRect newFrame;
newFrame.origin = NSZeroPoint;
newFrame.size = [printInfo paperSize];
[self setFrame:newFrame];
// Number of lines per page
self.linesPerPage = (self.pageRect.size.height - self.headerHeight - self.footerHeight) / self.entryHeight - 1;
// Number of full pages
NSUInteger noPages = self.tableToPrint.numberOfRows / self.linesPerPage;
// Rest of lines on last page
if (self.tableToPrint.numberOfRows % self.linesPerPage > 0) {
noPages++;
}
range->location = 1;
range->length = noPages;
return YES;
}
- (NSRect)rectForPage:(NSInteger)page
{
self.currentPage = (int)page - 1;
return self.pageRect;
}
- (NSAttributedString *)pageHeader
{
return [[NSAttributedString alloc] initWithString:self.header];
}
#pragma mark Drawing
- (BOOL)isFlipped
{
// Origin top left
return YES;
}
// We need this to find any transformers which are used to display the values of a certain column
static NSValueTransformer *TransformerFromInfoDict( NSDictionary *dict )
{
NSDictionary *options = dict[NSOptionsKey];
if (options == nil) return nil;
NSValueTransformer *transformer = options[NSValueTransformerBindingOption];
if (transformer == nil || (id)transformer == [NSNull null]) {
transformer = nil;
NSString *name = options[NSValueTransformerNameBindingOption];
if (name != nil && (id)name != [NSNull null]) {
transformer = [NSValueTransformer valueTransformerForName: name];
}
}
return transformer;
}
// This is where the drawing takes place
- (void)drawRect:(NSRect)dirtyRect
{
float margin = 20;
float leftMargin = self.pageRect.origin.x + margin;
float topMargin = self.pageRect.origin.y + self.headerHeight;
[NSBezierPath setDefaultLineWidth:0.25];
CGFloat originalWidth = 0;
for (NSTableColumn *col in self.tableToPrint.tableColumns) {
originalWidth += col.width;
}
CGFloat widthQuotient = (self.pageRect.size.width - margin) / originalWidth;
CGFloat inset = (self.entryHeight - self.lineHeight - 1.0)/2.0;
// Column titles
CGFloat horOffset = 0;
for (NSTableColumn *col in self.tableToPrint.tableColumns) {
NSRect rect = NSMakeRect(linkerRand + horOffset, topMargin, widthQuotient * spalte.width, self.entryHeight);
horOffset += widthQuotient * col.width;
[NSBezierPath strokeRect:rect];
NSString *theTitle = #"--";
if ([col respondsToSelector:#selector(title)]) { // OS X 10.10 and higher
theTitle = col.title;
} else {
NSTableHeaderCell *cell = col.headerCell;
theTitle = cell.title;
}
[theTitle drawInRect:NSInsetRect(rect, inset, inset) withAttributes:self.attributes];
}
NSUInteger firstEntryOfPage = self.currentPage * self.linesPerPage;
NSUInteger lastEntryOfPage = ((self.currentPage + 1) * self.linesPerPage) > self.tableToPrint.numberOfRows ? self.tableToPrint.numberOfRows : ((self.currentPage + 1) * self.linesPerPage);
for (NSUInteger i = 0; i < lastEntryOfPage - firstEntryOfPage; i++) {
#autoreleasepool { // to avoid memory hogging
NSUInteger row = firstEntryOfPage + i;
CGFloat horOffset = 0;
for (NSTableColumn *col in self.tableToPrint.tableColumns) {
NSDictionary *bindingInfo = [spalte infoForBinding: #"value"];
NSArray *columnValues = [bindingInfo[NSObservedObjectKey] valueForKeyPath: bindingInfo[NSObservedKeyPathKey]];
NSString *valueAsStr = #"";
id value = [columnValues objectAtIndex:col];
if ((value != nil) && (![value isKindOfClass:[NSNull class]])) {
// Do we have a transformer for that column? Then transform accordingly.
NSValueTransformer *transformer = TransformerFromInfoDict(bindingInfo);
if (transformer != nil)
value = [transformer transformedValue: value];
if ([value isKindOfClass:[NSString class]]) {
valueAsStr = value;
} else if ([value isKindOfClass:[NSNumber class]]) {
NSCell *cell = [col dataCellForRow:zeile];
if (cell.formatter != nil) {
valueAsStr = [cell.formatter stringForObjectValue:value];
} else {
valueAsStr = [value stringValue];
}
} else {
// We don't know what that is
NSLog(#"value class: %#", [value class]);
valueAsStr = #"????!";
}
}
NSRect rect = NSMakeRect(leftMargin + horOffset, topMargin + (i+1) * self.entryHeight, widthQuotient * col.width, self.entryHeight);
horOffset += widthQuotient * col.width;
// Now we can finally draw the entry
[NSBezierPath strokeRect:rect];
NSRect stringRect = NSInsetRect(rect, inset, inset);
[valueAsStr drawInRect:stringRect withAttributes:self.attributes];
}
}
}
}
#end
This is what you have to include in your document class in printDocumentWithSettings which gets called when the user selects "Print...":
[[self.printInfo dictionary] setValue:#YES forKey:NSPrintHeaderAndFooter];
NSString *headerLine = #"My first printed Table View";
MyPrintView *myPrintView = [[MyPrintView alloc] initWithTableView:theTableView andHeader:headerLine];
NSPrintOperation *op = [NSPrintOperation
printOperationWithView:myPrintView
printInfo:[self printInfo]];
[op setShowsPrintPanel:showPrintPanel];
// Run print operation, which shows the print panel if showPanels was YES
[self runModalPrintOperation:op
delegate:self
didRunSelector:nil
contextInfo:NULL];

Related

how can i display score in other scene?

I have to show score with SKLabel in gameOverScene. how can i show score in GameOverScene Label? I am tried, please help me.
My game scene codes here. You can see all details about score in down stair.
MyScene.m
#interface MyScene ()<SKPhysicsContactDelegate>
#property NSUInteger score;
#end
-(void)setupUI
{
self.score = 0;
SKLabelNode *scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
scoreLabel.fontColor = [SKColor redColor];
scoreLabel.fontSize = 20.0;
scoreLabel.text = #"SCORE: 0";
scoreLabel.name = #"scoreLabel";
scoreLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
scoreLabel.position = CGPointMake(self.size.width/2, self.size.height - scoreLabel.frame.size.height);
[self addChild:scoreLabel];
}
-(void)adjustScoreBy:(NSUInteger)points {
self.score += points;
SKLabelNode* score = (SKLabelNode*)[self childNodeWithName:#"scoreLabel"];
score.text = [NSString stringWithFormat:#"SCORE: %lu", (unsigned long)self.score];
}
- (void)gameOver
{
GameOverScene *gameOverScene = [GameOverScene sceneWithSize:self.size];
[self.view presentScene:gameOverScene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:0.5]];
}
GameOverScene.h
#interface GameOverScene : SKScene
#property NSUInteger *score;
#end
GameOverScene.m
#implementation GameOverScene
{
SKLabelNode *scoreLabel;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:1.5 green:1.0 blue:0.5 alpha:0.0];
[self addStartButton];
[self addRateButton];
[self addBackButton];
[self addScoreLabel];
}
return self;
}
-(void)addScoreLabel
{
scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
scoreLabel.text = [NSString stringWithFormat:#"SCORE: %lu", (unsigned long)self.score];
scoreLabel.position = CGPointMake(500, 50);
scoreLabel.name = #"gameOverScore";
[self addChild:scoreLabel];
}
There are several approaches to do this.
You could use a singleton class to handle that.
Other option would be to create a public score property in GameOverScene, and then pass the score value of MyScene to GameOverScene, something like this:
In GameOverScene.h add a score property
#interface GameOverScene : SKScene
#property NSUInteger score;
#end
Then in you gameOver method set the score value
- (void)gameOver
{
GameOverScene *gameOverScene = [GameOverScene sceneWithSize:self.size];
gameOverScene.score = self.score;
[self.view presentScene:gameOverScene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:0.5]];
}
In GameOverScene create didMoveToView
- (void)didMoveToView:(SKView *)view
{
[self addScoreLabel];
}
You just need a property in your next scene (target class) and call it from (void)gameOver (in the source class)
Add it int your target class like this
#property int score;
then use it like this in your source class
gameOverScene.score = self.score
You will use this kind of stuff a lot for when moving data around.

NSView drawRect not being called

This is my first time really using cocoa to make an application for mac. The only problem is that drawRect isn't being called at all. The real kicker is that it used to, but now it doesn't, and I have no idea what I did wrong. :/
I am setting 'setNeedsDisplay' but it refuses to work. As you can see, I have two debug prints in there, but only "DEBUG 1" is being called. Any ideas?
//ScrollingTextView.h:
//Adapted from http://stackoverflow.com/a/3233802/3438793
#import <Cocoa/Cocoa.h>
#interface ScrollingTextView : NSView {
NSTimer *scroller;
NSPoint point;
NSString *text;
NSString *tempText;
NSString *nextText;
CGFloat stringWidth;
NSInteger delay;
BOOL draw;
NSDictionary *fontDict;
NSFont *font;
BOOL isBigger;
}
#property (nonatomic, copy) NSString *text;
#property (nonatomic, copy) NSString *tempText;
#property (nonatomic, copy) NSString *nextText;
#property (nonatomic) NSInteger delay;
#property (nonatomic) BOOL draw;
#property (nonatomic) NSDictionary *fontDict;
#property (nonatomic) NSFont *font;
#property (nonatomic) BOOL isBigger;
#end
//ScrollingTextView.m
//Adapted from http://stackoverflow.com/a/3233802/3438793
#import "ScrollingTextView.h"
#implementation ScrollingTextView
#synthesize text;
#synthesize tempText;
#synthesize nextText;
#synthesize delay;
#synthesize draw;
#synthesize fontDict;
#synthesize font;
#synthesize isBigger;
- (void) setNextText:(NSString *)newText {
font = [NSFont fontWithName:#"Lucida Grande" size:15.0];
fontDict = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, [NSNumber numberWithFloat:1.0], NSBaselineOffsetAttributeName, nil];
if (text == nil) {
text = [newText copy];
} else {
nextText = [newText copy];
}
point = NSZeroPoint;
stringWidth = [newText sizeWithAttributes:fontDict].width;
if (stringWidth <= 163) {
NSString *size = [#"{" stringByAppendingString:[[NSString stringWithFormat:#"%f", stringWidth] stringByAppendingString:#", 22}"]];
[self setFrameSize:NSSizeFromString(size)];
isBigger = false;
} else {
isBigger = true;
}
if (text != nil) {
scroller = nil;
scroller = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(moveText:) userInfo:nil repeats:YES];
}
draw = false;
delay = 100;
}
- (void) moveText:(NSTimer *)timer {
point.x = point.x - 1.0f;
[self setNeedsDisplay:YES];
NSLog(#"DEBUG 1");
}
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
NSLog(#"DEBUG 2");
//NSLog([#"Next: " stringByAppendingString:nextText]);
//NSLog([#"Temp: " stringByAppendingString:tempText]);
//NSLog([#"Main: " stringByAppendingString:text]);
if (isBigger) {
if (draw) {
CGFloat pointX = dirtyRect.size.width + (-1*(dirtyRect.size.width - stringWidth)) + 30;
if (point.x < -1*pointX) {
point.x += pointX;
draw = false;
delay = 100;
text = tempText;
tempText = nil;
}
[text drawAtPoint:point withAttributes:fontDict];
if (point.x < 0) {
NSPoint otherPoint = point;
otherPoint.x += pointX;
if (tempText != nil) {
[tempText drawAtPoint:otherPoint withAttributes:fontDict];
} else {
[text drawAtPoint:otherPoint withAttributes:fontDict];
}
}
} else {
if (nextText != nil) {
tempText = nextText;
nextText = nil;
}
point.x = 0;
[text drawAtPoint:point withAttributes:fontDict];
if (delay <= 0) {
draw = true;
} else {
delay -= 1;
}
}
} else {
dirtyRect.size.width = stringWidth;
point.x = 0;
text = nextText;
[text drawAtPoint:point withAttributes:fontDict];
}
}
#end
EDIT: I added a line to "moveText" to see what "needsDisplay" was, and it turns out that "[self setNeedsDisplay:YES];" is doing nothing. Here is the line I added:
- (void) moveText:(NSTimer *)timer {
point.x = point.x - 1.0f;
[self setNeedsDisplay:YES];
NSLog([NSString stringWithFormat:#"%hhd", [self needsDisplay]]); //This one
}
It just prints 0

Getting a current value from UIProgressView, and triggering an event when a threshold is met

I have several UIProgressViews that read the voltage of an external device attached to the iOS device. The view scales it from 0-5 volts and I wanted to know if there was a way to retrieve the current progress/voltage of the UIProgressView and compare it to a threshold that is input by a user. Once this threshold is surpassed; a method I have written should be called.
The problem isn't necessarily the comparison part or calling the method. I am having trouble retrieving a live update of what the current value of the progressview is.
Thanks for your time.
EDIT FOR PART II:
So the problem is I did not code this and it is code pertaining to a certain product called the Nanospark board. This board is an input/output control board for factory systems that is run by various iOS devices.
I cannot quite seem to figure out what the previous coder did within this class:
//
// AnalogVC.m
// NanosparkController
//
#import "AnalogVC.h"
#import "AppDelegate.h"
#interface InputItem : NSObject
#property (weak,nonatomic) UISwitch *onSwitch;
#property (weak,nonatomic) UIProgressView *progressView;
#property (weak,nonatomic) UILabel *valueLabel;
#end
#implementation InputItem
AppDelegate *appDelegate;
+ (id)itemWithSwitch:(id)temp progress:(id)progress label:(id)label
{
InputItem *item = [[InputItem alloc] init];
item.onSwitch = temp;
item.progressView = progress;
item.valueLabel = label;
return item;
}
- (void)setDisconnected
{
self.onSwitch.on = NO;
self.onSwitch.enabled = NO;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
}
- (void)setOn
{
self.onSwitch.on = YES;
self.onSwitch.enabled = YES;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
[appDelegate watchPins:#"testing ON"];
}
- (void)setOff
{
self.onSwitch.on = NO;
self.onSwitch.enabled = YES;
self.valueLabel.text = #"0.000 v";
self.progressView.progress = 0;
[appDelegate watchPins:#"testing OFF"];
}
- (void)setValue:(double)value
{
if (self.onSwitch.on)
{
self.valueLabel.text = [NSString stringWithFormat:#"%0.3f v",value];
self.progressView.progress = value/5.0;
if(value > 0.8){
[appDelegate watchPins:#"testing VALUE"];
}
}
}
#end
#interface AnalogVC ()
{
NSArray *_inputItems;
AppDelegate *appDelegate;
NSMutableArray *channel0Values;
UIColor *custom1;
UIColor *custom2;
UIColor *custom3;
UIColor *custom4;
}
#property (nonatomic) NCBoardManager *manager;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch0;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch1;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch2;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch3;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch4;
#property (weak,nonatomic) IBOutlet UISwitch *inputSwitch5;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress0;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress1;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress2;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress3;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress4;
#property (weak,nonatomic) IBOutlet UIProgressView *inputProgress5;
#property (weak,nonatomic) IBOutlet UILabel *inputValue0;
#property (weak,nonatomic) IBOutlet UILabel *inputValue1;
#property (weak,nonatomic) IBOutlet UILabel *inputValue2;
#property (weak,nonatomic) IBOutlet UILabel *inputValue3;
#property (weak,nonatomic) IBOutlet UILabel *inputValue4;
#property (weak,nonatomic) IBOutlet UILabel *inputValue5;
#property (weak,nonatomic) IBOutlet UISlider *outputSlider0;
#property (weak,nonatomic) IBOutlet UISlider *outputSlider1;
#property (weak,nonatomic) IBOutlet UIStepper *outputStepper0;
#property (weak,nonatomic) IBOutlet UIStepper *outputStepper1;
#property (weak,nonatomic) IBOutlet UILabel *outputValue0;
#property (weak,nonatomic) IBOutlet UILabel *outputValue1;
- (IBAction)inputChannelChanged:(UISwitch *)sender;
- (IBAction)outputSliderMoved:(UISlider *)sender;
- (IBAction)outputSliderStopped:(UISlider *)sender;
- (IBAction)outputStepperChanged:(UIStepper *)sender;
#end
#implementation AnalogVC{}
//////////////////////////////
#pragma mark View Lifecycle
//////////////////////////////
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Analog VC loaded");
_inputItems = #[[InputItem itemWithSwitch:_inputSwitch0 progress:_inputProgress0 label:_inputValue0],
[InputItem itemWithSwitch:_inputSwitch1 progress:_inputProgress1 label:_inputValue1],
[InputItem itemWithSwitch:_inputSwitch2 progress:_inputProgress2 label:_inputValue2],
[InputItem itemWithSwitch:_inputSwitch3 progress:_inputProgress3 label:_inputValue3],
[InputItem itemWithSwitch:_inputSwitch4 progress:_inputProgress4 label:_inputValue4],
[InputItem itemWithSwitch:_inputSwitch5 progress:_inputProgress5 label:_inputValue5]];
_manager = [NCBoardManager sharedBoardManager];
__unsafe_unretained AnalogVC *vc = self;
[_manager setAnalogInputHandling:dispatch_get_main_queue()
filter:^(NCAnalogInputs *inputs){ return YES; }
handler:^(NCAnalogInputs *inputs){ [vc setAnalogInputs:inputs]; }];
// Register for notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boardConnected:)
name:CONNECTED_NOTIFICATION
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boardDisconnected:)
name:DISCONNECTED_NOTIFICATION
object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateAnalogInputs];
[self updateAnalogOutputs];
custom1 = [UIColor whiteColor];
custom2 = [UIColor darkGrayColor];
custom3 = [UIColor blackColor];
custom4 = [UIColor colorWithRed:.97 green:.97 blue:.588 alpha:1.0];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[custom2 CGColor], (id)[custom1 CGColor], (id)[custom2 CGColor], nil];
gradient.startPoint = CGPointMake(0.5, 0);
gradient.endPoint = CGPointMake(0.5, 1.0);
gradient.locations = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.5], [NSNumber numberWithFloat:1.0], nil];
[self.view.layer insertSublayer:gradient atIndex:0];
[self.inputSwitch0 setOnTintColor:custom4];
[self.inputSwitch1 setOnTintColor:custom4];
[self.inputSwitch2 setOnTintColor:custom4];
[self.inputSwitch3 setOnTintColor:custom4];
[self.inputSwitch4 setOnTintColor:custom4];
[self.inputSwitch5 setOnTintColor:custom4];
[self.inputSwitch0 setTintColor:custom3];
[self.inputSwitch1 setTintColor:custom3];
[self.inputSwitch2 setTintColor:custom3];
[self.inputSwitch3 setTintColor:custom3];
[self.inputSwitch4 setTintColor:custom3];
[self.inputSwitch5 setTintColor:custom3];
self.inputProgress0.trackTintColor = custom3;
self.inputProgress1.trackTintColor = custom3;
self.inputProgress2.trackTintColor = custom3;
self.inputProgress3.trackTintColor = custom3;
self.inputProgress4.trackTintColor = custom3;
self.inputProgress5.trackTintColor = custom3;
self.inputProgress0.progressTintColor = custom4;
self.inputProgress1.progressTintColor = custom4;
self.inputProgress2.progressTintColor = custom4;
self.inputProgress3.progressTintColor = custom4;
self.inputProgress4.progressTintColor = custom4;
self.inputProgress5.progressTintColor = custom4;
self.outputSlider0.minimumTrackTintColor = custom4;
self.outputSlider1.minimumTrackTintColor = custom4;
self.outputSlider0.maximumTrackTintColor = custom3;
self.outputSlider1.maximumTrackTintColor = custom3;
self.outputSlider0.thumbTintColor = custom3;
self.outputSlider1.thumbTintColor = custom3;
if(_manager.isBoardConnected)
{
self.outputStepper0.tintColor = custom4;
self.outputStepper1.tintColor = custom4;
self.outputStepper0.enabled = TRUE;
self.outputStepper1.enabled = TRUE;
self.outputSlider0.enabled = TRUE;
self.outputSlider1.enabled = TRUE;
}
else
{
self.outputStepper0.tintColor = custom2;
self.outputStepper1.tintColor = custom2;
self.outputStepper0.enabled = FALSE;
self.outputStepper1.enabled = FALSE;
self.outputSlider0.enabled = FALSE;
self.outputSlider1.enabled = FALSE;
}
}
//////////////////////////////
#pragma mark Rotation Calls
//////////////////////////////
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return FALSE;
}
//////////////////////////
#pragma mark Board Calls
//////////////////////////
- (void)boardConnected:(NSNotification *)notification
{
[self updateAnalogInputs];
[self updateAnalogOutputs];
self.outputStepper0.enabled = TRUE;
self.outputStepper1.enabled = TRUE;
self.outputSlider0.enabled = TRUE;
self.outputSlider1.enabled = TRUE;
self.outputStepper0.tintColor = custom4;
self.outputStepper1.tintColor = custom4;
}
- (void)boardDisconnected:(NSNotification *)notification
{
[self updateAnalogInputs];
[self updateAnalogOutputs];
self.outputStepper0.enabled = FALSE;
self.outputStepper1.enabled = FALSE;
self.outputSlider0.enabled = FALSE;
self.outputSlider1.enabled = FALSE;
self.outputStepper0.tintColor = custom2;
self.outputStepper1.tintColor = custom2;
}
- (void)updateAnalogInputs
{
uint8_t channel = self.manager.analogInputChannels;
switch (self.manager.analogInputStatus)
{
case NCInputConnected:
// Check if channels we left on
if (channel) self.manager.analogInputChannels = 0;
[_inputItems makeObjectsPerformSelector:#selector(setOff)];
break;
case NCInputDisconnected:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
case NCInputLiveUpdating:
for (InputItem *item in _inputItems)
{
//if (channel & 1) [item setOn];
//else [item setOff];
channel >>= 1;
}
break;
case NCInputSampling:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
case NCInputTransfering:
[_inputItems makeObjectsPerformSelector:#selector(setDisconnected)];
break;
}
}
- (void)setAnalogInputs:(NCAnalogInputs *)inputs
{
int i = 0;
uint8_t channels = inputs.channels;
for (InputItem *item in _inputItems)
{
if (channels & 1)
{
[item setValue:[inputs valueForChannel:i]];
}
channels >>= 1;
i++;
}
}
- (void)updateAnalogOutputs
{
BOOL connected = [self.manager isBoardConnected];
self.outputSlider0.value = self.manager.analogOutput0;
self.outputSlider0.enabled = connected;
self.outputStepper0.value = self.outputSlider0.value * 1000;
self.outputStepper0.enabled = connected;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",self.outputSlider0.value];
self.outputSlider1.value = self.manager.analogOutput1;
self.outputSlider1.enabled = connected;
self.outputStepper1.value = self.outputSlider1.value * 1000;
self.outputStepper1.enabled = connected;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",self.outputSlider1.value];
}
///////////////////////////////
#pragma mark IBAction Methods
///////////////////////////////
- (IBAction)inputChannelChanged:(UISwitch *)sender
{
NSLog(#"TEST");
InputItem *item = [_inputItems objectAtIndex:sender.tag];
uint8_t channels = self.manager.analogInputChannels;
if (sender.on)
{
channels |= (1 << sender.tag);
[item setOn];
}
else
{
channels &= ~(1 << sender.tag);
[item setOff];
}
if (!self.manager.analogInputChannels) [self.manager startAnalogLiveUpdating];
else if(!channels) [self.manager stopAnalogLiveUpdating];
self.manager.analogInputChannels = channels;
}
- (IBAction)outputSliderMoved:(UISlider *)sender
{
if (!sender.tag)
{
self.manager.analogOutput0 = sender.value;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",sender.value];
}
else
{
self.manager.analogOutput1 = sender.value;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",sender.value];
}
}
- (IBAction)outputSliderStopped:(UISlider *)sender
{
if (!sender.tag)
{
self.manager.analogOutput0 = sender.value;
self.outputStepper0.value = round(sender.value * 1000.0);
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",self.outputStepper0.value/1000.0];
}
else
{
self.manager.analogOutput1 = sender.value;
self.outputStepper1.value = round(sender.value * 1000.0);
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",self.outputStepper1.value/1000.0];
}
}
- (IBAction)outputStepperChanged:(UIStepper *)sender
{
float value = sender.value/1000.0;
if (!sender.tag)
{
self.manager.analogOutput0 = value;
self.outputSlider0.value = value;
self.outputValue0.text = [NSString stringWithFormat:#"%0.3f v",value];
}
else
{
self.manager.analogOutput1 = sender.value/1000.0;
self.outputSlider1.value = value;
self.outputValue1.text = [NSString stringWithFormat:#"%0.3f v",value];
}
}
#end
The problem I am having is I cannot figure out how to take values to and from the UIProgressViews that are on the storyboard (which is, like you said, a trivial concept). However with this set up it is rather convoluted.
Second problem is that; I am not sure if there is a way I can debug this as the application only runs when the external device (the nanospark controller board) is connected to the iPod.
The last but final problem I am having is that I am assuming the IBAction InputChannelChanged is being called (cannot debug this regularly with a breakpoint as aforementioned because it requires the external device to run the application), but when I run the application it does everything it should and the buttons react correctly to what the original software developer had coded.
This means that if I add my texting method to the IBAction (by adding [appDelegate watchPins#"TEST"]) it does not send the text to the user, but the buttons do still do what they should have done in concordance to the previous developers' aspirations.... this implies that the IBAction method is indeed being called... but then why isn't my text going through? I know that [appDelegate watchPins:#"TEST"]; should work as I have used it within several of his other classes.
Here is a screenshot displaying the UI of the AnalogVC.m:
http://i.stack.imgur.com/NNpZk.png
Do not feel obligated to answer all of these questions it's just I felt it necessary to provided all three for greater context of the problem. Thanks and sorry for the TL;DR.
EDIT 2: I'd upload the image but I do not have the required minimum reputation.
EDIT 3: I have tried to add another IBAction just to see if the buttons react to that; still nothing even if I copied the exact code from the other working classes.
Solution 1:
You need to call your method when you update the UIProgressView I assume that use are using the setProgress: animated: method somewhere in your code to update the progress view. If so, try this code in the method in which you update the UIProgressView:
float myVoltageFromDevice = self.deviceInput / 5.0; //this can be whatever your input is mapped to 0.0...1.0
//the self.device input variable should be the input from the external device.
if(myFloatFromDevice > myThreshold){ //set my threshold to whatever you would like.
[self doAction]; //do whatever you need to do when the value is surpassed
}else{
[myProgressView setProgress:myVoltageFromDevice animated: NO];
}
Edit:
I commented in the code above //this can be whatever your input is mapped to 0.0...1.0. Just in case this isn't clear, to achieve mapping you would do:
float myVoltageFromDevice = self.deviceInput / 5.0;
because the device input variable should be a value from 0-5 as you said in the OP. This makes the value from 0.0-1.0 which is the range of values that UIProgressView will accept.
Solution 2:
If can't pull off the above (which you really should be able to do), you should use Key Value Observing (KVO) which is detailed in this apple developer doc.
Edit 2:
The code you posted is quite complicated, but I believe that the method that you need to edit is - (void)setAnalogInputs:(NCAnalogInputs *)inputs try changing some of the code to this:
for (InputItem *item in _inputItems)
{
if (channels & 1)
{
if([inputs valueForChannel:i] > myThreshold){
[self doAction]; //Do your action here.
}else{
[item setValue:[inputs valueForChannel:i]];
}
}
channels >>= 1;
i++;
}

Strange "splitting" with custom UICollectionViewLayout upon resizing

I have a custom UICollectionViewLayout class that is exhibiting a weird problem. Screen shots above demonstrate. As the keyboard would obscure the lower fields is they were edited, I wanted to shorten the UICollectionView so that it would not be obscured by the keyboard when it came up. The problem is that I get the result in the right picture. The orange border is the background of the view hosting the UICollectionView, the red is the background color of the UICollectionView. The view with the orange background is the root view for the view controller and is resized with the following (self being the view controller).
CGRect frame = self.view.frame;
frame.size.height -= 300;
self.view.frame = frame;
It indicates that the view gets sized as desired, but for some reason the UICollectionView thinks it does not need to draw the cells in the lower portion. The place where it splits seem to be about consistent but arbitrary. If the original view is scrolled down it does not necessarily split at the section header.
In the layout class, if I return YES for - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds it draws properly but does full redraw so it looks like hell as it does that shifting/fadey thing. And resizing the view does not really invalidate the layout, there is no reason the same layout cannot be used.
Looking for any ideas as to what might be going on. The layout code follows:
//
// MyLayout.h
// uicontroller
//
// Created by Guy Umbright on 8/1/12.
// Copyright (c) 2012 Guy Umbright. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SFNativeLayout.h"
#import "SFLayoutView.h"
#class SFLayout;
#interface SFLayout : UICollectionViewLayout
#property (readonly, strong) SFNativeLayout* sfLayout;
#property (assign) BOOL deferRowsColsToCollectionView;
#property (strong) SFLayoutView* layoutView;
- (id) initWithDictionary:(NSDictionary*) dict;
- (NSInteger) numberOfSections;
- (NSInteger) numberOfItemsInSection:(NSInteger)section;
#end
//
// MyLayout.m
// uicontroller
//
// Created by Guy Umbright on 8/1/12.
// Copyright (c) 2012 Guy Umbright. All rights reserved.
//
#import "SFLayout.h"
#define ROW_HEIGHT 81 //79 with one pixel top bottom
#define HEADER_HEIGHT 30
#interface SFLayout ()
#property (strong) SFNativeLayout* sfLayout;
#property (nonatomic, strong) NSMutableArray* sectionMetrics;
#end
#implementation SFLayout
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (id) initWithDictionary:(NSDictionary*) dict
{
if (self = [super init])
{
self.sfLayout = [[SFNativeLayout alloc] initWithDictionary:dict];
}
return self;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSInteger) numberOfSections
{
if (self.deferRowsColsToCollectionView)
{
return [self.layoutView numberOfSections];
}
else
{
return self.sfLayout.sectionCount;
}
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (BOOL) sectionHasHeader:(NSInteger) section
{
BOOL result = YES;
if (self.deferRowsColsToCollectionView)
{
result = [self.layoutView sectionHasHeader:section];
}
return result;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSInteger) numberOfColumnsInSection:(NSInteger) section
{
if (self.deferRowsColsToCollectionView)
{
return [self.layoutView numberOfColumnsInSection:section];
}
else
{
SFNativeLayoutSection* layoutSection = [self.sfLayout.sections objectAtIndex:section];
NSInteger sectionColumns = layoutSection.columnCount;
return sectionColumns;
}
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSInteger) numberOfRowsInSection:(NSInteger) section
{
if (self.deferRowsColsToCollectionView)
{
return [self.layoutView numberOfRowsInSection:section];
}
else
{
SFNativeLayoutSection* layoutSection = [self.sfLayout.sections objectAtIndex:section];
return layoutSection.rowCount;
}
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (CGFloat) heightForSection:(NSInteger) sectionNdx
{
CGFloat height = 0;
if (self.deferRowsColsToCollectionView)
{
height = [self numberOfRowsInSection:sectionNdx] * ROW_HEIGHT;
if ([self sectionHasHeader:sectionNdx])
{
height += HEADER_HEIGHT;
}
}
else
{
SFNativeLayoutSection* section = [self.sfLayout.sections objectAtIndex:sectionNdx];
height += section.rowCount * ROW_HEIGHT;
if (section.includeHeader)
{
height += HEADER_HEIGHT;
}
}
return height;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (CGSize)collectionViewContentSize
{
BOOL fillSectionMetrics = NO;
CGFloat lastY = 0;
if (self.sectionMetrics == nil)
{
self.sectionMetrics = [NSMutableArray array];
fillSectionMetrics = YES;
}
CGSize sz = [self collectionView].frame.size;
CGFloat height = 0;
for (NSInteger ndx=0; ndx < [self numberOfSections]; ++ndx)
{
CGFloat sectionHeight = [self heightForSection:ndx];
height += sectionHeight;
if (fillSectionMetrics)
{
[self.sectionMetrics addObject:#{#"height":#(sectionHeight),#"startingY":#(lastY),#"endingY":#(lastY+sectionHeight)}];
lastY += sectionHeight;
}
}
sz.height = height;
return sz;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSArray*) attributesForSection:(NSInteger) sectionNdx inRect:(CGRect) rect
{
// NSLog(#"generate attrs for section %d", sectionNdx);
NSMutableArray* result = [NSMutableArray array];
CGRect intersect;
NSDictionary* sectionMetrics = [self.sectionMetrics objectAtIndex:sectionNdx];
SFNativeLayoutSection* layoutSection = [self.sfLayout.sections objectAtIndex:sectionNdx];
NSInteger columnCount = [self numberOfColumnsInSection:sectionNdx];
CGFloat rowStart = [[sectionMetrics valueForKey:#"startingY"] floatValue];
if ((self.layoutView.layoutDatasource != nil) || (layoutSection.includeHeader))
{
CGRect headerFrame = [self collectionView].bounds;
headerFrame.origin.y = rowStart;
headerFrame.size.height = HEADER_HEIGHT;
intersect = CGRectIntersection(rect, headerFrame);
if (!CGRectIsEmpty(intersect))
{
UICollectionViewLayoutAttributes* attr = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:#"Header"
withIndexPath:[NSIndexPath indexPathForItem:0 inSection:sectionNdx]];
attr.frame = headerFrame;
[result addObject:attr];
}
rowStart = headerFrame.origin.y + headerFrame.size.height;
}
for (int rowNdx = 0; rowNdx < [self numberOfRowsInSection:sectionNdx]; rowNdx++)
{
CGRect rowRect = [self collectionView].frame;
rowRect.size.height = ROW_HEIGHT;
rowRect.origin.y = rowStart + (rowNdx * ROW_HEIGHT);
intersect = CGRectIntersection(rect, rowRect);
if (!CGRectIsEmpty(intersect))
{
NSInteger columns = [self numberOfColumnsInSection:sectionNdx];
for (NSInteger colNdx =0; colNdx < columns; ++colNdx)
{
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:rowNdx * columnCount+colNdx inSection:sectionNdx];
CGRect frame;
frame.origin.y = rowRect.origin.y;
frame.size.height = ROW_HEIGHT;
frame.size.width = self.collectionView.frame.size.width/columnCount;
frame.origin.x = colNdx * frame.size.width;
UICollectionViewLayoutAttributes* attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.frame = frame;
[result addObject:attrs];
}
}
}
return result;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray* attributes = [NSMutableArray array];
for (NSDictionary* sectionMetric in self.sectionMetrics)
{
//can short circuit based on top of section and bottom of rect
CGRect sectionRect = [self collectionView].frame;
sectionRect.origin.y = [[sectionMetric valueForKey:#"startingY"] floatValue];
sectionRect.size.height = [[sectionMetric valueForKey:#"height"] floatValue];
CGRect intersect = CGRectIntersection(rect, sectionRect);
if (!CGRectIsEmpty(intersect))
{
NSArray* sectionAttrs = [self attributesForSection:[self.sectionMetrics indexOfObject:sectionMetric] inRect:intersect];
[attributes addObjectsFromArray:sectionAttrs];
}
}
return attributes;
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [super layoutAttributesForItemAtIndexPath:indexPath];
}
///////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////
- (NSInteger)numberOfItemsInSection:(NSInteger)section
{
SFNativeLayoutSection* layoutSection = [self.sfLayout.sections objectAtIndex:section];
NSInteger sectionColumns = [self numberOfColumnsInSection:section];
NSInteger sectionRows = layoutSection.rowCount; //%%%
return sectionColumns * sectionRows;
}
#end

Realtime Calculator

For a school assignment I have been told to make a calculator app, the same as the spotlight calculator. It works in realtime and has no buttons for things to begin.
So far this is my code. It is written in a text field with the event Editing Did End. Im pretty sure thats wrong but i can't find an alternative solution. Also i haven't gotten the realtime thing to work so i've kind of reverted to completing the following steps when pressed off the text field.
- (IBAction)Didend_Action:(id)sender {
NSString *list = [Sum_TextField text];
NSArray *listItemsArray = [list componentsSeparatedByString:#" "];
float firstNumber = [[listItemsArray objectAtIndex: 0] floatValue];
NSString *symbol = [listItemsArray objectAtIndex: 1];
float secondNumber = [[listItemsArray objectAtIndex: 2] floatValue];
{
Calculator* calc = [[Calculator alloc] init];
[calc setNum1:firstNumber];
[calc setNum2:secondNumber];
if ([symbol isEqualToString:#"-"])
{
[calc minus];
}
else if ([symbol isEqualToString:#"+"])
{
[calc add];
}
if ([symbol isEqualToString:#"*"])
{
[calc multiply];
}
else if ([symbol isEqualToString:#"/"])
{
[calc divide];
}
[Answer_TextField setText:[NSString stringWithFormat:#"%d", [calc answer]]];
}
}
I think a better way to do it would be to implement the UITextViewDelegate protocol methods like textViewDidChange:. For example, you could do something like this:
- (void)textViewDidChange:(UITextView *)textView {
NSString *currentText = [textview text];
NSArray *currentItems = [currentText componenetsSeparatedByString:#" "];
float result = 0.0;
//If a valid expression is in the text view
if([currentItems count] > 2) {
float num1 = [[currentItems objectAtIndex:0] floatValue];
float num2 = [[currentItems objectAtIndex:2] floatValue];
NSString *operator = [currentItems objectAtIndex:1];
if([operator isEqualToString:#"+"]) {
result = num1 + num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"-"]) {
result = num1 - num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"*"]) {
result = num1 * num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else if([operator isEqualToString:#"/"]) {
result = num1 / num2;
answerTextField.text = [NSString stringWithFormat:#"%f", result];
}
else{
answerTextField.text = #"Invalid Operation";
}
}
}
This would be called every time the user edited the text in the text view. It should work, but I didn't test it out. Make sure that in the header of whatever file this code is in, you do this:
#interface yourClassName : yourSuperclass <UITextViewDelegate> {
//Your instance variables
}
//Your method and property declarations
EDIT:
Let's say I put the - (void)textViewDidChange:(UITextView *)textView code in a file called MyClass.m. The file MyClass.m would then look like this:
#implementation MyClass
- (void)textViewDidChange:(UITextView *)textView {
//All the above code goes here
}
- (void)viewDidLoad
{
[super viewDidLoad];
//INCLUDE THESE LINES
Sum_TextField.delegate = self;
Answer_TextField.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
In the header file (MyClass.h), I would put this:
#interface MyClass : UIViewController <UITextViewDelegate>
//Note: you don't declare - (void)textViewDidChange:(UITextView *)textView in the header file because you are implementing
//a protocol method.
//MAKE SURE SUM_TEXTFIELD AND ANSWER_TEXTFIELD ARE UITEXTVIEWS NOT UITEXTFIELDS
#property (strong, nonatomic) IBOutlet UITextView *Sum_TextField;
#property (strong, nonatomic) IBOutlet UITextView *Answer_TextField;
#end
Hope this helps!