Objective-c: How to find UITableViewCell's containing UITableView - objective-c

I've subclassed UITableView with some specific properties that I'd like to be able to access in a subclass of UITableViewCell. Is there something like cell.containingTableView that I'm just missing?

Really you don't want your views to know much of anything about their parents. The thing to do is subclass UITableViewCell, give it some properties to hold the information you want to pass to it and then let your UITableViewController do so in tableView:cellForRowAtIndexPath:

You may think of using
self.superview
but it's VERY fragile, so please DON'T!.
You'd better pass a reference to the UITableView explicitly to the cell.
In your cell declare a property
#property (nonatomic, weak) UITableView *parentTableView;
and assign it in the tableView:cellForRowAtIndexPath: data source method.
Finally if your purpose is just to call some methods on the parent controller, you can use a proper delegation pattern and define your own protocol
#protocol CellDelegate <NSObject>
- (void)aMethodThatINeedToCall:(id)whatever;
#end
declare a delegate property
#property (nonatomic, weak) id<CellDelegate> delegate;
and make your UITableViewController conform to that
#interface MyTableViewController : UITableViewController <CellDelegate>
...
#end
#implementation
...
- (void)aMethodThatINeedToCall:(id)whatever {
// do stuff
}
...
#end

Related

Change property class type in private implementation

I have a class on iOS which has a UIView property that other objects are able to read. For the sake of my internal implementation of this class, I want this view to be a custom subclass of UIView, but there is no need for the clients of this class to be aware of that. To them, this property should and will behave just like any other UIView.
Ideally what I want is something like this.
MyViewController.h:
#property (nonatomic, readonly) UIView *aView;
MyViewController.m:
#property (nonatomic, readwrite) CustomView *aView;
The compiler allows me to do this, but it doesn't actually understand what I want to do. It won't allow to message .aView as a CustomView object, it just lets me message it as a regular UIView object.
A workaround I have at the moment is to do this.
CustomView *newView = [[CustomView alloc] initWithFrame:CGRectZero];
[newView customViewMessage];
self.aView = newView;
Is there a better way to do this?
My preferred approach is to give the private property a different name. I then implement the getter for the public property and return the internal property's instance variable.
Example:
Class.h
#interface Class : NSObject
#property (nonatomic, readonly) UIView *aView
#end
Class.m
#interface Class ()
#property (nonatomic, retain) CustomView *cView;
#end
#implementation
- (UIView *)aView {
return _cView;
}
#end
Since you are using a view controller this is how you'd do it.
Step one.
Add a private property, for example
#property(nonatomic, readonly) CustomView *customView;
And override the getter to return the view.
-(CustomView *)customView{ return (id)self.view; }
This way you can use that property to call methods specific to your class without having to cast it every time, and any other objects can interface with your view controller's view in the same way they would with any other view controller.
Step 2
Since you are using a view controller there are two options:
You are instantiating the vc from a storyboard
In this case, you can select the view in Interface Builder and set it to a custom class. Done.
You are instantiating the VC from code
In that case, you have to override your view controller's loadView method and create your view in there:
-(void)loadView{ self.view = [CustomView new]; }

How can i make a UITabbar like this?

I would like to create a UITabbar like below but i don't know what is the logic to do that.
Here is the large answer:
First of all, you will need to create a UIView subclass to get a view that looks like the bar that you want. It can be composed by a background UIImageView and three buttons.
Then, the best thing would be to create a subclass of the UITabBarController and in its viewDidLoad or at any point where the flow will go through just once, you instantiate one view of type specified at first point. You should place the frame of this view in order to hide the original tabbar of the controller.
This would be the custom bar header file:
#interface CustomBar : UIView
{
}
#property (nonatomic, retain) UIImageView *backgroundView;
#property (nonatomic, retain) NSArray *buttons;
#end
You can easily complete the implementation. You can try to look for how to instantiate it with a nib file to make it easier to design it. In order to test, you can first just set the background color to green or something visible.
Then, this would be the subclass of the UITabBarController class:
#interface CustomTabBarController : UITabBarController
#property (nonatomic, retain) CustomBar *customBar;
#end
#implementation CustomTabBarController
- (void)viewDidLoad
{
[super viewDidLoad];
self.customBar = [[[CustomBar alloc] initWithFrame:[self.tabBar frame]] autorelease];
[self.view addSubview:self.customBar];
}
#end
Please, remember to implement the dealloc if you are not using ARC.
The thing I am not sorting out here is how to create the communication between the buttons from the custombar and the tabbarcontroller. This should be solved by delegates. If you need help with that, I will complete that too.
Good luck!

IBOutlet in ARC releases and sets to nil. How to avoid this? objective c

I'm new to ARC and Storyboarding. I've set IBOutlet to UITableView from my UIViewController.
After some time my IBOutlet sets to nil and I can't reload it from other classes.
Here is my dataTable IBOutlet:
#property (weak, nonatomic) IBOutlet UITableView *dataTable;
At the start dataTable is not nil, but not when I try to access it from another class (via appDelegate). How to solve this problem?
UPDATE
I call this method from my UIViewController
[appDelegate.myClass loginWithUserName:loginField.text andPassword:pwdField.text];
When it's done, and I have data to show, I call this code from loginWithUserName method:
MyViewController *controller = [[AppDelegate sharedStoryboard] instantiateViewControllerWithIdentifier:#"MyViewController"];
[controller audioLoaded];
And here is that method in my UIViewController, wich reloads data
-(void) audioLoaded
{
//it is nil here
[self.dataTable reloadData];
}
Set the property to strong retain the object:
#property (strong, nonatomic) IBOutlet UITableView *dataTable;
It's not good practice to access a UITableView from another view controller though..
EDIT:
You shoul reconsider the whole approach, by moving that logic from your appdelegate to a dedicated class that will perform the login. You can create a simple protocol that the UIViewController with the table can implement, then, when calling the login method, pass a reference to the current viewcontroller, something like
loginWithUserName:andPassword:andCaller:(id<LoginDelegate>)sender
Where LoginDelegate is something on this line:
#protocol LoginDelegate
- (void)audioLoaded;
#end
In this way you can just call
[sender audioLoaded];

Modifying properties of a view controller form another view controller

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.

How do I assign a delegate in a view for a control contained in a child view?

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