pass array from NSViewController to NSView in Objective C Cocoa - objective-c

Im making a program to draw the chart (which i keep in NSView class). However action & data I want to pass from NSViewController. So could you help me how to do it. I did try as bellow code however it doesn't work.
#implementation PlottingChart
#synthesize plotChartData;
- (void)drawRect:(NSRect)dirtyRect{
[super drawRect:dirtyRect];
[self drawChartGrid:plotChartData];
}
-(void)drawChartGrid:(NSMutableArray *)ChartData
{
//Drawing code here
}
#interface PlottingChart : NSView
#property (nonatomic, strong) NSMutableArray *plotChartData;
-(void)drawChartGrid:(NSMutableArray *)ChartData;
#end
#import "PlottingChart.h"
#interface ViewController :NSViewController<NSTableViewDataSource,NSTableViewDelegate>
{
PlottingChart *boxPlotChart;
}
- (IBAction)btnStart:(id)sender {
//trial draw chart
NSDictionary *dict1 = #{#"plot_Q1":#"180",#"plot_Q3": #"220", #"plot_Max":#"250", #"plot_Min":#"150", #"plot_Median":#"200"};
NSDictionary *dict2 = #{#"plot_Q1":#"190",#"plot_Q3": #"230", #"plot_Max":#"280", #"plot_Min":#"160", #"plot_Median":#"210"};
NSMutableArray *array = [NSMutableArray arrayWithObjects:dict1,dict2, nil];
boxPlotChart.plotChartData = array;
[boxPlotChart drawChartGrid:array];
boxPlotChart = [[PlottingChart alloc] initWithFrame:NSMakeRect(10, -50, 850, 360) ]; // x,y lenght,height
[self.view addSubview:boxPlotChart];
}

Finally I found the solution.
Add initWithFrame method into NSView.
- (id)initWithFrame:(NSRect)frame dataArray:(NSMutableArray *)dtArray;
{
self = [super initWithFrame:frame];
self.plotChartData = dtArray;
return self;
}
And initial this one in NSViewController.
boxPlotChart = [[PlottingChart alloc] initWithFrame:NSMakeRect(10, -50, 850, 360) dataArray:array ];

Related

What are the best practices for refactoring methods in Objective-C

How can I get this kind of method refactored better?
This is just a sample in my objective-c project, and I am trying to do it all programmatically
Im not sure what the best practices would be, create a protocol? an extension? or refactor further down to additional methods?
- (void)viewDidLoad {
[super viewDidLoad];
// Label to be changed
label = [[UILabel alloc]init];
label.text = #"Changed with code!";
[self.view addSubview:label];
// Label Constraints
label.translatesAutoresizingMaskIntoConstraints = NO;
[label.topAnchor constraintEqualToAnchor: self.view.safeAreaLayoutGuide.topAnchor constant:50].active = YES;
[label.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:20].active = YES;
[label.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor constant:-20].active = YES;
}
As an example you could create a UIView subclass that creates the label and makes the layout.
#interface MyView: UIView
#property (nonatomic, strong, readonly) UILabel *label;
#end
#interface ViewController: UIViewController
#property (nonatomic, strong, readonly) MyView *myView;
#end
#implementation MyView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_label = [[UILabel alloc] initWithFrame:CGRectZero];
_label.translatesAutoresizingMaskIntoConstraints = NO;
UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:#[
[label.topAnchor constraintEqualToAnchor: safeAreaLayoutGuide.topAnchor constant:50],
[label.leadingAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leadingAnchor constant:20],
[label.trailingAnchor constraintEqualToAnchor:safeAreaLayoutGuide.trailingAnchor constant:-20]
]];
}
return self;
}
#end
#implementation MyViewController
- (void)loadView {
self.view = [[MyView alloc] initWithFrame:CGRectZero];
}
- (MyView *)myView {
return (id)self.view; // it makes view to load regardless which
// property do you use – `view` or `myView`
}
- (void)viewDidLoad {
[super viewDidLoad];
self.myView.label.text = #"Changed with code!";
}
#end

How to set/get UIImageView.image through Singleton?

