I am having problems updating a view when a message from another class is sent to a ViewController.
Basically I have an application with a single window where different custom views will be swapped out for another. I have an AppController Class that manages this and works fine:
#interface AppController : NSObject
#property (weak) IBOutlet NSView *ourView;
#property (strong) NSViewController *ourViewController;
- (IBAction)changeView:(id)sender;
- (IBAction)start:(id)sender;
- (void)changeViewContoller:(NSInteger)tag;
#end
When a new view is swapped out for another, the ourViewController property will be updated to point to that view's controller class. Every view controller class will have a method all named the same thing, for example "action". This method is supposed to change something on a view.
So the "start" method in AppController class will then call the "action" method on the ourViewController property. To do this I used the objc_msgSend() method:
objc_msgSend(self.ourViewController, action);
Here's the View Controller class definition:
#interface CountdownViewController : NSViewController
#property (weak) IBOutlet NSTextField *label;
- (IBAction)changeLabel:(id)sender;
- (void)start;
#end
I placed an NSLog() in the "action" method for each ViewController, to see if it was working, and it does, however the "action" method is also supposed to change a label's string value, but it does not. If anyone knows why the view is not being updated, that would be extremely helpful. Thanks!
the view is held weak?
TRY making it strong if you need to retain that pointer in this class
btw: ..also why do you objc_msgsend.... use performSelector
Related
I was messing around in Xcode creating... something... when I needed to send a message to the MainViewController.m from the FlipsideViewController.m. Except, when I do, the ViewController I am sending the message from does not detect the method as existing, despite the fact that I put the method in MainViewController.h. This is the method:
- (void)setAutosave:(BOOL)boolee {
_autosave = boolee;
}
I have imported the MainViewController into the FlipsideViewController, but as I call it ([NSMainViewController setAutosave:_autosave];), it simply throws an error:
No known class method for selector 'setAutosave:'
I am doing this because FlipsideViewController has a segmented control in it, which tells autosave to be on or off, but it needs to send it's value to the other ViewController.
I really am stumped, help is appreciated.
MainViewController is a class, you can't access instance method from a class.
You can add a property to FlipsideViewController, for example:
#property (weak, nonatomic) MainViewController *mainViewController;
Assign your MainViewController instance to mainViewController (how to assign depends on the relationship between your two view controllers), then you can invoke that method by [self.mainViewController setAutosave:_autosave];.
I have subclassed a UIScrollView to customize it a bit. I am trying to create a delegate that will notify several other classes that a user has done a certain thing in the UIScrollView. In my UIScrollView class I have the code below. The problem I am running into is I am getting the warning
Property 'delegate' 'retain (or strong)' attribute does not match the
property inherited from 'UIScrollView'
I see that this is because my Class in inheriting from UIScrollView, but my delegate is conforming to the NSObject. This is the first time I tried creating my own delegate. What can I do to fix this?
My Code:
#import <UIKit/UIKit.h>
#protocol ChangeSpaceDelegate <NSObject>
- (void)changeSpace:(int)spaceId;
#end
#interface CustomUIScrollView : UIScrollView {
id<ChangeSpaceDelegate> delegate;
}
#property (retain, nonatomic)id delegate;
#end
To answer your question specifically, you are redefining the property attribute on the delegate property you get from UIScrollView. It should, like all delegates, be weak (or, pre-iOS 5, unsafe_unretained).
However, you shouldn't do it this way. UIScrollView already has a delegate, and if you expect to put your own delegate object implementing your new delegate methods into it, the inner workings of UIScrollView aren't going to be happy. Define a new protocol and a new delegate property for it.
#property (weak, nonatomic) id<ChangeSpaceDelegate> changeSpaceDelegate;
You don't have to create the delegate object in custom scrollview class since you are subclassing it from UIScrollView. You can directly use it as self.delegate in your custom scrollview class.
As mentioned by #Steve Madsen, I often add own delegate properties for subclasses. Like UITableView has separate DataSource and Delegate properties, and being assigned with the same object. In a long run, this will definitely pay off by not forcing you to repeat what have been already implemented in super class, and keeping your subclass implementations more manageable
I'm a beginner in objective-c and want to realize a simple GUI: A MyMainWindowController with a corresponding xib wich contains a table and a simple Add button.
#interface MyMainWindowController : NSWindowController
{
}
#end
The implementation code of the controller is nearly empty (pre-defined initWithWindow and windowDidLoad). The AddressTvc is defined like this:
#interface AddressTvc : NSObject <NSTableViewDataSource>
{
#private
IBOutlet NSTableView *myTableView;
NSMutableArray *list;
}
- (IBAction)add:(id)sender;
#end
This works fine. I can click on the Add button and a new row is inserted in the table.
It seems that the AddressTvc is created automatically (by the IB?) when the MyMainWindowController is visible. I want a reference in the code of MyMainWindowController to AddressTvc so I'm able to fill the table with some data retrieved by a background thread. This should be done by calling the - (IBAction)add:(id)sender; method.
I tried to create a AddressTvc inside the MyMainWindowController but then the object is initialized twice. I'm sure I have to wire it somewhere in the IB, but have no clue where to do this ...
Create an outlet in you MyMainWindowController class that can point to your table view controller class.
E.G.
#interface MyMainWindowController : NSWindowController
{
IBOutlet AddressTvc *myTVC
}
#end
Then simply control+drag from your file owner to the instance of the table view controller in interface builder and you can now access it in your window controllers code.
In my project, there are two view controllers - let's say firstViewController and secondViewController. The second view controller has a button, and I want to make sure when the button gets pressed, the second view controller is telling somehow the first view controller - "hey, I got pressed, do something!", and it will do something, like changing a label. How is this possible to perform? Thanks in advance. Some code :
#interface firstViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
#implementation firstViewController
#synthesize textLabel;
#end
#interface secondViewController : UIViewController
-(IBAction)buttonPressed;
#end
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// Hey, I got pressed! Set the text on textLabel to "OK"!
}
#end
This is a very simple case of delegation and protocol mechanism of objective-c..
have a look at this tutorial which will explain you how its done.. you can do this via notification also but that is not usually advised...(because notification is usually used when the receiver is unknown , like in the case of UIDeviceBatteryLevelDidChangeNotification you don't exactly know which view controller wants to know about this.)
I'd first consider what the button press means. Does it change the state of the model?
Say your model is an int, and the button increments it. The view controllers wouldn't message each other about that, they would just both observe the state of the model. (The one with the button could change the state, too).
Thinking about it this way, the solution probably isn't delegation. It's probably notification or KVO.
See the answer to this question: Passing data between two view controllers via a protocol
However, ask yourself if you really need a protocol here. If it is just between this classes or just about the question of accessing data of a class or sending information to a class then that is what the interface of a class is made for.
#interface firstViewController : UIViewController{
UILabel *textLabel; // I personally alway add IBOutlet here too, but I think that is not required.
}
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
And in SecondViewController.m:
#import "FirstViewController.h"
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// You will have to have a properly set instance variable firstViewController
[firstViewController.textLabel setText:#"OK"];
}
#end
So your second view controller needs to 'know' the first one. One way of achieving that is defining
FirstViewController *firstViewController;
as property and set it from wherever the second view controller is created and the first one is already known. How to do that exactly depends very much on the architecture of your app.
I have a view controller which contains a UISearchBar
#interface TradeFindHeaderViewController_iPhone : UIViewController {
UISearchBar *searchBar;
}
#pragma mark - Properties
#property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
#pragma mark - Methods
-(void) configureSearchBar;
#end
This controller is then initialized and stored in a property of another controller.
#interface TradeFindViewController_iPhone : TradeFindViewController<UISearchBarDelegate> {
TradeFindHeaderViewController_iPhone *_headerController;
}
#pragma mark - Properties
#property (nonatomic, retain) TradeFindHeaderViewController_iPhone *headerController;
#end
I want this TradeFindViewController_iPhone to receive the UISearchBar delegate events so I assign it's delegate
-(void)configureTableHeader{
self.headerController=[[TradeFindHeaderViewController_iPhone alloc]initWithNibName:#"TradeFindHeaderView_iPhone" bundle: nil];
self.headerController.searchBar.delegate=self;
self.tableView.tableHeaderView=self.headerController.view;
}
However, the UISearchBar delegate events are not being called. Have I assigned the delegate properly given the UISearchBar is in the contained view?
I would probably implement a multi-level delegate system. Your TradeFindHeaderViewController_iPhone class would register as the delegate for the UISearchBar, and would then call a delegate method in your TradeFindViewController_iPhone class.
This solution helps to keep the whole design very modular, and also prevents things breaking (changing the name of objects) across classes.
This should solve your issue with the delegate methods not being called.
Hope this was of some help.
Josh