How to call a function when button from datasource clicked ? Objective-C - objective-c

My problem seems simple but i can't get the answer. I have a UiSwitch created in my TableItemCell subclass and I want him to call a function (dismiss in my case) from my tableviewcontroller that is instatiate.
How do I access this function from my subclass of TableItemCell ?
This is my code :
#implementation CCSettingsTableItemCell
#synthesize idSetting;
//Overriding cell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier {
if ((self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier])) {
switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
self.accessoryView = switchView;
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
}
return self;
}
- (void) switchChanged:(id)sender {
UISwitch* switchControl = sender;
/////////////////////////////////////////////////////////
//Here i want to call my function from my main controller
}
And my main controller code is as this :
#implementation SettingController
///////////////////////////////////////////////////////////////////////////////////////////////////
// private
// This is the function i want to call
- (void)dismiss {
[self dismissModalViewControllerAnimated:YES];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// NSObject
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
random code;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TTTableViewController
- (void)loadView {
random code;
}
- (void)createModel {
self.dataSource = [SettingControllerDataSource viewDataSource];
// If dataSource nil, show an empty Message
if (self.dataSource == nil) {
[self showEmpty:YES];
}
}
#end
Clarification : the datasource adds object of my customcell type CCSettingsTableItem
Any help or hints would be great !

You can use #protocol which declares your "dismiss" method which is implemented by your SettingController. Conform SettingController class to CCSettingsTableItemCell.
Otherwise create an object of SettingController in the .h file of CCSettingsTableItemCell. Use this object to call "dismiss" method.

Related

Implement Delegate Method on NSTextField

I am attempting to implement a delegate method on NSTextField as described in this article from Apple. My goal is for the NSTextField to accept carriage returns and tabs. I have read elsewhere (including the linked article) that NSTextView is a better choice. However, I am working within a multiplatform framework that lacks support for NSTextView, and NSTextField will do the job if I can get it to accept carriage returns.
Based on the article, here is my code:
#interface MyTextFieldSubclass : NSTextField
{}
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
#end
#implementation MyTextFieldSubclass
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end
Additionally, in the Identity Inspector of the text field, I have changed the class name from the default NSTextField to my class name. However, when I run my program, the delegate method never gets called. Is there something else I have to do to set this up in Interface Builder?
There are a few parts of the documentation you linked which is pertinent that I think may have been neglected.
I've copied a few of the lines below:
Should you decide to keep using NSTextField, allowing the tab key and/or allowing enter and return keys for line-breaks can be achieved by implementing the following delegate method:
(BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
Note: When implementing this delegate method in your own object you should set your object up as the "delegate" for this NSTextField.
I've bolded a few of the callouts which I think might have been missed.
This method is within the NSControlTextEditingDelegate protocol within NSControl.h. As such it should be implemented by a class which implements the NSControlTextEditingDelegate (i.e. NSTextFieldDelegate)
One common way of doing this is to have the ViewController "holding" the NSTextField be the NSTextFieldDelegate.
Here's a very simple example using the sample code from Apple you linked:
ViewController.h
#import <Cocoa/Cocoa.h>
#interface ViewController : NSViewController <NSTextFieldDelegate>
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end
Then set your NSTextField's delegate to the ViewController
No need to add a custom subclass.
Alternatively you could probably make the custom text field subclass its own delegate. Something along these lines:
#import "MyTextFieldSubclass.h"
#interface MyTextFieldSubclass() <NSTextFieldDelegate>
#end
#implementation MyTextFieldSubclass
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
self.delegate = self;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end

Opening a sheet on 10.9

In one of my controllers I'd like to open a modal sheet on 10.9.
This is the implementation code for the first controller:
#import "ABSAdvancedPreferencesViewController.h"
#import "ABSUnsyncableWindowController.h"
#interface ABSAdvancedPreferencesViewController ()
#property (strong, nonatomic) ABSUnsyncableWindowController *unsyncableWindowController;
#end
#implementation ABSAdvancedPreferencesViewController
- (id)init {
return [super initWithNibName:#"ABSAdvancedPreferencesViewController" bundle:nil];
}
- (IBAction)showUnsyncableSheet:(id)sender {
if (self.unsyncableWindowController == nil) {
self.unsyncableWindowController = [ABSUnsyncableWindowController new];
}
[self.view.window beginSheet:[self.unsyncableWindowController window] completionHandler:^(NSModalResponse returnCode) {
CLS_LOG(#"Table dismissed");
}];
}
When I execute the linked IBAction nothing happens. The NSWindowController subclass that should should show the modal has a XIB, Visible at launch is deactivated and window is already an outlet.
Debugging I saw that the window parameter is nil here, presumably because I call new in the previous controller:
#implementation ABSUnsyncableWindowController
- (id)initWithWindow:(NSWindow *)window {
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
Is there something else that I can check to show the modal sheet?
your ABSAdvancedPreferencesViewController initmethod is missing assigning the super result to self. Also, why don't you alloc/init the sheet controller as expected?
self.unsyncableWindowController = [[ABSUnsyncableWindowController alloc] initWithWindowNibName:#"XIBNAME"];
That should alloc the window controller and you should be able to access its window for display.

UIGestureRecognizer - Get the reference to the touched UIViewController Instead of its View?

How do I get a reference to the UIViewController of a touched view?
I am using a UIPanGestureRecognizer on the view of a UIViewController. Here's how I initialize it:
TaskUIViewController *thisTaskController = [[TaskUIViewController alloc]init];
[[self view]addSubview:[thisTaskController view]];
UIPanGestureRecognizer *panRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
[[thisTaskController view] addGestureRecognizer:panRec];
In the tiggered action triggered using the gesture recognizer I am able to get the view from the parameter using recognizer.view
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
UIView *touchedView = [[UIView alloc]init];
touchedView = (UIView*)[recognizer view];
...
}
However what I really need is the underlying UIViewController of the view touched. How can I get a reference to the UIViewController that contains this view instead of only the UIView?
I would say that it is more a design issue than just getting a reference. So I would follow several simple advises:
Owner should catch events from its view. I.e. TaskUIViewController sould be a target to UIPanGestureRecognizer which you added to its view.
If a controller has a sub-controller and waits from its sub-controller some responses - implement this as delegate.
You have memory leak in your "handlePan:" method.
Here is a skeleton to solve your issue:
#protocol CallbackFromMySubcontroller <NSObject>
- (void)calbackFromTaskUIViewControllerOnPanGesture:(UIViewController*)fromController;
#end
#interface OwnerController : UIViewController <CallbackFromMySubcontroller>
#end
#implementation OwnerController
- (id)init
{
...
TaskUIViewController *thisTaskController = [[TaskUIViewController alloc] init];
...
}
- (void)viewDidLoad
{
...
[self.view addSubview:thisTaskController.view];
...
}
- (void)calbackFromTaskUIViewControllerOnPanGesture:(UIViewController*)fromController
{
NSLog(#"Yahoo. I got an event from my subController's view");
}
#end
#interface TaskUIViewController : UIViewController {
id <CallbackFromMySubcontroller> delegate;
}
#end
#implementation TaskUIViewController
- (id)initWithOwner:(id<CallbackFromMySubcontroller>)owner
{
...
delegate = owner;
...
}
- (void)viewDidLoad
{
UIPanGestureRecognizer *panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:panRec];
[panRec release];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
...
[delegate calbackFromTaskUIViewControllerOnPanGesture:self];
...
}
#end
[touchedView nextResponder] will return the UIViewController object that manages touchedView (if it has one) or touchedView's superview (if it doesn’t have a UIViewController object that manages it).
For more information, see the UIResponder Class Reference. (UIViewController and UIView are subclasses of UIResponder.)
In your case, since you happen to know that touchedView is your viewController's view (and not, for instance, a subview of your viewController's view), you can just use:
TaskUIViewController *touchedController = (TaskUIViewController *)[touchedView nextResponder];
In the more general case, you could work up the responder chain until you find an object of kind UIViewController:
id aNextResponder = [touchedView nextResponder];
while (aNextResponder != nil)
{
if ([aNextResponder isKindOfClass:[UIViewController class]])
{
// we have found the viewController that manages touchedView,
// so we break out of the while loop:
break;
}
else
{
// we have yet to find the managing viewController,
// so we examine the next responder in the responder chain
aNextResponder = [aNextResponder nextResponder];
}
}
// outside the while loop. at this point aNextResponder points to
// touchedView's managing viewController (or nil if it doesn't have one).
UIViewController *eureka = (UIViewController *)aNextResponder;

Add and control UIImageView Subview from another class

I have two classes: MainViewController and PlayerImageController (NSObject)
How would I be able to add the subview of my UIImageView from PlayerImageController to my MainViewController and dictate actions like
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:[PlayerImageController addPlayerImage]];
}
- (void)somethingHappened
{
[PlayerImageController changePlayerImage];
}
and have my methods in my PlayerImageController class like
+ (UIImageView *) addPlayerImage
{
heroPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"hero-still.png"]];
[heroPlayerImageView setFrame:CGRectMake(151, 200, 17, 23)];
return heroPlayerImageView;
}
+ (void) changePlayerImage
{
//change image
}
Thanks!
I think in your case you should use the Delegate pattern.
Declare:
#protocol PlayerImageUpdater
- createPlayerImage;
- changePlayerImage;
#end
Then add:
#interface PlayerImageController <PlayerImageUpdater>
then add to MainViewController ivar and property like:
#property (...) id<PlayerImageUpdater> playerDelegate;
set this delegate like: mainViewController.playerDelegate = playerImageControllerInstance;
and use in code:
[playerDelegate createPlayerImage];
[playerDelegate changePlayerImage];
On one hand, I would not recommend using class methods but instance methods. This way, you could implement as many instances of your class as you need and keep a reference to your instances to update them.
On the other hand, if the UIImageView is the important attribute of your class, I suggest you implement it as a UIView subclass (if it is not, you can do it as an NSObject subclass as well, and get its UIImageView attribute).
Have a look at the following code:
PlayerImageController.h:
#interface PlayerImageController : UIView{
UIImageView *_heroPlayerImageView;
}
-(void) changePlayerImage;
PlayerImageController.m:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_heroPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"hero-still.png"]];
// x = 0 and y = 0 because its relative to its parent view, the object itself.
[_heroPlayerImageView setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[self addSubview:_heroPlayerImageView];
}
return self;
}
MainViewController.h:
#import "PlayerImageController.h"
#interface MainViewController : UIViewController{
PlayerImageController *_player;
}
MainViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
_player = [[PlayerImageController alloc] initWithFrame:CGRect(151, 200, 17, 23)];
[self.view addSubview:_player];
}
- (void)somethingHappened
{
[_player changePlayerImage];
}
I hope it can help you (I haven't actually tried the code above, it could have some syntax errors).
If you are not using ARC, remember to retain and release your variables! Good luck!

Obj-C: Delegate method not getting called

I'm new to iOS Dev, I'm following the Stanford CS193P classes for Fall 2010. I'm on assignment 3 and I'm setting my delegate to my view and by using the debugger I'm noticing the call to my delegate method won't happen, I don't understand what could be happening. My code is as follows:
GraphViewController.h:
#interface GraphViewController : UIViewController <GraphViewDelegate> {
GraphView *graphView;
float scale;
}
#property (retain) IBOutlet GraphView *graphView;
#property float scale;
- (IBAction)zoomIn;
- (IBAction)zoomOut;
#end
GraphViewController.m:
#implementation GraphViewController
#synthesize graphView, scale;
- (NSString *)functionForGraph:(GraphView *)requestor {
NSLog(#"%#", #"culo peluo");
return #"lol";
}
- (float)scaleForGraph:(GraphView *)requestor {
return self.scale;
}
- (IBAction)zoomIn {
}
- (IBAction)zoomOut {
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.graphView.delegate = self;
self.scale = 20;
[self.graphView setNeedsDisplay];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
GraphView.h:
#class GraphView;
#protocol GraphViewDelegate
- (NSString *)functionForGraph:(GraphView *)requestor;
- (float)scaleForGraph:(GraphView *)requestor;
#end
#interface GraphView : UIView {
id <GraphViewDelegate> delegate;
}
#property (assign) id <GraphViewDelegate> delegate;
#end
GraphView.m:
#implementation GraphView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGFloat cgScale = [self.delegate scaleForGraph:self];
[AxesDrawer drawAxesInRect:self.bounds originAtPoint:self.center scale:cgScale];
}
- (void)dealloc
{
[super dealloc];
}
#end
Put a break point on the line where you set the graph view's delegate.
Inspect the graphView variable. Is it nil?
This happens to me all the time and it's always (well nearly always) because I have failed to connect the outlet to the view in interface builder.
Let's say class YourClass is a delegate for some other class. And delegate methods are not called, despite that delegate property is set up.
Most probably the problem is that your class instance, that is a delegate for your other class is released before the delegate method is called on it. Make it more persistent by making this class a property or instance variable of other class or by using dispatch_once. For example,
Change
YourClass *instance = [[YourClass alloc] init];
by
#property(nonatomic, strong) YourClass *instance;
self.instance = [[YourClass alloc] init];
This problem occurs because in ARC everything that is created inside a method (and is not an instance variable) is released, when method finishes. And delegate methods cannot be called, that are invoked by some background process.
I've written a big blog post on this problem. This is pretty common situation.
http://alwawee.com/wordpress/2013/07/31/on-xmppframework-delegate-method-not-being-called/
Ive got a few pointers for ya on this one:
You don't need an iVar (internal Variable) if you have the variable as a property, it will still work, I've never know it not to.
GraphView
#interface GraphView : UIView {
}
#property (assign) id <GraphViewDelegate> delegate;
#end
GraphViewController
#interface GraphViewController : UIViewController <GraphViewDelegate> {
}
#property (retain) IBOutlet GraphView *graphView;
#property float scale;
- (IBAction)zoomIn;
- (IBAction)zoomOut;
#end
You need to set your delegate in your view to be able to use it.
GraphViewController
#implementation GraphViewController
( ... )
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = self;
}
return self;
}
( ... )
#end
You are not calling the delegate functions anywhere in your GraphView.m. Try testing it out by placing this piece of code in your - (id)initWithFrame:(CGRect)frame method, right before you return.
[self.delegate functionForGraph:nil];
and see if it logs any message to the console.
Of course this is just to test the delegate implementation, you should actually be using this delegate call in some other method in your GraphView.m which is able to process your requestor