I have two ViewControllers.
FirstViewController has UILabels and UIImageViews. Also generated a Singleton from its class.
SecondViewController has a call of that Singleton and should GET values of UILabels and UIImageViews from FirstViewController.
NSString works, UILabel do not.
For example, access a NSString in SecondviewController with "sharedFirstViewController.nsstringvarname" I can GET and SET the value.
This works fine!
Trying the same with an UILabel like this: "sharedFirstViewController.uilabelvar.text" I doens´t can SET or GET the value of this UILable.
This works NOT!
Is there any special thing with UI-Objects?
Would be great if someone can help me with that.
Thanks!
EDIT:
in FirstViewController.h
+ (id)sharedFirstViewController;
#property (copy, nonatomic) NSString *path;
#property (strong, nonatomic) IBOutlet UIImageView *pic;
in FirstViewController.m
#synthesize path= _path;
#synthesize pic = _pic;
- (id)init {
if (self = [super init]) {
_pic = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)];
}
return self;
}
+ (id) sharedFirstViewController {
static sharedFirstViewController * sharedFirstViewController = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFirstViewController = [[self alloc] init];
});
return sharedFirstViewController;
}
in SecondViewController.m
#import "FirstViewController.h"
- (void)viewDidLoad
{
FirstViewController * sharedFirstViewController = [FirstViewController sharedFirstViewController];
NSLog(#"this is NSString from FVC: %#" sharedFirstViewController.path); //works
UIImage * picture = [UIImage imageWithContentsOfFile:sharedFirstViewController.path];
sharedFirstViewController.pic.image = picture; // does´t work
}
I'm sure you forget to give allocation to UIImageview *pic in init method of singleton class. That because your code running but without any crash and warning. If you debug your code you will get imageview memory allocation 0X0. check it.
- (id)init {
if (self = [super init]) {
_pic = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 200)];
}
return self;
}
write above method in in FirstViewController.m class. Their is doesn't need of write IBOutlet in front of UIImageView *pic because you not mention your viewcontroller nib while generation singleton instance. Use IBOutlet whenever you dealing UI in xib.

Calling SetNeedsDisplay from other class

