I am new to iOS development and also have a mentor, that helps me most of the time, but he isn't available at the moment.
So I am planning on doing an app from a JSON feed (easiest app idea I could come up with). But the page the JSON feed is about supports posts that either have an url or a comment (also the attributes in the JSON file). So if a comment is available, the post doesn't contain a url. But also the other way 'round: If a post contains a url, there is no comment in it.
What I want to do now is checking if the current post that is clicked on the UITableView has a comment or a url and then switching to the WebView or to a normal View.
How to do this one? Because I connected the UITableView with the View where the comment is displayed through Storyboard.
My current prepareForSegue looks like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSDictionary *story = stories[indexPath.row];
NSNumber *storyId = story[#"id"];
[[segue destinationViewController] setDetailItem:storyId];
}
}
Set up a datasource for your table view and then in the tableView:cellForRowAtIndexPath: method check if your post has a URL or comment. Based on which one it is you can create the correct view and add that view to the UITableViewCell's contentView.
Something like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Create your cell
UIView* contentView = nil;
if (<isURLPost>)
{
contentView = <CreateAndSetUpYourWebView>;
}
else
{
contentView = <CreateAndSetUpYourOtherView>;
}
[cell.contentView addSubView:contentView];
}
Related
- (void)viewDidLoad
{
//json parsing
for (NSDictionary *ResultDictionary in dataDictionary) {
ResultTabObject *ResultcurrenObjet = [[ResultTabObject alloc]initWithDate:[ResultDictionary objectForKey:#"MATCH_DATE"] ATeamName:[ResultDictionary objectForKey:#"COMPETITION_CODE"];
[self.ResultHolderArray addObject:ResultcurrenObjet];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
ResultTabCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
ResultTabObject *ResultcurrenObjet = [self.ResultHolderArray
objectAtIndex:indexPath.row];
cell.lblDate.text=ResultcurrenObjet.Date;
return cell;
}
-(NSMutableArray *)ResultHolderArray{
if(!_ResultHolderArray) _ResultHolderArray = [[NSMutableArray alloc]init];
return _ResultHolderArray;
}
I receive the dictionary values by using above code now I have to pass the ResultDictionary to another viewController
Thanks in Advance
Well, there are several ways of doing this, but an easy solution could be adding a ResultDictionary property on your target view controller and setting it just before the view controller changes.
Of you are creating the target view controller manually then just do it there. If the view controllers are created and connected through Interface Builder you can set the property in the - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// You can use the segue identifier to make sure that you have the correct segue. If there is only one possible segue from your source view controller you can skip this step.
if ([segue.identifier isEqualToString:#"your_segue_id"]) {
DestinationViewController *vc = [segue destinationViewController];
vc.resultTabObject = someResultTabObject; // get it from your array.
}
}
PS: Looking at your code it occurred to me that you might mean passing the ResultDictionary to the cell rather then an actual view controller. If this is the case then just add a ResultDictionary property to your ResultTabCell class and set it inside - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath.
Hello i am trying to pass variable with segue.
I am getting variable to pass with tableView:willSelectedRowAtIndexPath: is this correct way? If it is not, how should i achieve this? (Note: It is working like this.)
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedCoffeeShop = [coffeeShops objectAtIndex:indexPath.row];
return indexPath;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"coffeeShopDetailSegue"]) {
CoffeeShopDetailViewController *controller = (CoffeeShopDetailViewController *)segue.destinationViewController;
[segue destinationViewController];
controller.coffeeShop = selectedCoffeeShop;
}
}
If your segue is made from the cell itself, then there is no need to implement either willSelectRowAtIndexPath or didSelectRowAtIndexPath. You only need prepareForSegue:sender: since the sender argument will be the cell, and you can use that to get the indexPath you need,
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UITableViewCell *)sender {
if ([segue.identifier isEqualToString:#"coffeeShopDetailSegue"]) {
NSInteger row = [self.tableView indexPathForCell:sender].row;
CoffeeShopDetailViewController *controller = segue.destinationViewController;
controller.coffeeShop = coffeeShops[row];
}
}
That way to do it is absolutely fine.
Another way would be to remove the automatic segue trigger from storyboards and instead implement:
tableView:didSelectRowAtIndexPath: to call performSegueWithIdentifier:sender:.
It could look like this:
- (NSIndexPath *)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedCoffeeShop = [coffeeShops objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"coffeeShopDetailSegue" sender:self];
return indexPath;
}
In that case you still need your implementation of prepareForSegue:sender:.
You could also do it completely without segues, using UINavigationController, but then you'd have to instantiate the CoffeeShopDetailViewController programmatically as well.
Your approach is perfectly fine though!
As noted in the comments, you can remove [segue destinationViewController];, since this returns the destination view controller which you already saved in the variable controller in the line right above. :)
So this issue is similar to the one described here UITableView - IndexPath always returns 0 first & takes 2 clicks to pass correct data to view controller however the answer they suggested does not help to fix the problem so I'm asking specifically with my code. This is my prepareForSegue method:
//send to a detail version of each of the chatters allowing a report a user
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"pushToDetail"]){
ChatterDetailViewController *chatterDetail = [segue destinationViewController];
UITableViewCell *cell = (UITableViewCell*)sender;
long indexPathSelected = [self.tableView indexPathForCell:cell].row;
Chatter *detailChatter = [_arrayWithoutME objectAtIndex:indexPathSelected];
NSData *fbImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString: detailChatter.url]];
_detailProfile = [UIImage imageWithData:fbImageData];
chatterDetail.detailProfile = _detailProfile;
}
}
Which obviously looks different from the answer suggested on the other question, however I tried that and it did not change anything. Whenever I click on a tableView cell it completes the segue and I see the the profile picture from the first cell (index 0) and not the cell where it was clicked. I tried using
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
however it didn't pass the image value like I needed and also when I tried to return using an unwind segue it said the navigation tree was corrupted (doesn't happen with the current code). Basically I can't seem to find why selecting any cell returns only the first cell's information.
EDIT:
here is some code from Chatter Detail's view did load method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
_detailProfilePicture.contentMode = UIViewContentModeScaleAspectFill;
_detailProfilePicture.image = _detailProfile;
[_detailProfilePicture sizeToFit];
}
Again, it does return a picture, however it is only the picture from cell at index 0.
There is an easier way to extract the indexPath for your selected row in a UITableView:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"pushToDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// section and row return an int like this:
NSLog(#"index path: section %i, row %i", indexPath.section, indexPath.row);
}
}
However, your code does give the correct row value as long, and a long can indeed be used to get an object from an NSArray. I would put a breakpoint or log message at the end of this method and see if the correct value is actually in your array.
I would also check what value you get in your indexPathSelected - this should reflect the selected row.
I have two table views, one called mainTableViewController (mtvc), the other called detailTableViewController (dtvc). It's very typical click the accessory button on the main tableview cell bring you to the detail tableview kinda thing.
In the prepareForSegue method, the data passed from the main tableview to detail tableview is a NSMutableArray called item.
And this is how I got it displayed: cell.detailTextLabel.text = self.item[indexPath.row];
The cool thing is I managed to do in-place editing on the detail table view cell (overwrote the NSTableViewCell, added a UITextField as subview to each cell).
everything works, the last thing I spent whole day cannot figure out is how do I update the NSMutableArray item after in-place editing taken place, the ultimate goal is in-place editing, and the main tableview data shall reflect the change.
I tried to use delegation and protocol but it does not work (the in-place edited content didn't got passed back, part of the reason is I don't know how to capture the edited content, it's not like it's a text field with a name, I can't just do updatedContent = self.myTextField.text to grab the change)
I'm running out of ideas, any help would be highly appreciated, thanks.
Here's the prepareForSegue in the main tableview controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toInventoryDetail"]) {
NSMutableArray *selectedItem = nil;
if (self.searchDisplayController.active) {
selectedItem = _searchResults[[sender row]];
} else {
selectedItem = _appDelegate.items[[sender row]];
}
UPFInventoryDetailTableViewController *idtvc = segue.destinationViewController;
idtvc.item = selectedItem;
}
}
and here's the cellForRowAtIndex at the detail tableview controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UPFEditableUITableViewCell *cell = [[UPFEditableUITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier];
cell.textLabel.text = _appDelegate.title[indexPath.row];
cell.detailTextLabel.text = self.item[indexPath.row];
[cell showEditingField:YES];
return cell;
}
I wrote the delegation but delete them after cause they didn't work.
I had an idea, still using delegation and protocol obviously: when the 'done' button in the detail tableview hit, I go grab all the row contents and build a new array, using delegation to pass this new array back to the main tableview controller, add this new array into the model meanwhile delete the old one. The tricky thing is still HOW CAN I GRAB ALL THE CONTENTS in the detail tableview?
update:
Haha! I think solved half of the puzzle !
here's the solution for the detail tableview controller
- (IBAction)doneUpdate:(UIBarButtonItem *)sender {
[self.delegate addItem:[self newItem]];
}
- (NSMutableArray *)saveItem
{
NSMutableArray *newItem = [[NSMutableArray alloc] init];
NSArray *indexPathes = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in indexPathes) {
UPFEditableUITableViewCell *cell = (UPFEditableUITableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[newItem addObject:cell.editField.text];
}
return newItem;
}
and here's the main tableview controller
- (void)addItem:(NSArray *)item
{
//take the updated item then insert the items array as new item
[_appDelegate.items addObject:item];
//remove the selected item (the one being updated) from the items array
[_appDelegate.items removeObject:_appDelegate.selectedItem];
[self.tableView reloadData];
[self.navigationController popViewControllerAnimated:YES];
}
When you creating a cell - give tags to your UITextFields
You can collect data entered by its delegate methods - you can either make NSDictionary/ key value pairs or you can add it to NSArray.
- (void)textFieldDidEndEditing:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
OR
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
You can use Delegation/Protocol or store this values in NSUserDefault and get it back on mainViewController.
Do you have a separate data model class(classes) for your selectedItem? That would be the appropriate way to persist data between the two TableViewControllers. It can be Core Data or simply a NSMutableArray that lives in memory. The DetailViewController updates the item and saves the changes, then the mainTableViewController reloads the TableView (or even just the data backing the previously edited cell.
Perhaps even consider the Model-View-Controller-Store pattern promoted by BigNerdRanch.
Hey there I have been working on a transit app for some time and have been stuck with this issue for a while now.
I am using iOS 5 and a storyboard. Basically I have a UITableView that displays favorite bus stop locations, when a user selects a row I use:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
}
With the stop and route information of the cell the user chose I then prepare for a segue that corresponds to a segue on my storyboard, pushing a detail view controller that uses the stop name and route name to display times from a plist.
I am fairly new to Objective C and iOS so I am using a segue that my friend told me would work, however, it might be the problem. The segue looks like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
After the segue in my DetailViewController I grab the stop and route information in the view DidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
route = [selection objectForKey:#"route"];
stopName = [selection objectForKey:#"stop"];
NSLog(#"stopName: %#", stopName);
NSLog(#"routeName: %#", route);
}
Here is where my problems arise. When I run the simulator and click on an a cell in my table view, I am pushed to the DVC, however, the stopName and routeName are both null, so no information was sent or received. BUT, if I go back to the table and click another cell, the routeName and stopName are filled with the information that should have sent the first time I clicked a cell. If I continue this process it continues to send the information for the cell tapped previously, not currently.
So basically information is sending but only after I go through the segue twice. Obviously I want it to send the information and receive it the first time, but it is delayed and driving me nuts. I appreciate any help someone can give me as I have been searching the internet for days now trying to fix this issue, thank you so much in advance for any assistance!
prepareForSegue: is being called before didSelectRowAtIndexPath:. This is why the values you see always are lagging behind.
The better solution is to get the stopString and routeString values in your prepareForSegue: method (and not use didSelectRowForIndexPath: at all). The key to doing this is to realize that the sender parameter value being passed to prepareForSegue: is the UITableViewCell that was tapped. You can use the UITableView method indexPathForCell to get the cell's indexPath in your table, and then use that to look up the data in your favoriteItems array.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
Make sure that you are NOT connecting the segue to the next view controller to the tableView CELL directly. Connect to the whole UITableViewController / UIViewController (whichever you are using) and give a name, say "segueNameInStoryboard".
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
/* add this line */
[self performSegueWithIdentifier:#"segueNameInStoryboard" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueNameInStoryboard"])
{
UIViewController *nextViewController = segue.destinationViewController;
nextViewController.delegate = self;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:routeString, #"route", stopString, #"stop", nil];
nextViewController.selection = selection1;
}
}