I've got the following situation: I've got a UISwipeGestureRecognizer hooked up on my table cell. On swipe, two buttons get their .hidden-status switched to NO.
Those cells are custom made. Meaning, the IBAction of those buttons is in that controller. Now I'd like the UIButton in those custom cells to send back a variable to the parent view. That parent view is the UIViewController (containing a UITableView).
How can I do this? I've read about protocols, but I can't get a solid example for that... Or are there any other ideas? They are all welcome :)
The approach you are following may result in a crash or a memory leak as you have to retain the controller of your uitableview cell content.
However protocols are simple approach where after you create your table view cell you can allocate the viewcontroller (which creates the table view) as a delegate to all actions for the cell's content view.
In your custom cell's controller just call the delegate method and it will be handled by your parent view.
It may sound confusing, but its really simple. Will try to post sample code.
#protocol yourDelegate;
#interface yourViewController{
id<yourDelegate> delegate;
}
#property (nonatomic,assign) id<yourDelegate> delegate;
#end
#protocol yourDelegate
- (void)passThisActionToMainController;
- (void)passThisEventToMainController;
#end
Once you are done with the above in your table view cell view controller, in your main controller create an instance of your view controller and then
controller.delegate = self;
In your cell's view controller when a user taps or performs any action then call
[delegate passThisActionToMainController];
or
[delegate passThisEventToMainController];
This way your main controller gets the event and it can be processed.
Alternate way to do this would be to create the buttons when you create the cell and handle the events. I would recommend this approach.
[myButton addTarget:self action:#selector(whatMyButtonShouldDo:)
forControlEvents:UIControlEventTouchUpInside];
The above line is copied from an answer here.
Related
I have a UIViewcontroller on which I have added a UIView as a subview and then added another UIView as another subview over the first view.
Now I want to call a method in the UIViewController from the last UIView. All of these views are custom views and have been created as different classes.
What would be the best way to call this topmost view from the child of the child?
You cannot add a view as a UIViewController's subview. I believe you added it as a subview of the controller's view. I do not understand what you mean to ask in the question. Please correct me if I'm wrong.
I assume you want to call a method in the UIViewController on some user interaction. If this view (the child of child)is a button, you could simply do this:
[button addTarget:nil action:#selector(methodToCall:) forControlEvents:UIControlEventTouchUpInside];
A nil target will result in the method methodToCall in the UIViewController to be called.
If you don't see a call there, make sure the view hierarchy (all predecessor views in it) is user interaction enabled.
A better approach (If you haven't subclassed UIButton) would be to create a protocol for your custom views, specify a tap gesture recognizer, specify a delegate and send call a method in the delegate whenever you receive a tap.
The whole point of a view controller is that it's in control of the view. You shouldn't be adding knowledge to the view which requires it to know to pass information to the controller. The view should either have UIControls on it and the controller sets the appropriate target and action or the controller should add gestures to the view and, again, specify appropriate target and action.
I can not get what you want exactly..if you want simple topmost view then you set tag of this view when you call addsubview .and every time increment tag when add subview and decrement tag when remove subview.then you easily get topmost view with help of current tag.
Do like this,
In TopView.h
#property (nonatomic, assign) YourViewController *ViewController_object;
In TopView.h
-(void)dealloc {
[super dealloc];
ViewController_object=nil;
}
-(void)your_method {
[ViewController_object method_from_VC];
}
hope it will helps you...
So I have a UIViewController A which adds a UIView B as a subclass. The UIView B has a UITableView. I want the UITableView scrollView delegate to be in the UIViewController A. How do I do this? As of now the scrollViewDidScroll delegate is inside this UIView class. Is there a way so that the scrollViewDidScroll is inside viewController A and is called whenever the UITableView in the UIView is scrolled?
Make the tableView as a property accessible from the outside. Than you could set it up in your ViewController A like that:
// ClassB.h
#property (nonatomic, readonly) UITableView* tableView;
// In your UIViewControllerA.m
// in loadView: or anywhere else
self.viewB.tableView.delegate = self;
So result is: your ViewController is the delegate.
The scroll view and the table view are the same object -- notice that UITableView is a subclass of UIScrollView. That object has only one delegate, not separate delegates for the table stuff and for the scroll stuff. Also notice that UITableViewDelegate adopts UIScrollViewDelegate. So, the object you set as the table's delegate will also get UIScrollViewDelegate messages.
Now, there's no reason that your table's delegate can't forward messages about scrolling to some other object. You'd have to set that up yourself, of course. So, when B gets a -scrollViewDidScroll: message, it might send an equivalent message to A, or whatever. I'd think twice before actually doing that, though... I'd try to have just one object (the view controller) be responsible for everything related to the table.
the main tableview will have rows that is added/created from the user and that row will open their own tableview which also have their own add/create own row. My main question is how to make sure each row thats added in the main tableview opens their own tableview (as blank until added from user).
This link i've seen is the closest How can I move to another view controller when the user clicks on a row? but not exactly what Im looking for. Most of the samples/tutor is all ususally populated or already have an array in their tableview. I wanted to make mines both completely nothing in it but added only by the users and main tableview each row opens its own tableview.
I already have two tableview one is main and other is sub to each row. I was able to insert row in main table view but it opens the same tableview as the other rows in the main tableview and would like to know how to make sure each row in main tableview opens their own blank tableview.
Usually your view controller controls the tableView. I guess you are trying to fit in there multiple table views and logic for each one. Instead, just create controllers for each table.
First create your main view controller using a UIViewController template (don't use a TableViewController). Use a XIB and add there a TableView element, OR create a UITableView instance using code.
Now create a class conforming to the delegate and datasource, protocols. This doesn't need to be a view controller. Example:
// MiniTVC.h
#interface MiniTVC : NSObject <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSArray *array;
-(id) initWithTableView:(UITableView*)tableView array:(NSArray*)array;
#end
and implement the delegate and datasource methods as usual. You need at least the following:
tableView:heightForRowAtIndexPath:
numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
tableView:cellForRowAtIndexPath:
Then back to your main view controller, you create (or hook from the interface designer using IBOutlets) a tableView and set its delegate and datasource to your MiniTVC class, eg:
// sample table created by code
CGRect frame = CGRectMake(50,50,100,100);
UITableView *tableView = [[UITableView alloc] initWithFrame:frame]
[self.view addSubView:tableView];
// set the datasource/delegate
MiniTVC *miniTVC = [[MiniTVC alloc] initWithTableView:self.tableView array:someArray];
tableView.dataSource = self.miniTVC;
tableView.delegate = self.miniTVC;
You can repeat this process creating another UITableView + MiniTVC from inside a custom UITableViewCell. Or you could make the UITableViewCell conform to the table delegate/datasource and implement there the methods.
Btw, creating tableviews inside tableviews is a bit unusual. Since they are both UIViewScroll subclasses, it could lead to unexpected behaviour, tho I haven't tried myself.
I'm using a UITableViewController and want to add a searchbar.
My options are [self.tableview addSubview:searchBar] or self.tableview.tableHeaderView = searchBar
Both options will scroll the searchbar along the rest of the tableview, which I understand. But is there a way to lock elements up, instead of using a UIViewController or changing the frame origin on scroll?
I'm thinking of a way to get above the current view hierarchy and add a subview onto that.
I tried the opposite approach, to add a view to the superview and bring that to front
[[self.tableView superview] addSubview:searchBar];
[[self.tableView superview] bringSubviewToFront:searchBar];
}
but it didn't work.
UITableViewControllers are UIViewControllers that have their main view property being an UITableView. Thus this UITableView do takes all the screen.
The approach you are looking for is to use a standard UIViewController whose view is a standard UIView, and add your UISearchBar and the UITableView as a subview to it in IB.
To do this, simply:
Change the class of your view controller and instead mention that it conforms to the tableview-associated protocols : change #interface UITableViewController to #interface UIViewController <UITableViewDelegate, UITableViewDataSource>
Add a #property(nonatomic, retain) IBOutlet UITableView* tableView property to your header file, and the associated #synthesize tableView; statement in the implementation. Don't forget to set this property back to nil (self.tableView = nil) in your dealloc for good memory managment
Link your UITableView instance in InterfaceBuilder to this newly created IBOutlet.
Thus you still have a UITableView but this won't be the main view that takes all your screen; instead you can layout your tableView like you want and make it take only a part of your screen, and position the UISearchBar above it.
Another approach would be to use the UISearchDisplayController to manage your UISearchBar.
I'm working on an app that has three table view controllers in a navigation stack. The root view controller and the second VC have toolbars, but I want to add a subview to the second view controller like this. (The color is just there for visualization.)
I want to add the view programmatically, since I haven't been able to do it with IB without major headaches. Right now, I've been able to kind of get what I want by drawing a UIView in the second view controller like this:
- (void)viewDidLoad {
[super viewDidLoad]
UIView *detailView = [[UIView alloc] initWithFrame:CGRectMake(0, 392, 320, 44)];
detailView = [UIColor redColor];
[self.navigationController.view addSubview:detailView];
[detailView release];
}
The problem with this approach is that once the UIView is loaded in the second view controller, it stays loaded and is drawn in the third and root view controllers. I've tried a variety of methods of removing the UIView, including setting the detailView to nil in viewDidUnload, calling removeFromSuperview in didSelectRowAtIndexPath (which removed the view from the whole stack).
I've also tried adding the subview to self.view, but that pushes it below the visible area of the table view, so I have to scroll up to see it, and it snaps back down when I let go.
Clearly, adding this subview to the navigation controller is not the best way to do what I want, but I'm at a loss as to where to go from here.
As you've already discovered, you definitely should not be reaching up into the navigation controller's view.
You want your SecondViewController to be an UIViewController that implements the UITableViewDelegate and UITableViewDataSource and whose view lays out the UITableView and the UIView you wish to use for your stationary 'footer' in it's own main UIView.
It helps to keep in mind that UITableViewController is ultimately is just a convenience for creating a view controller whose view consists entirely of a UITableView.
Anyway, rather than attempt to put a pile of that code inline in this answer, you can browse it (or svn co) from this read-only svn repo.
EDITED (now that it's not midnight, putting some code/explanation directly in answer):
For the controller to be pushed onto the nav stack that needs the footer create a new UIViewController-based class (do NOT check the 'UITableViewController subclass' box in the template selection dialog).
Add instance variables for the UITableView and the UIView that is to be the extra bottom view.
#interface SecondViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView* tableView;
UIView* customFooterView;
}
#property (nonatomic,retain) IBOutlet UITableView* tableView;
#property (nonatomic,retain) IBOutlet UIView* customFooterView;
#end
In IB add a UITableView and UIView to the existing root view for the controller and lay them out as desired (probably worth altering the auto-resize parameters too if your app can be used in both landscape and portrait). Hook up the two views to the outlets defined for them in the "File's Owner" and also ensure you hook up the UITableView's delegate and dataSource properties to point at the "File's Owner."
Then just implement the UITableViewDelegate and UITableViewDataSource protocols as appropriate for your application.
If you want to lay out the entire 'footer' view in IB then go right ahead. Otherwise you can easily add items programmatically in viewDidLoad (and remember to tear it down in viewDidUnload).
I don't like the approach. You should put your table view inside another view, and put your detail view together in that view.
Despite of that, I think you can remove your view in viewWillDisappear method of your view controller. I also notice that you did not keep your detailView as a private variable, which you should do because you need to reference it when removing it later (I still wonder how you have done it.)
Note that viewDidUnload is called in case of view unloading (i.e. releasing from its controller), so it is not related to navigation.
Not sure which behavior you're looking for but try one of these:
Assign the detailView to the tableFooterView property of the tableview on the second VC.
Reduce the height of the table view and add the detailView to self.view.