Today I'm facing a new problem:
I have a ViewController (Subclass of NSView) and another class (Subclass of NSObject) which, through an IBAction, try to call back the viewController to redraw its view using SetNeedsDisplay:YES.
The method to redraw the view (ViewController.m) is:
- (void) redrawView {
[self setNeedsDisplay:YES]
}
// With an NSLog i see that the method is called !
// Instead of self i tried even an outlet connected to the custom view.
}
What I'm doing to call the ViewController method through my other class is:
1) #import "ViewController.h"
2) into the IBAction i made a new istance of ViewController as:
ViewController *newIstanceOfViewController = [[ViewController alloc] init]
3) [newIstanceOfViewController redrawView]
The log show me that setNeedsDisplay is called but drawrect no ! Why ? I have forgotten to init or subview something ? Thanks
THE ORIGINAL CODE HERE (DIFFERENT NAME OF METHODS DUE TO LANGUAGE, SAME SYNTAX)
// Controller.h
#import <Cocoa/Cocoa.h>
#interface Controller : NSView {
IBOutlet NSView *tastierinoView;
}
- (void) aggiornaTastierinoView;
#end
// Controller.m
#import "Controller.h"
#implementation Controller
//Contatore numero volte disegno view
int contatore = 0;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
tastierinoView = [[NSView alloc] init];
}
return self;
}
- (void) aggiornaTastierinoView { //THIS IS THE METHOD TO CALL REDRAW
[tastierinoView setNeedsDisplay:YES];
NSLog(#"Chiamo setneedsDisplay\n\n");
}
- (void)drawRect:(NSRect)dirtyRect
{
contatore++;
NSLog(#"La view è stata disegnata %d volte\n\n",contatore);
[super drawRect:dirtyRect];
// Drawing code here.
NSBezierPath* percorso = [NSBezierPath bezierPath];
[[NSColor cyanColor] set];
[percorso moveToPoint:NSMakePoint(150, 150)];
[percorso lineToPoint:NSMakePoint(250, 150)];
[percorso setLineWidth:5.0];
[percorso stroke];
}
#end
// ManipolatorePin.h
#import <Foundation/Foundation.h>
#interface ManipolatorePin : NSObject {
IBOutlet NSWindow *finestraPrincipaleWindow;
IBOutlet NSTextField *codiceTextField;
NSArray *coordinate_x;
NSArray *coordinate_y;
//L'array sotto riportato serve, tramite un ciclo for, a salvare il codice pin spezzato in singoli numeri che corrisponderanno al punto.
NSMutableArray *numeroAllaIndexArray;
}
- (IBAction)aggiornaTastierino:(id)sender;
#end
// ManipolatorePin.m
#import "ManipolatorePin.h"
#import "Controller.h"
#implementation ManipolatorePin
- (void)awakeFromNib {
coordinate_x = [[NSArray alloc] initWithObjects:#"150",#"50",#"150",#"250",#"50",#"150",#"250",#"50",#"150",#"250", nil];
coordinate_y = [[NSArray alloc] initWithObjects:#"50",#"150",#"150",#"150",#"250",#"250",#"250",#"350",#"350",#"350", nil];
numeroAllaIndexArray = [[NSMutableArray alloc] init];
NSLog(#"Array coordinate iniziallizato.\nTest:(%#) -> deve risultare \"50\"\n\n",[coordinate_x objectAtIndex:4]);
}
- (IBAction)aggiornaTastierino:(id)sender {
NSString *codiceString = [[NSString alloc] initWithFormat:[NSString stringWithFormat:#"%#",[codiceTextField stringValue]]];
NSLog(#"codiceTextField = %#", codiceString);
int lunghezzaCodiceString;
NSLog(#"Il codice risulta essere composto da (%d) numeri\n\n",[codiceString length]);
//Svuoto array
[numeroAllaIndexArray removeAllObjects];
for (lunghezzaCodiceString = 0; lunghezzaCodiceString < [codiceString length]; lunghezzaCodiceString++) {
//Compilo array (Ci provo ahah)
NSString *carattereDelCodiceStringInEsame = [[NSString alloc] init];
carattereDelCodiceStringInEsame = [codiceString substringWithRange:NSMakeRange(lunghezzaCodiceString,1)];
NSLog(#"Aggiungo il numero (%#) all'array 'numeroAllaIndexArray'",carattereDelCodiceStringInEsame);
[numeroAllaIndexArray addObject:carattereDelCodiceStringInEsame];
}
//DEBUG - DA QUI IN POI E' CANCELLABILE
NSLog(#"\n\nCiclo for termitato\nProcesso concluso con successo\n\n\nContenuto array:");
int conteggioArray;
for (conteggioArray = 0; conteggioArray < [numeroAllaIndexArray count] ; conteggioArray++ ) {
NSLog(#"index (%d) -> (%#)",conteggioArray,[numeroAllaIndexArray objectAtIndex:conteggioArray]);
NSLog(#"\n\n");
}
//FINE DEBUG
///////////////HERE THE INSTANCE TO CALL THE CONTROLLER
Controller *istanzaGestionaleController = [[Controller alloc] init];
[istanzaGestionaleController aggiornaTastierinoView];
}
#end
It sounds like you have two unrelated instances of ViewController. You need to call redrawView on the instance that actually has a view on the screen.
In your class (which is not the NSViewController), create a property like this:
#property (nonatomic, readwrite, weak) NSViewController *vc;
When the class is instantiated:
YourClass *yourInstance = [[YourClass alloc] init];
assign the value of vc:
yourInstance.vc = self; // self is your NSViewController instance
Then when you want to trigger a redraw, you pass the command to self.vc inside your other class.

accessing method of custom view in view controller

I am a new-be in Objective C. Here I want to ask a simple question.
I have created a view controller in storyboard, and customized its view with a subclass of UIView.
However, I don't know how to call methods of the view in my view controller. Can anyone help? All I want to do is to call drawLine:pointStore in ChartViewController.m from chartView.m
Here are some of my codes.
ChartViewController.h
#import <UIKit/UIKit.h>
#class chartView;
#interface ChartViewController : UIViewController{
chartView *chart_view;
}
ChartViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *pointStore = [[NSMutableDictionary alloc]init];
NSNumber *initX;
NSNumber *initY;
NSMutableDictionary *variableSet = [[NSMutableDictionary alloc]init];
for(initX = [NSNumber numberWithDouble:-10.0f];initX.floatValue<=10.0f;initX = [NSNumber numberWithDouble:(initX.floatValue+0.5f)] )
{
[variableSet setValue:initX forKey:#"x"];
initY = [NSNumber numberWithDouble:[self.brain performOperation:equationOfChart withVariable:variableSet]];
[pointStore setObject:initX forKey:initY];
}
[chart_view drawLine:pointStore];
}
chartView.h
#interface chartView : UIView
#property (nonatomic, strong) NSString *equation;
-(void) getEquation:(NSString *)Equation;
-(void) drawLine:(NSMutableDictionary *)pointsStore;
#end
chartView.m
-(void)drawLine:(NSMutableDictionary *)pointsStore{
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextStrokePath(c);
for(NSNumber *ax = [NSNumber numberWithDouble:-10.0f];ax.floatValue<10.0f;){
float ay = [[pointsStore objectForKey:ax]doubleValue];
ax = [NSNumber numberWithDouble:(ax.floatValue+0.5f)];
int by = [[pointsStore objectForKey:ax]doubleValue];
[self.class line:ax.floatValue y:ay x2:(ax.floatValue+0.5) y2:by];
}
}
ChartViewController.h
#import <UIKit/UIKit.h>
#class chartView;
#interface ChartViewController : UIViewController
#property (string, nonatomic) chartView *chart_view;
#end
chartView.m
- (void)viewDidLoad
{
[super viewDidLoad];
...
// Don't forget to alloc and init
self.chart_view = [[chartView alloc] init];
[self.chart_view drawLine:pointStore];
}
You should check that the instance of chart_view is created and assigned. Probably you have nil in that poiner.
How to do this -- depend on the way you have chosen to create the view: in Interface Builder or dynamically, in code.

Issue with multiple instances of same UIView object

I've got a problem at programming for iOS. Already looked for some similar problems but haven't found anything yet.
I'm creating at least 8 custom UIViews. As you can see in the appended code i'm running through a loop creating one instance per round. The reference of every object is on a different space in the memory but when i change a value in one object it only affects the object that has been created in the last loop-round (last created instance).
Any Ideas?
PadView.h:
#import <UIKit/UIKit.h>
#interface PadView : UIView {
}
- (void)setText:(NSString*)text;
#end
PadView.m:
#import "PadView.h"
#import "AVFoundation/AVFoundation.h";
#implementation PadView
AVAudioPlayer *player;
UILabel *label;
- (void)setText:(NSString*)text {
label.text = text;
}
- (void)initialize {
label = [[ UILabel alloc ] initWithFrame:CGRectMake(0.0, 93.0, 107.0, 13.0)];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:9];
label.textAlignment = UITextAlignmentCenter;
label.text = #"Empty";
[self addSubview:label];
[label release];
}
- (id) initWithCoder:(NSCoder *)aCoder {
if (self = [super initWithCoder:aCoder]) {
[self initialize];
}
return self;
}
- (id) initWithFrame:(CGRect)rect {
if (self = [super initWithFrame:rect]) {
[self initialize];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
Create the Objects:
NSMutableArray *pads = [[NSMutableArray alloc] initWithCapacity:8];
for (int i = 0; i < 8; i++) {
PadView *pad = [[PadView alloc] initWithFrame:CGRectMake(0.0, i*150, 107.0, 107.0)];
[padsView addSubview:pad];
[pads addObject:pad];
}
Call setText:
PadView *pad = [pads objectAtIndex:5];
[pad setText:#"test"];
Your variables:
AVAudioPlayer *player;
UILabel *label;
are defined in the #implementation block, so they are effectively global variables (in the C sense).
So basically, all your instances of PadView will change the same UILabel when you set its text property (which explains the behavior you are seeing).
I'm lacking some context, but it seems that you want label to be an instance variable instead (and I'd assume player as well). If that's the case, you need to declare them in the #interface block as follows:
#interface PadView : UIView {
AVAudioPlayer *player;
UILabel *label;
}
- (void)setText:(NSString*)text;
#end
pads is not initialized
NSMutableArray *pads = [[NSMutableArray alloc] initWithCapacity:8];
and you must release pad after adding as subview and to the array
By convention the class should be named PadView, not padView
edit
for(padView *pad in pads){
//manipulate each pad
}
//manipulate a certain pad
padView *pad = [pads objectAtIndex:5];
pad. //...