I have this Segue here:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
//NSDate *object = self.objects[indexPath.row];
NSString *strPOIndex = [self.tableData[indexPath.row] valueForKey:#"POIndex"];
LHPurchaseOrderDetail *controller = (LHPurchaseOrderDetail *)[[segue destinationViewController] topViewController];
[controller setDetailItem:strPOIndex];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}
and what I am trying to do with it is pass strPOIndex to setDetailItem in my detail controller from my master controller.. but when I run this, I get an error:
-[LHPurchaseOrderMaster setDetailItem:]: unrecognized selector sent to instance 0x156cce80
I dont understand why this is happening, is it an issue with my storyboard? or my master controller or detail controller? Here is my Detail Controller:
.h:
#import <UIKit/UIKit.h>
#interface LHPurchaseOrderDetail : UIViewController
#property (strong, nonatomic) IBOutlet UINavigationBar *NavBar;
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
#end
.m:
#import "LHPurchaseOrderDetail.h"
#interface LHPurchaseOrderDetail ()
#end
#implementation LHPurchaseOrderDetail
- (void)setDetailItem:(id)newDetailItem {
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView {
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self configureView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Master Controller:
.h
#import <UIKit/UIKit.h>
#import "ShinobiDataSource.h"
#import "PopupGenerator.h"
#class LHPurchaseOrderDetail;
#interface LHPurchaseOrderMaster : UITableViewController<UIPopoverControllerDelegate, UIPickerViewDelegate>
#property (strong, nonatomic) IBOutlet UIButton *communityBtn;
#property (strong, nonatomic) IBOutlet UIButton *lotBtn;
#property (strong, nonatomic) IBOutlet UIButton *goBtn;
- (IBAction)communityBtnPressed:(id)sender;
- (IBAction)lotBtnPressed:(id)sender;
- (IBAction)goBtnPressed:(id)sender;
#property(nonatomic, retain) NSArray * tableData;
#property (strong, nonatomic) LHPurchaseOrderDetail *purchaseOrderController;
#end
Your error is this:
-[LHPurchaseOrderMaster setDetailItem:]: unrecognized selector sent to instance 0x156cce80
so it seems that somewhere in your LHPurchaseOrderMaster class you're trying to access and set the detailItem property as if it would be a part of LHPurchaseOrderMaster but because it doesn't exist there, you get an unrecognized selector error.
Edit
You should check for three things:
In Interface Builder check that the segue from LHPurchaseOrderMaster ViewController is to an UINavigationController that embeds the LHPurchaseOrderDetail ViewController as the first view controller in its stack.
Check the Class name returned by [segue destinationViewController]topViewController] like this:
id obj = [segue destinationViewController]topViewController];
NSLog(#"%#", NSStringFromClass([obj class]));
The class name should be LHPurchaseOrderDetail. If it's not, then you have a problem in your Storyboard where more than certainly you've connected the segue wrong.
Check your LHPurchaseOrderMaster class for any code that tries to access the "detailItem" property as if it would be part of this class.
It seems that the property you are trying to access is not accessible (wrong retrieved object).
Have you tried to use instead of
LHPurchaseOrderDetail *controller = (LHPurchaseOrderDetail *)[[segue destinationViewController] topViewController];
Something like
LHPurchaseOrderDetail *controller = (LHPurchaseOrderDetail *)[[segue destinationViewController] viewControllers][0];
I had sometimes the same your issue.
Set your detailItem not to NSString. Not to id. The problem is here,
self.detailDescriptionLabel.text = [self.detailItem description];
In configureView method change the code as follow,
- (void)configureView {
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = self.detailItem;
}
}
Don't forget to change this as well,
- (void)setDetailItem:(NSString *)newDetailItem {
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
Related
I've problems passing data to a new view controller.
I've two VC. In the "FirstVC" there is a table view with dynamic prototypes cells. The generic cell contains a label and a button and it's assigned to its own class "GenericCell". I want to pass to the SecondVC the text of the cell label when I press the button, but the app crashes with errors:
Unknown class _TtC13testTableView37SecondVC in Interface Builder file.
UIViewController setMyString:]: unrecognized selector sent to instance 0x7fa05e418110
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIViewController setMyString:]: unrecognized selector sent to instance 0x7fc8add05710
I tried:
To use the segue (with prepareForSegue method) outside the getText method
1a. Pushing the VC from the outside of the getText method
Implementing and calling set/get method of myString
Adding attributes to myString property
but the app crashes the same.
GenericCell.h
#import <UIKit/UIKit.h>
#protocol MyCellDelegate
-(void)getText:(NSString *)text;
#end
#interface GenericCell: UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel * label;
#property (assign, nonatomic) id <MyCellDelegate> delegate;
#end
GenericCell.m
#import "GenericCell.h"
#interface GenericCell()
#end
#implementation GenericCell
- (IBAction)buttonPressed {
if (self.delegate) {
[self.delegate getText:self.label.text];
}
}
#end
FirstVC.m
#import "FirstVC.h"
#import "GenericCell.h"
#import "SecondVC.h"
#interface FirstVC() <UITableViewDataSource,UITableViewDelegate, MyCellDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property NSString * idToPass;
#end
#implementation FirstVC
NSMutableArray<NSString *> *array;
- (void)viewDidLoad {
array = [[NSMutableArray alloc]initWithObjects:#"0x22", #"0x11", #"0x24", nil];
[super viewDidLoad];
self.tableView.dataSource=self;
self.tableView.delegate=self;
self.tableView.rowHeight=100;
}
-(void)getText:(NSString *)text {
self.idToPass = text;
SecondVC * secondVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondVC"];
secondVC.myString = self.idToPass;
[[self navigationController] pushViewController:secondVC animated:YES];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return array.count;
}
- (GenericCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
GenericCell * cell = [[self tableView]dequeueReusableCellWithIdentifier:#"GenericCell" forIndexPath:indexPath];
cell.delegate = self;
cell.label.text = [array objectAtIndex:indexPath.row];
return cell;
}
#end
SecondVC.h
#import <UIKit/UIKit.h>
#interface SecondVC : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *label;
#property NSString * myString;
#end
SecondVC.m
#import "SecondVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (void)viewDidLoad {
self.label.text = self.myString;
[super viewDidLoad];
}
#end
The "idToPass" is set correctly with delegation.
This work for me: in the buttonPressed method update the if statement like this
if ([self.delegate respondsToSelector:#selector(getText:)]){
[self.delegate getText:self.label.text];
}
Another option to give SecondVC the data is using
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondVC * vc = segue.destinationViewController;
if ([segue.identifier isEqualToString: #"yourSegue"]) {
vc.myString = self.idToPass;
}
}
A tip I suggest is to check (with a simple if statement) if your object in SecondVC is nil or !nil
I hope this help :)
In getText method you assign text to myProperty instead myString...
It should be:
secondVC.myString = self.idToPass;
I have two storyboards and each one has its own respective view controller but I need to change the appearance of the second storyboard based on the button pressed in the first view controller.
In the first view controller I have:
// First view controller .h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface FirstViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *LevelOneButton; // tag 0
#property (strong, nonatomic) IBOutlet UIButton *LevelTwoButton; // tag 1
-(IBAction)selectLevel:(UIButton *)sender; // both buttons connected to this method
#property (assign, nonatomic) int levelSelect;
#end
then in the first .m file:
//FirstViewController.m
-(IBAction)selectLevel:(UIButton *)sender {
if (sender.tag == 0) {
_levelSelect = 0;
}
if (sender.tag == 1) {
_levelSelect = 1;
}
}
This code works fine but the problem occurs in the secondViewController that I have. When I try and access the levelSelect property in the SecondViewController I get the errors "Property 'levelSelect' not found on object of type 'FirstViewController'" or "Unexpected identifier levelSelect" or something among those lines. I've tried every single thing I could think of and every question I found on StackOverflow relating to this but none have fixed the problem. Anyone know what I'm doing wrong?
You should be setting the property on the second view controller as you're pushing or segueing.
So in your first view controller it should look something like this:
#import "ViewController.h"
#import "SecondViewController.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIButton *levelOne;
#property (strong, nonatomic) IBOutlet UIButton *levelTwo;
#property (assign, nonatomic) int selectedLevel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.levelOne.tag = 1;
self.levelTwo.tag = 2;
}
- (IBAction)selectLevel:(UIButton *)sender
{
if (sender.tag == 1) {
self.selectedLevel = 1;
} else {
self.selectedLevel = 2;
}
[self performSegueWithIdentifier:#"pushToSecond" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *dest = segue.destinationViewController;
dest.levelSelect = self.selectedLevel;
}
#end
Now, when viewDidLoad gets called on the SecondViewController that property will be set and you can use it. Like so:
#import "SecondViewController.h"
#interface SecondViewController ()
#property (strong, nonatomic) IBOutlet UILabel *levelLabel;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.levelLabel.text = [#(self.levelSelect) stringValue];
}
#end
Quick Edit, if you're not using segues you can do the same thing by pushing manually. Would look something like:
- (IBAction)selectLevel:(UIButton *)sender
{
if (sender.tag == 1) {
self.selectedLevel = 1;
} else {
self.selectedLevel = 2;
}
SecondViewController *secondVC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"second"];
secondVC.levelSelect = self.selectedLevel;
[self.navigationController pushViewController:secondVC animated:YES];
}
I am trying to pass an image from my TableViewController to a DetailViewController. My problem is a logic problem i guess because Xcode doesn't show any error in my code. When i tap on any row and takes me to the DetailView, the DetailView shows nothing and i want the DetailView to show me the image i am passing.
My TableViewController.h
#import <UIKit/UIKit.h>
#import "CustomCellControllerCell.h"
#import "DetailViewController.h"
#interface ListViewController : UITableViewController
#property (nonatomic) NSArray *foodImgArray;
#property (nonatomic) NSArray *foodTitleArray;
#end
My TableViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.foodTitleArray = #[#"manito",
#"jatito",
#"dolorsito",
#"pajarito",
#"calalito",
#"chinguito"];
self.foodImgArray = #[#"ajigallina.jpg",
#"anticucho.jpg",
#"causaRellena.jpg",
#"anticucho.jpg",
#"anticucho.jpg",
#"anticucho.jpg"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"detailView"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
dvc.passedImage.image = [UIImage imageNamed:[self.foodImgArray objectAtIndex:indexPath.row]];
}
}
My DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *detailImage;
#property (nonatomic) UIImageView *passedImage;
#end
And my DetailViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.detailImage.image = self.passedImage.image;
}
why dont you just pass a image
like--
dvc.image=[UIImage imageNamed:[self.foodImgArray objectAtIndex:indexPath.row]];
and set that image to detailImage in DetailViewController like--
self.detailImage.image = self.image
try to put some break point to check if image in not nil in DetailViewController.
I looked at lots of threads about delegation but couldn't find why this isn't working
I have UINavigationController in storyboard in Xcode 5 and here is how the delegate is assigned:
in MasterViewController I have:
//
// MasterViewController.h
//
#protocol MyViewDelegate
#required
- (void) getBackContacts:(NSArray *)c andEmails:(NSArray *)e;
#end
#interface ListContactsViewController : UITableViewController <MyViewDelegate> {
}
#property (strong, nonatomic) NSString* myString;
#property (strong, nonatomic) ABContact* currentML;
#end
//
// MasterViewController.m
//
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
DetailtViewController *vc = [segue destinationViewController];
vc.delegate = self;
vc.currentML = _currentML;
vc.contacts = _contacts;
vc.emails = _emails;
}
- (void) getBackContacts:(NSArray *)c andEmails:(NSArray *)e
{
_contacts = [NSMutableArray arrayWithArray:c];
_emails = [NSMutableArray arrayWithArray:e];
}
for DetailViewController I have:
//
// DetailViewController.h
//
#protocol MyViewDelegate;
#interface SelectContactViewController : UITableViewController {
id< MyViewDelegate > delegate;
}
-(IBAction) save;
#property (nonatomic, strong) id< MyViewDelegate > delegate;
#property (strong, nonatomic) ABContact* currentML;
#property (strong, nonatomic) NSMutableArray* contacts;
#property (strong, nonatomic) NSMutableArray* emails;
//
// DetailViewController.m
//
- (IBAction) save
{
// *** this line compiles with error
[delegate getBackContacts:_contacts andEmails:_emails];
}
When I compile compiler says:
No known instance method for selector 'getBackContacts:andEmails:'
i've been trying to implement this protocol for several hours and it doesn't seem to work for some reason. Basically i have a split view which has a view controller and a table controller, one class holds these two together. The main class creates an instance of the table and runs perfectly, but if i select a cell i want the view controller to react. So i wanted to create a protocol for when a table cell is selected it will do something in the main class.
TableSplitViewController, this is the main class:
#interface TableSplitViewController : UIViewController <updateView>
{
ChildrenTableViewController *firstController;
IBOutlet UITableView *firstTable;
IBOutlet UITableViewCell *tablecell;
NSString *name;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) IBOutlet UILabel *childnamelabel;
#end
THis is the TableSplitViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowChildrenDetails"]) {
ChildrenDetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *myIndexPath = [firstController.tableView indexPathForSelectedRow];
detailViewController.childrenDetailModel = [[NSArray alloc]
initWithObjects: [firstController.childname objectAtIndex:[firstController.index row]], nil];
}
}
- (void) setNameLabel:(NSString *)sender
{
// self.name = sender;
NSLog(#"ran");
}
This is the ChildrenTableViewController.h:
#protocol updateView <NSObject>
#required
- (void) setNameLabel:(NSString *)sender;
#end
#interface ChildrenTableViewController : UITableViewController
{
NSIndexPath *index;
id <updateView> delegate1;
}
#property (nonatomic, strong) NSMutableArray *childname;
#property (nonatomic, strong) NSIndexPath *index;
#property (retain) id delegate1;
#end
This is the critical part of ChildrenTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self delegate1] setNameLabel:[self.childname objectAtIndex:[indexPath row]]];
NSLog(#"rannn");
As you can see in the last code i'm trying to call the method using the protocol function. It doesn't seem to work for some reason, i've put in NSLOG and it doesn't even run the setNameLabel method at all. :( Will appreciate any help offered :)
In the code above I cant see you setting the delegate as so:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Set up the delegate for the controller
[firstController setDelegate1:self];
// Do any additional setup after loading the view.
}
Also, the delegate property should usually be (weak) rather than (retain).