I'm trying to hide a UILabel in every object (UIView) of the same class in my app. I tried something with a static class method but I'm not able to access to the instance variable.
MyView.h
#interface MyView: UIView
{
UILabel *titleLabel;
UILabel *subTitleLabel;
}
+(void)hideLabel;
#end
MyView.m
#import "MyView.h"
#implementation TempNodeView
+(void)hideLabel
{
[titleLabel setHidden:YES];
}
#end
What is the best (proper) solution in this kind of situation?
Thank you very much
For your case I suggest you to have references to all of this objects. This means you will need to add the object into some static array in its constructor.
The problem then occurs that the views will be retained by the array so you need another object that will be a container for a weak reference to your object so you avoid memory leak.
Try to build something like the following:
static NSMutableArray *__containersPool = nil;
#interface MyViewContainer : NSObject
#property (nonatomic, weak) MyView *view;
#end
#implementation MyViewContainer
#end
#interface MyView : UIView
#property (nonatomic, readonly) UILabel *labelToHide;
#end
#implementation MyView
+ (NSMutableArray *)containersPool {
if(__containersPool == nil) {
__containersPool = [[NSMutableArray alloc] init];
}
return __containersPool;
}
// TODO: override other constructors as well
- (instancetype)initWithFrame:(CGRect)frame {
if((self = [super initWithFrame:frame])) {
MyViewContainer *container = [[MyViewContainer alloc] init];
container.view = self;
[[MyView containersPool] addObject:container];
}
return self;
}
+ (void)setAllLabelsHidden:(BOOL)hidden {
for(MyViewContainer *container in [[self containersPool] copy]) {
if(container.view == nil) {
[[self containersPool] removeObject:container]; // It has been released so remove the container as well
}
else {
container.view.labelToHide.hidden = hidden;
}
}
}
#end
Related
I have an UITableViewCell's subclass (Say, MyTableViewCell).
Inside MyTableViewCell's implementation, I have a subclass of UIView (Say, MyView) where I drew all needed stuffs. I assigned MyView to the MyTableViewCell's content view.
See the code for better clarification:
MyTableViewCell.h
#interface MyTableViewCell : UITableViewCell
{
...
...
}
#property ...
#property ...
#end
MyTableViewCell.m
#import "MyTableViewCell.h"
#interface MyView : UIView //Interface of MyView
{
MyTableViewCell *myCell;
...
}
#end
#implementation MyView //Implementation of MyView
-(id)initWithFrame:(CGRect)frame cell:(MyTableViewCell *)cell
{
if (self = [super initWithFrame:frame])
{
myCell = cell;
}
return self;
}
-(void)drawRect:(CGRect)rect
{
... //Drawing stuffs
}
...
...
-(void)eventOccured
{
//I want to call a method doNeedful() of MyTableViewCell here.
}
#end
#interface MyTableViewCell() //private variables of cell
{
UIView *myView;
}
#end
#implementation MyTableViewCell
#synthesize ...
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
myView = [[MyView alloc] initWithFrame:self.contentView.bounds cell:self];
...
[self.contentView addSubview:myView];
}
return self;
}
...
...
-(void)doNeedful
{
}
#end
Question:
I want to call the doNeedful() method in MyTableViewCell class when MyView's eventOccured() was called.
I tried to create protocol but failed in all ways. Can someone help me to know how to define protocol to achieve my goal?
instead of this code
#interface MyView : UIView //Interface of MyView
{
MyTableViewCell *myCell;
...
}
add below code
#interface MyView : UIView //Interface of MyView
#property(nonatomic,assign) id mycell// It should be assign or weak bcoz cell is the parent
in your method
-(void)eventOccured
{
[self.myCell doNeedful];
}
When allocating
myView = [[MyView alloc] initWithFrame:self.contentView.bounds cell:self];
myview.myCell = self;
[self.contentView addSubview:myView];
I have a superclass called SuperClass a read-only property. That looks like this:
#property (nonatomic, strong, readonly) NSArray *arrayProperty;
In a subclass I need an initializer that takes a instance of SuperClass as a parameter:
- (instancetype)initWithSuperClass:(SuperClass *)superClass
I created a GitHub sample project that shows what the problem is: https://github.com/marosoaie/Objc-test-project
I cannot do _arrayProperty = superClass.arrayProperty in the initializer.
I want to keep the property read-only in SubClass as well.
Any ideas on how this could be solved?
I know I could declare the property as readwrite in a class extension inside the SubClass implementation file, but I'm hoping that there's a better solutions than this.
Edit:
SuperClass.h
#import <Foundation/Foundation.h>
#interface SuperClass : NSObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
#property (nonatomic, strong, readonly) NSString *stringProperty;
#property (nonatomic, strong, readonly) NSArray *arrayProperty;
#end
SuperClass.m
#import "SuperClass.h"
#implementation SuperClass
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
_arrayProperty = dictionary[#"array"];
_stringProperty = dictionary[#"string"];
}
return self;
}
#end
SubClass.h:
#import <Foundation/Foundation.h>
#import "SuperClass.h"
#interface SubClass : SuperClass
#property (nonatomic, strong, readonly) NSString *additionalStringProperty;
- (instancetype)initWithSuperClass:(SuperClass *)superClass;
#end
SubClass.m:
#import "SubClass.h"
#implementation SubClass
#synthesize additionalStringProperty = _additionalStringProperty;
- (NSString *)additionalStringProperty
{
if (!_additionalStringProperty) {
NSMutableString *mutableString = [[NSMutableString alloc] init];
for (NSString *string in self.arrayProperty) {
[mutableString appendString:string];
}
_additionalStringProperty = [mutableString copy];
}
return _additionalStringProperty;
}
- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
self = [super init];
if (self) {
// Doesn't work
// _stringProperty = superClass.stringProperty;
// _arrayProperty = superClass.arrayProperty;
}
return self;
}
#end
You already exposed an initializer, that writes to that readonly property -initWithDictionary:. Call that in your SubClass, instead [super init]:
- (instancetype)initWithSuperClass:(SuperClass *)superClass {
NSDictionary *dict = #{
#"array": superClass.arrayProperty,
#"string": superClass.stringProperty,
};
self = [super initWithDictionary:dict];
if (self) {
// Nothing here.
}
return self;
}
It’s quite common to have an initializer for readonly properties, although using dictionary is not that good solution. Typically, I would create:
- (instancetype)initWithArray:(NSArray *)array string:(NSString *)string;
First of all, there is a bug in your test setup: Your key in - (instancetype)initWithDictionary:(NSDictionary *)dictionary is #"array", where the array contains #"arrayProperty".
Regarding your problem:
//...
#interface SuperClass : NSObject
{
#protected // this is what you want: a protected class property, accessible in subclasses, but no where else
NSString *_stringProperty;
NSArray *_arrayProperty;
}
#property (nonatomic, strong, readonly) NSString *stringProperty;
#property (nonatomic, strong, readonly) NSArray *arrayProperty;
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
#end
// SubClass.m
//...
#implementation SuperClass
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
_arrayProperty = dictionary[#"arrayProperty"]; // this was #"array", so could not work
_stringProperty = dictionary[#"stringProperty"]; // same here
}
return self;
}
#end
Then it works. In addition, I would write
#interface SubClass ()
#property (nonatomic, strong, readwrite) NSString *additionalStringProperty;
#end
#implementation SubClass
- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
self = [super init];
if (self) {
_stringProperty = superClass.stringProperty;
_arrayProperty = superClass.arrayProperty;
}
return self;
}
because I prefer the readwrite property in a class extension over the #synthesize magic. But this is a personal opinion.
One main issue regarding to class design still holds: What happens if (similar to your test setup) the dictionary of the superclass does not contain the key? Then it won't be initialized, which is not a good idea, because you expect them to be initialized. So you should check in the subclass if superclass.stringProperty is not nil and add a standard constructor for the superclass to avoid that the two dictionaries are uninitialized.
In your SuperClass.m:
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
// these were always nil, check your dictionary keys
_arrayProperty = dictionary[#"arrayProperty"];
_stringProperty = dictionary[#"stringProperty"];
}
return self;
}
In your SubClass.m:
#interface SubClass ()
#property (strong, nonatomic) NSString * additionalStringProperty;
#property (strong, nonatomic) NSString * subClassString;
#property (strong, nonatomic) NSArray * subClassArray;
#end
#implementation SubClass
- (instancetype)initWithSuperClass:(SuperClass *)superClass
{
self = [super init];
if (self) {
_subClassString = superClass.stringProperty;
_subClassArray = superClass.arrayProperty;
}
return self;
}
I tried the answers here to no avail. What ended up working for me was this answer which mentions that you should directly access the member variable (after declaring it as protected) like so:
self->_stringProperty = #"some string";
I have a long UIViewController with lots of sections and rows. In spite of having separate methods for each of these sections, it still becomes cumbersome jumping to different methods. Is there a best way to design such class? I was thinking of having a category for each of these sections? is it a good idea?
I've found that code related to delegate methods become a large part of my view controllers. One strategy, described in this article at objc.io, is to move data source delegate methods to their own class.
In the last year I started to move out as much code as possible from my view controllers, attempting to create «Lighter View Controllers». There-for you can use objects that implement one — and only one — certain aspect of the functionality that the view controller will have. You could call it Sub Controller, but I and others use the name «Intentions» to express the fact, that each of this objects has one intention.
I experimented with different kinds, from target/action to block based, that I finally found to be the most useful.
An example from real code:
I habe a cash register app that need to communicate via wifi with thermal printers.
To add a printer I have the AddPrinterViewController with textfields for ip address, port and name/location.
Instead of implementing the view controller as delegate for all the textfields I create on class that will serve as delegate for one textfield and has a block-based validation.
#interface TextfieldDelegateIntention : NSObject
#property(nonatomic, weak, readonly) UITextField *textField;
#property (nonatomic, copy) BOOL (^validationBlock)(UITextField *textField);
-(instancetype)initWithTextField:(UITextField *)textField
validationBlock: (BOOL (^)(UITextField *textField)) validationBlock;
-(BOOL)validate;
#end
#import "TextfieldDelegateIntention.h"
#interface TextfieldDelegateIntention ()<UITextFieldDelegate>
#property(nonatomic, weak) UITextField *textField;
#end
#implementation TextfieldDelegateIntention
-(instancetype)initWithTextField:(UITextField *)textField
validationBlock: (BOOL (^)(UITextField *textField)) validationBlock
{
self = [super init];
if(self){
self.validationBlock = validationBlock;
self.textField = textField;
self.textField.delegate = self;
}
return self;
}
-(BOOL)validate
{
if (self.validationBlock) {
return self.validationBlock(self.textField);
}
return NO;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
#end
I setup the view controller with the three textfields in the storyboard.
The IP (version 4 only) address' textfield could look like this:
TextfieldDelegateIntention *ipAddresIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.ipTextField validationBlock:^BOOL(UITextField *textField) {
NSArray *components = [textField.text componentsSeparatedByString:#"."];
if ([components count] == 4) {
__block BOOL compsAreValidNumbers = YES;
[components enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj integerValue] > -1 && [obj integerValue] < 256 ) {
} else {
compsAreValidNumbers = NO;
*stop = YES;
}
}];
return compsAreValidNumbers;
}
return NO;
}];
A overly simple validation, but useful to assist the user with the correct input.
The complete view controller's code looks like
#import <UIKit/UIKit.h>
#import "BaseModalViewController.h"
#class NamendPOSNetworkPrinter;
#class PrinterProvider;
#interface AddPrinterViewController : BaseModalViewController
#property (nonatomic, strong) PrinterProvider *printerProvider;
#property (nonatomic, copy) void(^printerPreferecesEntered)(NSDictionary *printerDict);
#end
#import "AddPrinterViewController.h"
#import "TextfieldDelegateIntention.h"
#import "ButtonIntention.h"
#import "PrinterProvider.h"
#interface AddPrinterViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *ipTextField;
#property (weak, nonatomic) IBOutlet UITextField *portTextField;
#property (nonatomic, strong) NSArray *textFieldIntentions;
#property (nonatomic, strong) ButtonIntention *okIntention;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
#end
#implementation AddPrinterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
TextfieldDelegateIntention *nameIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.nameTextField validationBlock:^BOOL(UITextField *textField) {
if ([textField.text length] > 0) {
return YES;
}
return NO;
}];
TextfieldDelegateIntention *ipAddresIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.ipTextField validationBlock:^BOOL(UITextField *textField) {
NSArray *components = [textField.text componentsSeparatedByString:#"."];
if ([components count] == 4) {
__block BOOL compsAreValidNumbers = YES;
[components enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj integerValue] > -1 && [obj integerValue] < 256 ) {
} else {
compsAreValidNumbers = NO;
*stop = YES;
}
}];
return compsAreValidNumbers;
}
return NO;
}];
TextfieldDelegateIntention *portIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.portTextField validationBlock:^BOOL(UITextField *textField) {
if ([textField.text integerValue] > 1023 && [textField.text integerValue]< 65536) {
return YES;
}
return NO;
}];
self.textFieldIntentions = #[nameIntention, ipAddresIntention, portIntention];
__block typeof(self) weakSelf = self;
ButtonIntention *okIntention = [[ButtonIntention alloc] initWithButton:self.okButton actionBlock:^(UIButton *button) {
typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
__block BOOL formIsValid = YES;
[strongSelf.textFieldIntentions enumerateObjectsUsingBlock:^(TextfieldDelegateIntention *intention, NSUInteger idx, BOOL *stop) {
BOOL isValid = [intention validate];
if (!isValid) {
intention.textField.backgroundColor = [UIColor redColor];
formIsValid = NO;
} else {
intention.textField.backgroundColor = [UIColor greenColor];
}
}];
if (formIsValid) {
if (self.printerPreferecesEntered) {
self.printerPreferecesEntered(#{
#"name": strongSelf.nameTextField.text,
#"ipAddress": strongSelf.ipTextField.text,
#"port": #([strongSelf.portTextField.text integerValue])
});
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongSelf dismissViewControllerAnimated:YES completion:NULL];
});
}
}
}];
self.okIntention = okIntention;
}
#end
As you see I can use simple blocks to add different behavior to the three textfields and only one method is overwritten — no other added.
Another example with email validation from my blog: Lighter ViewControllers with Block-based Intentions
A tableview's datasource is also very easy implemented as Intention.
#import <UIKit/UIKit.h>
#class PrinterProvider;
#interface PrinterDataSource : NSObject <UITableViewDataSource>
#property (nonatomic, strong) PrinterProvider *printerProvider;
#end
#import "PrinterDataSource.h"
#import "VSPOSNetworkPrinter.h"
#import "PrinterProvider.h"
#interface PrinterDataSource ()
#end
#implementation PrinterDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.printerProvider allPrinters] count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PrinterCell" forIndexPath:indexPath];
cell.textLabel.text = [[self.printerProvider allPrinters][indexPath.row] ipAddress];
cell.detailTextLabel.text = [[self.printerProvider allPrinters][indexPath.row] name];
return cell;
}
#end
Use it as
#import "PrinterViewController.h"
#import "AddPrinterViewController.h"
#import "ButtonIntention.h"
#import "BarButtomItemIntention.h"
#import "NamendPOSNetworkPrinter.h"
#import "PrinterProvider.h"
#import "PrinterDataSource.h"
#interface PrinterViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *addButton;
#property (nonatomic, strong) BarButtomItemIntention *addIntention;
#property (nonatomic, strong) AddPrinterViewController *addPrinterViewController;
#property (strong, nonatomic) IBOutlet PrinterDataSource *printerDataSource;
#property (nonatomic, strong) PrinterProvider *printerProvider;
#end
#implementation PrinterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.printerDataSource.printerProvider = self.printerProvider;
self.addPrinterViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddPrinterViewController"];
typeof(self) weakSelf = self;
[self.addPrinterViewController setPrinterPreferecesEntered:^(NSDictionary *printerDict) {
NamendPOSNetworkPrinter *printer = [[NamendPOSNetworkPrinter alloc] initWithName:printerDict[#"name"]
ipAddress:printerDict[#"ipAddress"]
port:printerDict[#"port"]];
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.printerProvider addPrinter:printer];
[strongSelf.tableView reloadData];
}
}];
self.addPrinterViewController.printerProvider = self.printerProvider;
self.addIntention = [[BarButtomItemIntention alloc] initWithButtonItem:self.addButton
actionBlock:^(UIBarButtonItem *buttonItem)
{
typeof(weakSelf) strongSelf = weakSelf;
if(strongSelf){
[strongSelf presentViewController:strongSelf.addPrinterViewController
animated:YES completion:^{
}];
}
}];
}
#end
The charm about this approach is that you can independently create and subclass view controller and intentions, and that I can rearrange them as needed easily. Actually apple started to advertise a similar approach in the recent wwdc video «Advanced User Interfaces with Collection Views»
I would suggest using #pragma mark [divider name] to split up your class into sections so you can easily group methods. Otherwise creating subclasses where it makes sense to handle contained functionality is you're only option. I've seen 8,000+ line view controller classes, so sometimes it's unavoidable if you're too deep to break it all apart.
This is more like a programming philosophy.
Objective-C has lengthy methods (name and declaration), so the best way to ease the code is to delegate any repetitive pattern to an external class.
For example, I create a class reserved solely of returning numerical operations, such as arithmetic operations on NSNumbers, strings, etc. Another one that just returns booleans on an input.
For example, if you verify the validity of a number and increment it, or set it to 1, in even just two places in your code, rather than having somethings like this:
if (variableName != nil && variableName.intValue > 0)
{
variableName2 = [NSNumber numberWithInt:(variableName.intValue + 1)];
}
else
{
variableName2 = [NSNumber numberWithInt:1];
}
It would be easier to have this function outside (in an "Arithmetics" class for example), and call it like follow:
variableName2 = [Arithmetics incrementNumber:variableName];
If you replace all your code like this, by having well named classes and easy to understand methods (names), your code will be more logically readable and easy to follow, which is the philosophy of Objective-C (you get also the bonus of being able to reuse these methods in all your code, keeping the same logic everywhere).
I have a a class I created to generate UIButton's I add to my UIView. This worked great until my conversion to ARC yesterday, not I get the following error:
-[OrderTypeButton performSelector:withObject:withObject:]: message sent to deallocated instance 0x12449f70
Here is the code to add the button to my UIView (actually a subview in my main UIView):
OrderTypeButton *btn = [[OrderTypeButton alloc]initWithOrderType:#"All Orders" withOrderCount:[NSString stringWithFormat:#"%i",[self.ordersPlacedList count]] hasOpenOrder:NO];
btn.view.tag = 6969;
btn.delegate = self;
[btn.view setFrame:CGRectMake((col * width)+ colspacer, rowHeight + (row * height), frameWidth, frameHeight)];
[self.statsView addSubview:btn.view];
And here is my class header:
#import <UIKit/UIKit.h>
#protocol OrderTypeButtonDelegate
-(void) tapped:(id)sender withOrderType:(NSString*) orderType;
#end
#interface OrderTypeButton : UIViewController {
id<OrderTypeButtonDelegate> __unsafe_unretained delegate;
IBOutlet UILabel *lblOrderType;
IBOutlet UILabel *lblOrderCount;
NSString *orderType;
NSString *orderCount;
BOOL hasOpenOrder;
}
#property (nonatomic, strong) IBOutlet UIButton *orderButton;
#property (nonatomic, strong) IBOutlet UILabel *lblOrderType;
#property (nonatomic, strong) IBOutlet UILabel *lblOrderCount;
#property (nonatomic, strong) NSString *orderType;
#property (nonatomic, strong) NSString *orderCount;
#property (nonatomic, assign) BOOL hasOpenOrder;
#property (nonatomic, unsafe_unretained) id<OrderTypeButtonDelegate> delegate;
-(id) initWithOrderType: (NSString *) anOrderType withOrderCount: (NSString *) anOrderCount hasOpenOrder: (BOOL) openOrder;
-(IBAction)btnTapped:(id)sender;
#end
Implementation:
#import "OrderTypeButton.h"
#implementation OrderTypeButton
#synthesize orderButton;
#synthesize lblOrderType, lblOrderCount, orderType, orderCount, hasOpenOrder, delegate;
-(id) initWithOrderType: (NSString *) anOrderType withOrderCount: (NSString *) anOrderCount hasOpenOrder: (BOOL) openOrder {
if ((self = [super init])) {
self.orderType = anOrderType;
self.orderCount = anOrderCount;
self.hasOpenOrder = openOrder;
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.lblOrderType.text =[NSString stringWithFormat:#"%#", self.orderType];
self.lblOrderCount.text = [NSString stringWithFormat:#"%#", self.orderCount];
if (self.hasOpenOrder) {
[self.orderButton setBackgroundImage:[UIImage imageNamed:#"background-order-btn-red.png"] forState:UIControlStateNormal];
self.lblOrderType.textColor = [UIColor whiteColor];
self.lblOrderCount.textColor = [UIColor whiteColor];
}
}
-(IBAction)btnTapped:(id)sender {
NSLog(#"TAPPED");
if ([self delegate] ) {
[delegate tapped:sender withOrderType:self.orderType];
}
}
- (void)viewDidUnload
{
[self setOrderButton:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
This seems fairly simple what I am doing here, not sure what changed with ARC that is causing me problems.
Maybe ARC autorelease created button, try to store created buttons in Array
//.h file
#property (nonatomic, strong) NSArray *buttonsArray
//.m file
#synthesize buttonsArray
...
- (void)viewDidLoad {
buttonsArray = [NSArray array];
...
OrderTypeButton *btn = [[OrderTypeButton alloc]initWithOrderType:#"All Orders"
withOrderCount:[NSString stringWithFormat:#"%i",[self.ordersPlacedList count]]
hasOpenOrder:NO];
btn.view.tag = 6969;
btn.delegate = self;
[btn.view setFrame:CGRectMake((col * width)+ colspacer, rowHeight + (row * height), frameWidth, frameHeight)];
[self.statsView addSubview:btn.view];
//Add button to array
[buttonsArray addObject:btn];
Also this approach will help if you want to change buttons, or remove some specific button from view
File:PeopleLinkEditViewController.h
#protocol PeopleLinkEditViewControllerDelegate<NSObject>
#optional
-(void)headerInfoEditFinish;
#end
#interface PeopleLinkEditViewController : UITableViewController
{
id<PeopleLinkEditViewControllerDelegate> delegate;
}
#property (nonatomic, retain) id<PeopleLinkEditViewControllerDelegate> delegate;
-(IBAction)doneEdit:(id)sender;
#end
File:PeopleLinkEditViewController.m
#implementation PeopleLinkEditViewController
...
#synthesize delegate = _delegate;
...
- (void)viewDidLoad
{
...
headerView = [[PeopleLinkHeaderView alloc] initWithFrame:CGRectMake(0, 0, 320, 286)
passData:headerDic];
...
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if(section == 0)
{
return headerView;
}
return nil;
}
-(IBAction)doneEdit:(id)sender
{
if ([delegate respondsToSelector:#selector(headerInfoEditFinish)])
{
NSLog(#"%d", __LINE__);
[delegate headerInfoEditFinish];
}
}
#end
File:PeopleLinkHeaderView.h
#import "PeopleLinkEditViewController.h"
#interface PeopleLinkHeaderView : UIView<PeopleLinkEditViewControllerDelegate>
{
}
#end
File:PeopleLinkHeaderView.m
#interface PeopleLinkHeaderView()
#property (nonatomic, retain) PeopleLinkEditViewController *edit;
#end
#implementation PeopleLinkHeaderView
- (id)initWithFrame:(CGRect)frame passData:(NSDictionary *)data
{
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
PeopleLinkEditViewController *edit = [sb instantiateViewControllerWithIdentifier:#"PeopleLinkEditController"];
edit.editDelegate = self;
}
-(void)headerInfoEditFinish
{
[baseInfo setValue:baseInfoValue forKey:#"value"];
[dataPass writeHeaderValueToPlist:baseInfo];
}
the method for delegate can't be called. And when I debug it, I find delegate is nil in editcontroller. and editcontroller is created by storyboard. Headerview is a subview of edit controller.
The problem is that the Instance you are sending the action to and the one you have declared the delegate for are not the same.
I can tell by looking at this
edit = [[PeopleLinkEditViewController alloc] init];
edit.delegate = self;
This is a newly created instance and you are not displaying or presenting it in any way. Perhaps this is form a different View Controller made on the storyboard? If it is one you specified in the storyboard you should retrieve THAT one and assign its delegate.
Use this to retrieve the correct instance
#import ViewController.h
Then on the place where you want to set the delegate.
ViewController *tmp = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewControllerIdentifier"];
tmp.delegate = self;
Dont forget to change to include the header for the class and change to the correct tag.
Retrieved from here:
https://stackoverflow.com/a/11931714/1068522