UITextView doesn't update text after segue - iphone

I'm trying to update a UITextView's text property, after a segue, which happens after a row of a table view is tapped.
here is my code
MasterViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"4 Choices"]) {
NSLog(#"prepareForSegue is called");
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
NSString *questionContent = [[selectedObject valueForKey:#"questionContent"]description];
self.questionContent = questionContent;
[segue.destinationViewController setTextViewWith:self.questionContent];
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath is called");
NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
NSNumber *questionID = [selectedObject valueForKey:#"qID"];
int i = [questionID intValue];
if (i == 1) {
[self performSegueWithIdentifier:#"4 Choices" sender:self];
}
else
{
[self performSegueWithIdentifier:#"5 Choices" sender:self];
}
}
DetailViewController.m
-(void)setTextViewWith:(NSString *)aText
{
self.questionText = aText;
NSLog(#"setTextViewWith is called");
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.questionView setText:self.questionText];
NSLog(#"viewWillAppear is called");
}
EDIT 1 : change the code to set the text at 'viewWillAppear'
EDIT 2 : add 'didSelectRowAtIndexPath'
Note : the segue was pointed from MasterViewController to DetailViewController (not from the
tableViewCell)
Thanks for your help : )

The text view may not be loaded at the prepareForSegue point. I've only just started with storyboards and have found that I had to set string properties on the destination view controller instead, then update the UI components in viewDidLoad or viewWillAppear.

Related

Passing data between two controllers using storyboards

I am trying to pass data from one UITableViewController to another. This is my code in the initial view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Subject *subject = (Subject *)[self.fetchedResultsController objectAtIndexPath:indexPath];
[self showList:subject animated:YES];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)showList:(Subject *)subject animated:(BOOL)animated {
ListsViewController *lists = [[ListsViewController alloc] initWithStyle:UITableViewStyleGrouped];
lists.subject = subject;
NSLog(#"%#", lists.subject);
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
The log output was showing that it had the data I wanted had been passed over. However when I perform the segue and log the subject in the ListsViewController is shows null.
Any ideas?
You need to overwrite prepareForSegue:sender: method. The quick fix would be
- (void)showList:(Subject *)subject animated:(BOOL)animated
{
[self performSegueWithIdentifier:#"showDetail" sender:subject];
}
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetail"]) {
ListsViewController *controller = ([segue.destinationViewController isKindOfClass:[ListsViewController class]]) ? segue.destinationViewController : nil;
controller.subject = ([sender isKindOfClass:[Subject class]]) ? subject : nil;
}
}
The reason why your code did not work is that in your showList:animated: method you created a ListsViewController instance and assigned it a subject, but this view controller was never presented. Instead performSegueWithIdentifier:sender creates another instance of your ListsViewController class which knows nothing about your subject. That's why you need to wait for UIStoryboardSegue to instantiate a destination view controller from the storyboard and then configure it the way you want, which you can do in prepareForSegue:sender: method.
Also it might be not the best idea to use subject as a sender in performSegueWithIdentifier:sender method, because it's just not the sender :). What I would do is create a property subject in your view controller class and use it prepareForSegue:sender:
#interface MyViewController ()
#property (strong, nonatomic) Subject *subject;
#end
#implementation MyViewController
- (void)showList:(Subject *)subject animated:(BOOL)animated
{
self.subject = subject;
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetail"]) {
ListsViewController *controller = ([segue.destinationViewController isKindOfClass:[ListsViewController class]]) ? segue.destinationViewController : nil;
controller.subject = self.subject;
}
}
...
#end
Implement prepareForSegue and pass date in this methos
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([seque.identifier isEqualToString:#"showDetail"])
{
ListsViewController *lists = seque.destinationViewController;
lists.subject = subject;
}
}
That's good, but now you need to add this:
First instead of:
[self performSegueWithIdentifier:#"showDetail" sender:self];
You need to send the object:
[self performSegueWithIdentifier:#"showDetail" sender:subject];
Add a property in your ListsViewController.h:
#property (nonatomic, strong) Subject * subjectSegue;
And now in your first view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetail"]) {
ListsViewController * lists = (ListsViewController *)[segue destinationViewController];
lists.subjectSegue = sender;
}
You need to understand that performSegueWithIdentifier:sender: creates a new instance of view controller. So the ListsViewController that you have created is not the being displayed on the screen.
You need to override `prepareForSegue:sender:
- (void)showList:(Subject *)subject animated:(BOOL)animated
{
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetail"]) {
ListsViewController *controller = (ListsViewController *)segue.destinationViewController;
controller.subject = self.subject;
}

How to make SearchBar in tableview work?

So i am trying to make an application that displays names of people in a tableview and on tap moves to the next view controller that shows an image of the person.
However when i add the search bar on the table view; i dont seem to have it right.
What am i doing wrong here?
The code compiles and displays on the simulator but when i click on any of the buttons, it gives me the errors i hate the most (Thread 1 : signal SIGABRT)
Here is my code for the Table View Controller
#import "PhotoTableViewController.h"
#import "Photo.h"
#import "DisplayViewController.h"
#interface PhotoTableViewController ()
#end
#implementation PhotoTableViewController
#synthesize photoSearchBar, showPhotos, filteredPhotos;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
showPhotos = [NSArray arrayWithObjects:
[Photo photoofname:#"Main" filename:#"photo1.jpg" notes:#"Amazing Road Bike"],
[Photo photoofname:#"Back" filename:#"photo3.jpg" notes:#"this is the back"], nil];
[self.tableView reloadData];
self.filteredPhotos = [NSMutableArray arrayWithCapacity:[showPhotos count]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [filteredPhotos count];
} else {
return [showPhotos count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Photo *photo = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
photo = [filteredPhotos objectAtIndex:indexPath.row];
}else
{
photo = [showPhotos objectAtIndex:indexPath.row];
}
cell.textLabel.text = photo.name;
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredPhotos removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
filteredPhotos = [NSMutableArray arrayWithArray:[showPhotos filteredArrayUsingPredicate:predicate]];
}
#pragma mark - UISearchDisplayController Delegate Methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// Tells the table data source to reload when text changes
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
// Tells the table data source to reload when scope bar selection changes
[self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#pragma mark - TableView Delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Perform segue to candy detail
[self performSegueWithIdentifier:#"candyDetail" sender:tableView];
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"photoDetail"]) {
UIViewController *candyDetailViewController = [segue destinationViewController];
// In order to manipulate the destination view controller, another check on which table (search or normal) is displayed is needed
if(sender == self.searchDisplayController.searchResultsTableView) {
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
NSString *destinationTitle = [[filteredPhotos objectAtIndex:[indexPath row]] name];
[candyDetailViewController setTitle:destinationTitle];
}
else {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *destinationTitle = [[showPhotos objectAtIndex:[indexPath row]] name];
[candyDetailViewController setTitle:destinationTitle];
}
}
}
Also this is the code for my Objective C Class called Photo
#import "Photo.h"
#implementation Photo
#synthesize name,filename,notes;
+(id) photoofname: (NSString*)name filename:(NSString*)filename notes:(NSString*)notes{
Photo *newPhoto = [[Photo alloc]init];
newPhoto.name = name;
newPhoto.filename = filename;
newPhoto.notes = notes;
return newPhoto;
}
#end
Well, just by looking at the code what I can suggest you is, first remove that call to prepareForSegue method called in UITableView's delegate method, didSelectForRowAtIndexPath.
You are overriding prepareForSegue, so in your storyboard you should have a prototype cell from where you have to ctrl-drag to the destination controller and segue it accordingly. That's a basic concept. Still having problem? Let us see your console messages when it crashes.

Finding objectForKey in search results

I have a list of NSMutableArrays with NSMutableDictionaries in with objectsandkeys in this. like:
[silestoneArray addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Zen: Unsui", #"name",
#"silestone-unsui1.jpg", #"image", nil]];
I have a working search but I am now trying to segue to a detail view. I need to find a way of calling up the objects and keys from the search results so that when I segue from the search results the correct data is passed on.
My didselectrowatindexpath and prepareforsegue look like this:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableView == self.mainTableView){
[self.detailViewController setDetailItem:[[contentsList objectAtIndex:indexPath.section]
objectAtIndex: indexPath.row]];}
else
[self.detailViewController setDetailItem:[[contentsList objectAtIndex:indexPath.section]
objectAtIndex: indexPath.row]];
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"detailview"]) {
UIViewController *detailViewController = [segue destinationViewController];
// In order to manipulate the destination view controller, another check on which table (search or normal) is displayed is needed
if(sender == self.searchDisplayController.searchResultsTableView) {
detailViewController = self.detailViewController=segue.destinationViewController;
}
else {NSLog(#"Sender: %#", [sender class]);
self.detailViewController=segue.destinationViewController;
}
}
}
and this passes to a detailview which call forth the data like so:
- (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.navigationItem.title = [self.detailItem objectForKey:#"name"];
[self.detailImage setImage:[UIImage imageNamed:self.detailItem[#"image"]]];
}
}
The problem I have at the moment is when I segue from the results it will send the results from my original table so no matter what the cell of the results says the image and title will correspond with the order of the unfiltered results.
I know this is because I am calling on contentsList for both in didselectrow but when I try with search results it crashes.
the error report is: -[__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x21ed8
and under exemption breakpoints it says it is being thrown at this point:
self.navigationItem.title = [self.detailItem objectForKey:#"name"];
in this section of the detail view:
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.navigationItem.title = [self.detailItem objectForKey:#"name"];
[self.detailImage setImage:[UIImage imageNamed:self.detailItem[#"image"]]];
}
}
Thanks for any help.

How do I change a UILabel's text on a pushed (with segue) view, relative to the senders selected accessory?

I have a UITableView that pushes, via a storyboard segue, a view, which displays a UILabel that I wish to change the text on relative to the indexPath.row of the selected accessory on the UITableView.
I know it's probably wildly wrong, but this was my attempt. I feel like I'm going about it very wrong:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"ArticlePreviewSegue" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [sender indexPathForSelectedRow];
ArticlePreviewViewController *apvc = [segue destinationViewController];
NSDictionary *article = [_newsFetcher.articles objectAtIndex:indexPath.row];
apvc.titleLabel.text = [article objectForKey:#"title"];
apvc.bodyLabel.text = [article objectForKey:#"body"];
}
Thanks you!
One problem may be that tapping the accessory doesn't select the row. You can handle that by passing the index path as the sender of the segue:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"ArticlePreviewSegue" sender:indexPath];
}
Now you can access the index path in prepareForSegue:sender: without relying on the row being selected.
Another problem is that in prepareForSegue:sender:, apvc hasn't loaded its view yet. So apvc.titleLabel and apvc.bodyLabel are both nil.
The proper way to handle this is to give ArticlePreviewViewController an article property and set that property in prepareForSegue:sender:, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = (NSIndexPath *)sender;
ArticlePreviewViewController *apvc = [segue destinationViewController];
apvc.article = [_newsFetcher.articles objectAtIndex:indexPath.row];
}
Then, in -[ArticlePreviewViewController viewDidLoad], you can set the labels based on the article:
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = self.article[#"title"];
self.bodyLabel.text = self.article[#"body"];
}

update UILabel with the content of UICell from a different view

I have an NSArray tablePeople which makes up my UITableView on my 1st View Controller PeopleController. I have a UILabel personsName on my second View Controller PeopleDetailsController which I want to update with the contents of cell.textLabel.text of each row in my TableView. I have this method but it's not working:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((PeopleController *)segue.destinationViewController).delegate=self;
if ([[segue identifier] isEqualToString:#"toPeopleArticle"]) {
NSIndexPath *indexPath = (NSIndexPath *)sender;
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsName.text = [self.tablePeople objectAtIndex: indexPath.row];
}
}
I also have this code when the cell is selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toPeopleArticle" sender:indexPath];
}
I have to note that PeopleDetailsController is a modal view and PeopleController is a navigation view controller.
EDIT:
The text on the UILabel on the 2nd VC is just not being updated, it stays the same, that's the whole problem.
Change the following
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsName.text = [self.tablePeople objectAtIndex: indexPath.row];
to
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsNameString = [self.tablePeople objectAtIndex: indexPath.row];
where personsNameString is a property of type NSString in the PeopleDetailsController
Now in PeopleDetailsController viewDidLoad or viewWillAppear Function set the value of the label to the value of the property
mdvc.personsName.text = personsNameString;