I'm having the following problem.
I've created a custom view controller that has a some useful methods that I need.
This is the code in the .h
#interface MYViewController : UIViewController
- (void)method;
- (void)otherMethod;
#end
This is my init method of MYViewController class:
- (instancetype)init
{
self = [super init];
return self;
}
Then when I try to extend that class I can't set the title of the child controller. For example, in "MYOtherController.h"
#interface MYOtherViewController : MYViewController
- (void)childControllerMethod;
#end
And this is the init of MYOtherViewController:
- (instancetype)init
{
self = [super init];
return self;
}
And then, if I instantiate a MYOtherViewController object and try to set the title, it happens nothing. For example:
MYOtherViewController *controller = [[MYOtherViewController] alloc] init];
controller.title = #"Hello";
If I put this in the viewDidLoad of the MYOtherViewController class it logs that title is nil:
- (void)viewDidLoad {
NSLog(#"title: %#", self.title);
[super viewDidLoad];
}
Why can't set the title in this child class?
the title for the viewcontroller hasnt been made yet after the alloc init, you would need to set it after the viewDidLoad (which is when all the UI elements have been initialized), so what you can do is make an #property on the viewcontroller which you set after the alloc init, then in the viewDidLoad, set the title to the value of the #property
Related
Through delegation I created a view controller that takes the input from textfields and passes it back and adds it to an nsmutablearray, successfully adding a row for it. whenever i navigate away from the tableview my newly appended object just disappears. Here is some code to give you a better idea of what i might be doing wrong.
TableViewController.h -
#interface TableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>
#property(strong,nonatomic)NSMutableArray *codeList;
#property(strong,nonatomic)NSMutableArray *codeArray;
#end
TableViewController.m -
#interface TableViewController ()
#end
#implementation MCTableViewController
#synthesize codeList;
#synthesize codeArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.codeList = [NSMutableArray arrayWithObjects:#"Array",
#"Pointer",
#"Class",
#"Protocol",
#"Delegate",
nil];
self.codeDescArray = [NSMutableArray arrayWithObjects:
#"Array Description",
#"Pointer Description",
#"Class Description",
#"Protocol Description",
#"Delegate Description",
nil];
)
In the same class I programmatically created a button that moves to the view where data is supposed to be passed back. code below. This is done through delegation.
- (void)addNewCodeButtonPressed {
AddNewCodeVC *addVC = [[AddNewCodeVC alloc] init];
addVC.dataDelegate = self;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:addVC];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
Here is where the delegate logic is created in AddNewCodeVC.h -
#import <UIKit/UIKit.h>
#import "TableViewController.h"
#class AddNewCodeVC;
#protocol addNewCellData <NSObject>
- (void)sendDataToTableView:(NSString*)code codeDesc: (NSString*)desc;
#end
#interface AddNewCodeVC : UIViewController<UITextFieldDelegate> {
__weak id dataDelegate;
}
#property(weak,nonatomic)id<addNewCellData>dataDelegate;
#property(strong,nonatomic)UITextField *codeTextfield;
#property(strong,nonatomic)UITextField *descTextfield;
#end
finally here is the AddNewCodeVC.m -
#import "AddNewCodeVC.h"
#interface AddNewCodeVC ()
#end
#implementation AddNewCodeVC
#synthesize dataDelegate;
- (void)viewDidLoad {
[super viewDidLoad];
self.codeTextfield.delegate = self;
self.descTextfield.delegate = self;
//Programmatically created both textfields, nothing special
}
//"saveNewCode" is action for another button i created
- (void)saveNewCode {
sendDataToTableView:self.codeTextfield.text codeDesc:self.descTextfield.text];
[self.dataDelegate sendDataToTableView:self.codeTextfield.text codeDesc:self.descTextfield.text];
NSLog(#"CODE: %#", self.codeTextfield.text);
NSLog(#"DESC: %#", self.descTextfield.text);
[self dismissViewControllerAnimated:YES completion:nil];
}
The code works but nsmutable array wont hold the passed values if i navigate away from the TableViewController. I think this is because my main view controller programmatically segues to the tableview controller and creates a new instance of it, so that might have some effect? I'll leave some code below just in case it is relevant.
MainviewController.m -
- (void) tableViewBtnPressed:(UIBarButtonItem *)sender {
TableViewController *tableVC = [[TableViewController alloc] init];
//This for another delegate I created, not relevant
tableVC.selectedDataDelegate = self;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:tableVC];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
Hopefully this code is enough to illustrate the problem, i hope someone has an idea whats going on with the NSMutableArray, and why it isn't holding any new objects that are passed into it, any help is appreciated.
I am trying to add custom elements including drop downs and view changing from my navigationBar, because of this I need a custom one. However when trying to set the delegate to my view controller it throws an error.
#interface MyViewController : UIViewController <MyCustomNavigationBarDelegate>
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationController *nav = [[UINavigationController alloc] initWithNavigationBarClass:[MyCustomNavigationBar class] toolbarClass:[UIToolbar class]];
nav.navigationBar.assessmentDelegate = self;
}
The last line throws the error Property 'assessmentDelegate' not found on object of type 'UINavigationBar *' However it should be type of class MyCustomNavigationBar which clearly has the delegate:
#protocol MyCustomNavigationBarDelegate;
#interface MyCustomNavigationBar : UINavigationBar
#property (weak, nonatomic) id <MyCustomNavigationBarDelegate> assessmentDelegate;
#end
#protocol MyCustomNavigationBarDelegate <NSObject>
-(void)presentViewController:(UIAlertController *)alert;
#end
cast your navigaton Bar to MyCustomNavigationBar
MyCustomNavigationBar *switchNav = (MyCustomNavigationBar *)nav.navigationBar;
switchNav.assessmentDelegate = self
I have a custom class/object that handles gestures and conducts animations for a given view using a CADisplayLink. In its simplest form my class looks something like follows:
#interface SomeClass : NSObject
#property (strong) UIView *someView;
#end
When I add the following code to my view controller....
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
someClass.someView = someView;
}
... I was anticipating my someClass object would be retained for the life of the view controller, since I am using a strong reference to someView.
However someClass is immediately deallocated.
I am already aware that I can overcome the deallocation simply by adding someClass as a property (or indeed iVar) of the view controller however I would ideally like to avoid this extra work...
so is there anyway I can have my class retained until either the view or view controller its associated with are deallocated?
EDIT
UIGestureRecognizer objects are an exmaple of a class that doesn't get deallocated when I associate them with a view...
- (void)viewDidLoad
{
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
[someView addGestureRecognizer:gestureRecognizer];
}
// tapGestureRecognizer still lives
Presumably this is because the UIView takes owner ship of the UIGestureRecognizer object. Is there anyway to achieve this with my class and a UIView category? I.e....
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
[someView addSomeClass:someClass];
}
If you want to associate the object with a UIView in the same way a UIGestureRecognizer does then this is technically possible using associatedObjects as follows (but I'm not sure I'd advocate this approach since associatedObjects are often frowned upon)...
SomeClass.h
#class SomeClass;
#interface UIView (SomeClass)
- (void)addSomeClass:(SomeClass *)someClass;
- (void)removeSomeClass:(SomeClass *)someClass;
#end
#interface SomeClass : NSObject
#property (strong) UIView *someView;
#end
SomeClass.m
#import "SomeClass.h"
#import <objc/runtime.h>
#implementation UIView (AssociatedObject)
- (void)addSomeClass:(SomeClass *)someClass
{
NSMutableArray *someClasses = [self someClasses];
if (someClasses == nil) {
someClasses = [[NSMutableArray alloc] init];
[self setSomeClasses:someClasses];
}
[someClasses addObject:someClass];
}
- (void)removeSomeClass:(SomeClass *)someClass
{
NSMutableArray *someClasses = [self someClasses];
if (someClasses != nil) {
[someClasses removeObject:someClass];
if (someClasses.count == 0) {
[self setSomeClasses:nil];
}
}
}
#pragma mark - Private Methods
- (NSMutableArray *)someClasses
{
return (NSMutableArray *)objc_getAssociatedObject(self, #selector(someClasses));
}
- (void)setSomeClasses:(NSMutableArray *)someClasses
{
objc_setAssociatedObject(self, #selector(someClasses), someClasses, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
#implementation SomeClass
#end
Implementation
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
someClass.someView = someView;
[someView addSomeClass:someClass];
}
Some further reading on associatedObjects from NSHipster...
http://nshipster.com/associated-objects/
But you can declare SomeClass instance instead of property like this:
#implementation ViewController
{
SomeClass* _someClass;
}
...
- (void)viewDidLoad
{
[super viewDidLoad];
_someClass = [[SomeClass alloc] init];
_someClass.someView = someView;
}
Your SomeClass instance is holding a strong reference to the someView, but nothing is holding a reference to the SomeClass instance except the local variable inside your viewDidLoad message, so as soon as the method exits, that instance can be deallocated. As that was the object holding the only reference to your UIView the view can also be deallocated.
Your only options are to store the reference to the SomeClass object in an instance variable (or iVar) as stosha suggested or in a property. Properties are the preferred method and with automatic synthesis they don't take much more effort than a local variable declaration.
You can declare the property inside the .m file so that it isn't visible to other classes that reference your ViewController class -
In your ViewController.m file -
#interface ViewController ()
#property (strong, nonatomic) SomeClass *someClass;
#end
#implementation ViewController
...
(void)viewDidLoad
{
[super viewDidLoad];
self.someClass = [[SomeClass alloc] init];
self.someClass.someView = someView;
}
I use this code, but "it works" doesn't happen.
DetailViewController.h
[#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#protocol ProtocolNameDelegate
-(void)DoSomething;
#end
#interface DetailViewController : UIViewController {
id<ProtocolNameDelegate> _delegate;
}
#property (strong, nonatomic) id<ProtocolNameDelegate> _delegate;
DetailViewController.m
#synthesize _delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
[_delegate DoSomething];
}
MasterViewController.h
#interface MasterViewController : UITableViewController <ProtocolNameDelegate>
MasterViewController.m
-(void)DoSomething
{
NSLog(#"It works");
}
I think i have to add something like:
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
But it gives an error, and by the way will it create another instance of MasterViewController?
Instead of
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
write this,
DetailViewController* svc = [[DetailViewController alloc] init];
dvc._delegate = self;
You made mistake in implementation.
Abstract of implementation should be.
Create Protocol in DetailVC.
Create Property for Delegate, Synthesize, and make call.
Import DetailVC in MasterVC and include delegate in MasterVC.h
Implement protocol method in MasterVC.m
Create instance of DetailVC and assign DetailVCObj.delegate = self;
In MasterViewController.m, you need to allocate and intialise DetailViewController somewhere
DetailViewController* dvc = [[DetailViewControlleralloc] init];
dvc._delegate = self;
Also, because you have written [_delegate doSomething] in
DetailviewController's viewDidLoad method,
it means you must set dvc._delegate = self; in MasterViewController.m
before loading dvc's view (before addSubview or anything that loads view).
In the app I'm working on, I have a UIViewController sublcass and a UIView subclass. in the storyboard the view controller contains the UIview. in the uiview I'm drawing something but I need it to know some values that it should be getting from the view controller. So I created a custom protocol in the view controller .h file:
#protocol SSGraphViewControllerProtocol <NSObject>
- (void)numberOfSemesters:(int)number;
#end
#property (weak, nonatomic) id <SSGraphViewControllerProtocol> delegate;
and in the UIView class I confirmed it as having the protocol above and I implemented its method. However. when I pass a number from the view controller, UIView doesn't receive it. Using NSLog, I figured out that UIView isn't entering - (void)numberOfS:(int)number; am I doing anything wrong? How can I fix it? and is there another way that I can send data from the UIViewController class to the UIView controller?
Here is the full code:
UIViewController.h
#protocol SSGraphViewControllerProtocol <NSObject>
- (void)numberOfSemesters:(int)number;
#end
#interface SSGraphViewController : UIViewController
#property (weak, nonatomic) id <SSGraphViewControllerProtocol> delegate;
#end
UIViewController.m
#implementation SSGraphViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.delegate numberOfSemesters:2];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
UIView.h
#interface SSGraph : UIView <SSGraphViewControllerProtocol>
#end
UIView.m
static int numberOfS = 0;
#implementation SSGraph
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
SSGraphViewController *graph = [[SSGraphViewController alloc] init];
graph.delegate = self;
return self;
}
- (void) numberOfSemesters:(int)number{NSLog(#"YES");
numberOfSemesters= number;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
}
Read This Article, It is best example with Description
http://css.dzone.com/articles/do-not-publishcreating-your
Also read for create Protocol
Following i describe simple Example for How to create protocol
#DetailViewController.h
#import <UIKit/UIKit.h>
#protocol MasterDelegate <NSObject>
-(void) getButtonTitile:(NSString *)btnTitle;
#end
#interface DetailViewController : MasterViewController
#property (nonatomic, assign) id<MasterDelegate> customDelegate;
#DetailViewController.m
if([self.customDelegate respondsToSelector:#selector(getButtonTitile:)])
{
[self.customDelegate getButtonTitile:button.currentTitle];
}
#MasterViewController.m
create obj of DetailViewController
DetailViewController *obj = [[DetailViewController alloc] init];
obj.customDelegate = self;
[self.navigationController pushViewController:reportTypeVC animated:YES];
and add delegate method in MasterViewController.m for get button title.
#pragma mark -
#pragma mark - Custom Delegate Method
-(void) getButtonTitile:(NSString *)btnTitle;
{
NSLog(#"%#", btnTitle);
}
You're creating a view controller instance inside of initWithFrame:, assigning its delegate to be self, and then not keeping a reference to the controller or adding its view into the view hierarchy. This is certainly not what you meant to do. Make the connection in your storyboard instead, by making the delegate property an IBOutlet and connecting them by right clicking on the view controller and dragging from the circle next to the property name onto your view instance.
As an aside I'm not convinced of the utility of using a protocol in this way. If the view needs to know some information to do its job, if should either expose some properties that can be set by the controller, or declare a dataSource protocol and query its dataSource rather than rely on the view controller defining the interface it needs.
// Add an observer to your ViewController for some action in uiview
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveActionNotification:)
name:#"someActionNotification"
object:nil];
// Post Notification and method in your Viewcontroller will be called
[[NSNotificationCenter defaultCenter] postNotificationName:#"someActionNotification" object:self];
// at the end Dont forget to remove Observer.
[[NSNotificationCenter defaultCenter] removeObserver:#"someActionNotification"];