In a master tableview-detail view controller app, after loading a detail view, I would like to let the user view the detail for a different item from the tableview if they wish.
To do this, I pass two objects to the detail view in prepareForSegue and use one of them to load the view.
If I want, I am able to change individual items on the screen one by one, for example, by changing self.title = item.title to self.title = altitem.title. My question, is, whether there is a way to reload the entire view using the alternative object?
I have read that setNeedsDisplay should reload the whole view, but it is not changing the object behind the view or at least the view on the screen Here is my code currently:
//in prepareForSegue method in master tableview
destViewController.item=item;;
destViewController.altItem=altitem;
//in .h file of detail view
#property (nonatomic, strong) Items *item;
#property (nonatomic, strong) Items *altitem;
// in .m file of detailView
in viewDidLoad
self.title = item.title;
//my method to change object - NOT DOING ANYTING
-(void) reloadWithAltItem {
self.item = self.altitem;
[self.view setNeedsDisplay];
}
Thanks for any suggestions.
Related
I started a project with storyboards and it has a lot of views, each one of them has the same header (with an image, company name and two buttons). I want to do this once in the main View, and make it reusable for the other views.
You could create a containing view controller with your header. Then, add your current root view controller (the current initial view controller in your storyboard) to this containing view controller.
Create a custom view named HeaderView with XIB:
in HeaderView.h you have to define a protocol and the properties related to controls you want to display in header.
#protocol HeaderViewDelegate <NSObject>
- (void)onBackButtonTapped;
#end
#interface HeaderView : UIView
#property (nonatomic, strong) IBOutlet UIImageView *headerImageView;
#property (nonatomic, weak) id <HeaderViewDelegate> delegate;
-(IBAction)backButtonTapped:(id)sender;
#end
In Header.m:
#implementation HeaderView
-(IBAction)backButtonTapped:(id)sender
{
if (delegate != nil && [delegate respondToSelector:#selector(onBackButtonTapped)])
{
[delegate onBackButtonTapped];
}
}
#end
Now what you have to do is to create a BaseViewController and every controller should be extended from this controller in order to show the same header in all the views.
Let me know you have any questions regarding the implementation.
I have managed to create reusable views according to this post.
Create a class named SharedView which extends UIView
Create a nib with the same name
Set the ‘File’s Owner’ property in the nib to the SharedView class
Create an outlet from your view to the SharedView class, and call it ‘contentView’
Replace initWithCoder with the following:
-(void)awakeFromNib {
//Note that you must change #”BNYSharedView’ with whatever your nib is named
[[NSBundle mainBundle] loadNibNamed:#"BNYSharedView" owner:self options:nil];
[self addSubview: self.contentView];
}
In your ViewControllers create views, and set their class to SharedView
Another solution is to use Container View Controller, and as to your specific situation, I have tried to implement a demo for you, please try it!
Hope it could help!
Why don't you use NavigationController as rootviewControoler with leftBarbutton and rightbarButton
so that it can be in your whole app
I have custom UIButton which programmatically interacts (triggers) with ViewController methods via protocol. But the behaviour of the button has to be dependent on the ViewController placed on. I do this to minimise amount of code in ViewControllers itself, as the button has to remain the same and bear the same functions (navigation).
Is there any way in UIButton's custom class to get the ViewController it is placed on?
I'd follow #rmaddy advice in a specific way, borrowing from the SDK's style
// MyCutomButton.h
#protocol MyCustomButtonDatasource;
#interface MyCustomButton : UIButton
#property(weak,nonatomic) IBOutlet id<MyCustomButtonDatasource>datasource;
// etc
#end
#protocol MyCustomButtonDatasource <NSObject>
#optional
- (NSString *)howShouldIBehave:(MyCustomButton *)button;
#end
Now the button can have it's datasource set in IB. View controllers that include it will need a little additional code (sorry, it's unavoidable in a good design). They will declare themselves as implementing MyCustomButtonDatasource.
When MyCustomButton needs to behave conditionally based on where it's placed, it can ask its datasource...
// MyCustomButton.m
NSString *string = #"defaultBehavior"; // per #RichardTopchiy's suggestion
if ([self.datasource respondsToSelector:#selector(howShouldIBehave:)])
string = [self.datasource howShouldIBehave:self];
// string is just made-up here, have it answer something simple (int, BOOL)
// that lets the button proceed with the right behavior. Don't ask for
// anything that relies on specific knowledge of how MyCustomButton
// is implemented
EDIT - To create the relationship, if you've decorated the property as an IBOutlet (as shown above), you should be able to setup the relationship in IB. Declare your view controller as implementing <MyCustomButtonDatasource>. Select your custom button, then the connections inspector, then drag to your view controller.
Alternatively, make the button itself an IBOutlet property in the view controller and, in viewDidLoad, do:
self.customButton.datasource = self;
The last way to do it is give your button a tag, say, 128, then:
MyCustomButton *customButton = (MyCustomButton *)[self.view viewWithTag:128];
self.customButton.datasource = self;
I am working on a split view controller app, i have two views, a detail view and a second view. When a value on my first view is selected. (say we select dog). my second view loads a few options on the left pane. Therefore from that left pane when i select Color. I want my uilabel to read."Dog is Black" or if I had selected cat from the first view, i want it to read Cat is yellow. when i select color.
So what i did is, in the didSelectRowAtIndexPath i have
if ([[animalArray objectAtIndex:indexPath.row]isEqualToString:#"dog"] )
{
secondView *detailList=[[secondView alloc]init];
[self.detailViewController.navigationController pushViewController:detailList animated:YES];
self.activeOption=#"dog";
//defined as #property (nonatomic, assign) NSString *activeOption; in .h file
}
Then in my secondView i have ( tried to extract the most important things
.h file
#property (nonatomic,retain) DetailViewController *detailViewController;
.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
if ([self. detailViewController.activeOption isEqualToString:#"dog"]) {
self.labelOne.text=#"The Dog is Black";
}
}
Now my problem is, when i click the row to load the second view (dog) i get this error.-[secondView activeOption]: unrecognized selector sent to instance .
Now i believe you get that when you call a method that doesn't exist, so while the method does exist, it doesn't exist in secondView but in the DetailView So i am at a lost as too why it is looking there for the method. even if i said self.detailviewController.
Thanks
I have custom table view cell and have made a custom class. How can I get reference to the table view controller from the cell?
I have this custom tableview cell class. In this I have a button which is connected with a tap event. On tap I get the event fine but I am looking to get a hold on the table view controller so I can show the action sheet on top of the table view.
#interface MTPurchasedCartItemCell : UITableViewCell
- (IBAction)onShareTap:(id)sender;
#end
The way I would do this would be to use a block event handler. In your MTPurchasedCartItemCell class add a property in the header file like so:
#property (nonatomic, copy) void (^tapHandler)(id sender);
And in the implementation file you can do this:
- (IBAction)onShareTap:(id)sender {
if (self.tapHandler) {
tapHandler(sender);
}
}
And finally in your controller class, do something like this in your cellForRowAtIndexPath::
...
cell.tapHandler = ^(id sender) {
// do something
}
...
You might also consider adding a public #property for the button to the custom cell.
To do that, add the following line to the header file of your custom cell class:
#property (weak, nonatomic) IBOutlet UIButton *myButton;
That's if you're creating the button in Interface Builder. If you're creating the button in code, you'd add this line instead:
#property (nonatomic) UIButton *myButton;
Then, when initializing or configuring the cell from your table view controller, you now have a reference to myButton and can add an action to it with your table view controller as the target.
Then, in the action method, you can get the cell from the button. The answer to Objective-C: How to generate one unique NSInteger from two NSIntegers? explains how.
I've done something very similar that gets the table reference from a custom cell using a button event.
Code:
-(IBAction)onShareTap:(UIButton *)sender { // i've used a button as input..
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *buttonCell = (UITableViewCell *)senderButton.superview.superview.superview;
UITableView* table = (UITableView *)[buttonCell superview];
NSLog(#"Table: %#", table);
}
I have a Split View Controller with the master view containing a Tab Bar Controller, whose four tabs are each controlled by separate "MasterViewController" objects, which delegate to the detail view:
The MasterViewController class is as follows:
#interface MasterViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar; // Not pictured above
#property (nonatomic, strong) NSMutableArray *displayData;
#property (nonatomic, strong) NSMutableArray *auditedData;
#property (strong, nonatomic) IBOutlet UITableView *tableView; // Each of the tabs contains a tableview
#property (strong, nonatomic) IBOutlet UITextField *itemCounter;
#end
and is initialized as follows:
MasterViewController *blahMasterController = (MasterViewController *)tabItem;
blahController.displayData = [[NSMutableArray alloc] init];
blahController.delegate = detailTableViewController;
blahDetailController.recallDelegate = blahController;
Functionality is such that when a user selects a table row in one of the tabs, the relevant data is displayed in the detail view controller. Then, when the user taps a button on the detail view controller, the entry in the master view tab is removed.
The former works well...the later is behaving strangely. Here is my "respondToDetailViewButton" method in the MasterViewController (the method delegated to by the detail controller):
- (void)respondToDetailViewButton
{
NSLog(#"Class: %#: %#", [self class], self);
NSLog(#"TableView: %#", self.displayMachinesTableView);
NSLog(#"Search Bar:%#", self.searchBar);
}
and its output:
Class: MasterViewController: /MasterViewController: 0x105b10a0/
TableView: /UITableView: 0xe960c00; frame = (0 0; 320 395);
clipsToBounds = YES; autoresize = TM+BM; tag = 1; gestureRecognizers =
; layer = /CALayer: 0x114b9950/; contentOffset:
{0, 0}/ Search Bar: (null)
From the above you can see that when the detail controller delegates back to its master, the searchBar reference, despite being strongly attributed, becomes null despite the references to MasterViewController's tableView remaining intact.
And the searchBar is verifiably non-nil at some point because in the MasterViewController object, the following call in didSelectRowAtIndexPath:
currentScope = [searchBar.scopeButtonTitles objectAtIndex:searchBar.selectedScopeButtonIndex];
NSLog(#"%#", currentScope);
renders "New" or "Old" or "Blah" depending on the scope I have selected.
Does anyone have an idea as to why the searchBar reference becomes null based on what I have provided? Many thanks in advance!!
Based on the comment conversation under the question one possibility is that inconsistent variable naming might be the problem. It looks like you might have "#synthesize idSearchBar = searchBar;" in your implementation so self.idSearchBar and searchBar are effectively the same thing.
The problem with this is a local variable called searchBar somewhere else in your class could overwrite your instance variable. So try this: Change the synthesize line to "#synthesize idSearchBar = _idSearchBar;" or remove the #synthesize line completely and let the compiler auto-generate that exact line for you (if using LLVM compiler 4.0). Change all searchBar usage that show up as an error to "self.idSearchBar" to be consistent throughout your class. Any leftover uses of searchBar will be local variables that should not be changed.
After that - do you still see the problem?