Using protocol to trigger a segue from a UIView inside a UIViewcontroller - objective-c

CONFIGURATION:
-UIviewController embedded in Navigationcontroller.
-UIviewController has a UIscrollview as subview
-UIscrollview has some views where charts are created: each view containing a chart has its own .h and .m file and from this file I want trigger a segue to a tableview controller.
-A Tableviewcontroller was added in xcode and a segue from the UIviewController to the TableViewcontroller was created as well (Xcode)
-created a protocol in the UIView to have the segue pushed from there.
PROBLEM:
delegate always nil, segue method will never be called
UIVIEW .h file
#protocol UItoUIvcDelegate <NSObject>
-(void)triggerSegue;
#end
#interface CFfirstGraph : UIView <CPTPlotDataSource , CPTPieChartDelegate,UIActionSheetDelegate>
#property(weak, nonatomic) id <UItoUIvcDelegate> delegate;
#end
UIVIEW .m file (snippet)
-(void)pieChart:(CPTPieChart *)pieChart sliceWasSelectedAtRecordIndex:(NSUInteger)index
{
if (self.delegate == nil)
{
NSLog(#"nil");
}
[self.delegate triggerSegue];
}
UIVIEWCONTROLLER .h file
#import "CFfirstGraph.h"
#interface CFMainViewController : UIViewController <UItoUIvcDelegate, UITableViewDelegate, UITableViewDataSource>
#end
UIVIEWCONTROLLER .m file (snippet)
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
CFfirstGraph *click =[[CFfirstGraph alloc]init];
click.delegate = self ;
}
-(void)triggerSegue
{
[self performSegueWithIdentifier:#"detailedData" sender:self];
NSLog(#"estoy aqui");
}
What am I doing wrong ? why the delegate is always nil ? I tried to add the method setDelegate but still no luck.
Thanks,
dom

Make CFfirstGraph as a strong property.
#property (strong, nonatomic) CFfirstGraph * click;
- (void)viewDidLoad
{
[super viewDidLoad];
self.click =[[CFfirstGraph alloc]init];
self.click.delegate = self ;
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
}

ok, after many hours of sweat I found the issue.
First...delegate = nil was not the main problem.
The real issue was the protocol method triggering the segue was never called.
If i create and initialize a CFfirstGraph object (or even property) it won't be related to the view created already in x-code, and this is the main issue.
On the other hand...if I "CTRL-drag" an outlet from the UIview to the CFMainViewController i will have a property that is exactly the one i need:
#interface CFMainViewController () <UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet CFfirstGraph *FirstGraph;
Then i assign the delegate to self (CFMainViewController) in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.FirstGraph.delegate = self ;
}
and the delegate method "triggerSegue" will be executed being called from the UIVIEW.
Best Regards,
dom

Related

Obj-C / Using MapKitView and TableView together with delegation

I am a very beginner of objective c and I wanted to create a scene about using 2 child view controller in a master view controller which includes MapKit and TableView. I ve searched it on internet for 1 day and but I couldn't make any of solutions since I failed in different steps of each suggestions. I know that there are a lot of way to pass data between View Controllers and I thought the best way is using delegation logic in this situation. (let me know if I am wrong please). By the way, I can update or move the cursor onto specific location WHEN I set a dummy button on MapKitViewController, so MapKit part is not where I am failing at. I am pretty sure that the problem is about communication between 2 View Controllers which are active at the same time.
Problem:
Updating MapView by clicking a table row of TableView which includes location coordinate detail. Both children view are set on a MasterViewController by 2 container views.
Tried so far:
Created a method "goToLocation:(Location*) location" in MapViewController and sent parameters to this method from TableViewController. =>(Lat and Long parameters received by goToLocation() but MapKitView doesn't get updated)
Tried to create a delegate logic between MapView and TableView.=>(Probably I couldn't create it properly. Please see below)
What I want :
I want to know what part I am doing wrong. Is there a easy or proper way to achieve that ?
TableViewController.h
#class TableViewController; //define class
#protocol TableViewDelegate <NSObject> //define delegate protocol
//define delegate method to be implemented within another class
- (void) locationSelected: (TableViewController *) sender object:(Location *) location;
#end
#property (nonatomic, weak) id <TableViewDelegate> delegate; //define TableViewVCDelegate as delegate
TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self notifyNow:[_locations objectAtIndex:indexPath.row]];
}
- (void) notifyNow:(Location *) location {
//this will call the method implemented in your other class
[self.delegate locationSelected:self object:(Location *) location];
NSLog(#"Selected: %#",[location name]);
}
MapViewController.h
//make it a delegate for TableViewVCDelegate
#interface MapViewController : UIViewController <TableViewDelegate>
MapViewController.m
- (void)viewDidLoad {
_tableViewController = [[TableViewController alloc]init];
_tableViewController.delegate = self; //set its delegate to self somewhere
}
!!_ This Method Doesn't Get Triggered _!!
- (void)locationSelected:(TableViewController *)sender object:(Location *)location {
NSLog(#"%# is great!", [location name]);
}
MainViewController.m (Master/Root View Controller)
#import "MainViewController.h"
#import "MapViewController.h"
#import "TableViewController.h"
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet UIView *topCont;
#property (strong, nonatomic) IBOutlet UIView *bottomCont;
#property (strong, nonatomic) IBOutlet UIView *safeArea;
#end
#implementation MainViewController
MapViewController *mapViewVC;
TableViewController *tableViewVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES animated:YES];
tableViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableViewController"];
mapViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TopChildVC"];
[self addChildViewController:tableViewVC];
[tableViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.bottomCont addSubview:tableViewVC.view];
[tableViewVC didMoveToParentViewController:self];
[self addChildViewController:mapViewVC];
[mapViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.topCont addSubview:mapViewVC.view];
[mapViewVC didMoveToParentViewController:self];
}
#end

How can I send selectedSegmentIndex to containerView in Objective-c?

I have a containerView into my viewController. My viewController has a UISegmentedControl.
How can I send this selectedSegmentIndex selected at this moment and everytime when I change this?
I', trying with a property in the next class with prepareForSegue but this only send on first load..
Thanks!
Then Edit I'm getting: -[ViewController containerViewDidChangeSegmentIndex:]: unrecognized selector sent to instance 0x7f8db16286d0
FirstViewController (This contains A select and a containerView)
ViewController.h
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UISegmentedControl *select;
#property (weak, nonatomic) IBOutlet UIView *container;
#property(nonatomic, assign) ContainerViewController * classLevelReference;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[_select addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
}
- (void)segmentChanged:(UISegmentedControl *)segment{
//since you've reference to your container view here, you can directly call its method here:
[self.classLevelReference containerViewDidChangeSegmentIndex:segment.selectedSegmentIndex];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//don't forget to check the segue id, if you've multiple segues
ContainerViewController *containerView = [segue destinationViewController];
self.classLevelReference = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ContainerViewController.h
#interface ContainerViewController : UIViewController
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex;
#end
ContainerView.m
#implementation ContainerViewController
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex{
//Do whatever you want with your updated index
NSLog(#"changing");
}
#end
You might want to make a method in your ContainerViewController.h like this:
#interface ContainerViewController:UIViewController
//.....other implementation here.
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex;
#end
Now implement this method in your ContainerViewController.m like this:
- (void)containerViewDidChangeSegmentIndex:(NSInteger)updatedIndex{
//Do whatever you want with your updated index
}
Now in your prepare for segue, save the reference to your ContainerViewController in a class-level variable :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//don't forget to check the segue id, if you've multiple segues
ContainerViewController *containerView = [segue destinationViewController];
self.classLevelReference = containerView;
}
Lastly in the FirstViewController.m, tell the container view when segment index changes.
- (void)viewDidLoad{
//.....your other implementation here.....
//add a listener to your segment's value changed action
[youregements addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
}
- (void)segmentChanged:(UISegmentedControl *)segment{
//since you've reference to your container view here, you can directly call its method here:
[self.classLevelReference containerViewDidChangeSegmentIndex:segment.selectedSegmentIndex];
}
Important: you might've different names for the ContainerViewController and FirstViewController in your case, just apply the changes carefully.
Happy coding!

Cocoa app doesn't show textview

I'm an iOS developer and I want to create a simple desktop app. I thought the switch would go perfect but it doesn't.
I've created a cocoa app ( from the xCode template ). Now I don't want to use user interface builders and stuff so I wrote my first controller like this:
#interface MainViewController ()
#property (nonatomic, strong) NSTextView *test;
#end
#implementation MainViewController
-(instancetype) init
{
self = [super init];
if(self)
{
NSLog(#"%s", __PRETTY_FUNCTION__);
_test = [[NSTextView alloc] init];
[_test setString:#"DKDDK"];
[self.view addSubview:_test];
[_test mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
}
return self;
}
#interface MainViewController : NSViewController
#end
And I just use the NSWindow that is created by the template:
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
MainViewController * mainView = [[MainViewController alloc] init];
[self.window.contentView addSubview:mainView.view];
mainView.view.frame = ((NSView*)self.window.contentView).bounds;
}
When I run the application it gives me:
[NSViewController loadView] loaded the "(null)" nib but no view was set.
I don't know how to solve this. How can I create an app without nib, just like you do on iOS?
If you aren't loading the view from a NIB then there is little need for a view controller.
Discard the view controller and subclass NSView instead, and set that as the window's content view.
Note: you are making a rod for your own back by not using IB.

obj-c : Cant change subView of viewcontroller with Notification

I'm confused about below situation.
I have a viewcontroller(VC), it has 1 subview(SubV) and 1 other class.(classA)
Also i have an event handler called from classA, i want this event handler to change my subV in VC.
When i access SubV from VC directly, it is OK, image of subview changed etc.
But when the classA triggers an event handler of VC, it reaches VC, also access subView's method but no change in my subView !!! (I also try delegate but the result is same)
ViewController.h
#interface ViewController : UIViewController {
.
IBOutlet SubView *subView;
ClassA *classA;
.
}
#property (retain, nonatomic) IBOutlet SubView *subView;
#property (retain, nonatomic) ClassA *classA;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.subView = [self.subView init];
self.classA = [[ClassA alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(eventListener:) name:#"eventType" object:nil];
}
- (void) eventListener:(NSNotification *) not
{
[self.subView RefreshView]; // it doesnt work! calls refreshView method but no change
}
- (IBAction)buttonPressed:(id)sender
{
[self.subView RefreshView]; // it works perfect
}
SubView.h
#interface SubView : UIImageView
#property int state;
#property NSArray *imageArray;
- (void) RefreshView;
- (id) init;
#end
SubView.m
- (void) RefreshView{
[self stopAnimating];
self.imageArray = [NSArray arrayWithObjects:[UIImage imageNamed:#"a.png"], nil];
self.animationDuration = 1;
self.animationImages = self.imageArray;
self.animationRepeatCount = 0;
[self startAnimating];
}
ClassA.m
-(void)methodA{
[myEvent requestEvent];
}
So, what i am trying to do here is accessing & changing subView with a button in Viewcontroller and with a thread running in another classA
Edit: Not quite sure what to make of this. You've edited your post to the changes I proposed, making my answer look superfluous at best and deranged at worst...
You have created the class SubView as a subclass of UIImageView. But the IBOutlet *subView is not a member of class SubView but a member of UIImageView. I suspect this might carry over to the xib/storyboard as well(?) If so, this means that any messages you send to your subView instance will be handled by a stock UIImageView rather than your own class...
#property (retain, nonatomic) IBOutlet UIImageView *subView;
should probably read
#property (retain, nonatomic) IBOutlet SubView *subView;
Finally i've found the solution! Because of the thread that i run in ClassA, i should use '
[self performSelectorOnMainThread:#selector(myMethodToRefreshSubView) withObject:nil waitUntilDone:NO];
in my eventListener method.

mouseDown not firing properly on NSTextField

I tried implementing the second answer posted in this post here. I have the desire as the person asking the question however my mouseDown is not working/registering. Here is what I have.
AppDelegate.h
AppDelegate.m
MouseDownTextField.h
MouseDownTextField.m
and there relavent content:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "MouseDownTextField.h"
#interface AppDelegate : NSObject <MouseDownTextFieldDelegate> {
NSWindow *window;
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
NSMutableArray *selector;
NSMutableArray *display;
NSTimer *timer;
MouseDownTextField *quoteHolder; }
#property IBOutlet MouseDownTextField *quoteHolder;
#end
AppDelegate.m
- (void)displayString:(NSString *)title {
NSRect frame = NSMakeRect(50, 0, 200, 17);
quoteHolder = [[MouseDownTextField alloc] initWithFrame:frame];
[[self quoteHolder] setDelegate:self];
[quoteHolder setStringValue:title];
[quoteHolder setTextColor:[NSColor blueColor]];
[test addSubview:quoteHolder];
[statusItem setView:test]; }
-(void)mouseDownTextFieldClicked:(MouseDownTextField *)textField {
NSLog(#"Clicked");}
MouseDownTextField.h
#import <Appkit/Appkit.h>
#class MouseDownTextField;
#protocol MouseDownTextFieldDelegate <NSTextFieldDelegate>
-(void) mouseDownTextFieldClicked:(MouseDownTextField *)textField;
#end
#interface MouseDownTextField: NSTextField {
}
#property(assign) id<MouseDownTextFieldDelegate> delegate;
#end
MouseDownTextField.m
#import "MouseDownTextField.h"
#implementation MouseDownTextField
-(void)mouseDown:(NSEvent *)event {
[self.delegate mouseDownTextFieldClicked:self]; }
-(void)setDelegate:(id<MouseDownTextFieldDelegate>)delegate {
[super setDelegate:delegate]; }
-(id)delegate {
return [super delegate]; }
#end
Thoughts on what could be wrong or what i have done wrong?
You are creating quoteHolder in IB, you should remove the following line of code and you should be fine.
quoteHolder = [[MouseDownTextField alloc] initWithFrame:frame];
The result of reassigning the NSTextField is that the one you are clicking is no longer the one registered with the delegate. No need to add it as a subview either, it's already been added to the view hierarchy in IB.
Also, make sure in IB, under Accessibility, "User Interaction Enabled" is checked for the NSTextField.
As for the follow up quesion, how could you have multiple of these?
If you were adding multiple NSTextField instances in IB, each would be referenced as a #property just as you did with quoteHolder. The linkage is done in IB like this linked answer.
These could all have the same delegate. When mouseDownTextFieldClicked: is pressed you could interrogate the NSTextField for a unique id which could be assigned in IB as well. Hope this helps.