pushViewController in Xcode - objective-c

I'm trying to push a view from a table cell to another view, I know that there's a lot of tutorials out there, I've tried a lot of them, been trying for 2 days and still can't get it working, here's my code..
AppDelegate.h
#import <UIKit/UIKit.h>
#class RootViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) RootViewController *rootViewController;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "RootViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize rootViewController;
- (void)dealloc
{
[_window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.rootViewController = [[[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil] autorelease];
self.window.rootViewController = self.rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
#end
RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController{
NSMutableArray* array;
}
#property(nonatomic, retain)NSMutableArray* array;
#end
RootViewController.m
#import "RootViewController.h"
#import "SubLevelViewController.h"
#implementation RootViewController
#synthesize array;
- (void)viewDidLoad {
[super viewDidLoad];
array = [[NSMutableArray alloc] init];
[array addObject:#"One"];
[array addObject:#"Two"];
[array addObject:#"Three"];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release anything that can be recreated in viewDidLoad or on demand.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
// Configure the cell.
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SubLevelViewController *sub = [[SubLevelViewController alloc] initWithNibName:#"SubLevelViewController" bundle:nil];
sub.title = #"My First View";
[self.navigationController pushViewController:sub animated:YES];
}
- (void)dealloc {
[array release];
[super dealloc];
}
#end
and main.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

Actually, your problem is simple, you reference self.navigationController, yet the view controller has no navigation controller set up in the AppDelegate! You're sending a message to nil, which produces nil (ergo, nothing happens). Try this in the appDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.rootViewController = [[[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil] autorelease];
UINavigationController * controller = [[[UINavigationController alloc]initWithRootViewController:self.rootViewController]autorelease];
    self.window.rootViewController = controller;
    [self.window makeKeyAndVisible];
return YES:
}

Agree with CodaFi, you need to create a UINavigationController and remember it to push and pop any UIViewController if you want to keep the push/pop stack. Do not use self.navigationController.

Related

Xcode Xib - Black border right and bottom of iOS Simulator

I'm working with Xcode 8.2.1 and I'm getting these back borders on the right and bottom sides of the device whenever I set the view as a smaller device in the XIB file. There is also the problem where whenever I select iPhone 7 Plus and then run on a iPhone 7 that part of the interface is pushed off the right and bottom of the screen. Everything is currently coded in Objective-C.
Update:
Main screen.
Code:
RootViewController
#import "RootViewController.h"
#import "AppDelegate.h"
#import "DetailViewController.h"
- (void)viewDidLoad {
[super viewDidLoad];
if(CurrentLevel == 0) {
NSArray *tempArray = [[NSArray alloc] init];
self.tableDataSource = tempArray;
[tempArray release];
AppDelegate *AppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
self.tableDataSource = (AppDelegate.data)[#"Rows"];
self.navigationItem.title = #"Title";
}
else
self.navigationItem.title = CurrentTitle;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *dictionary = (self.tableDataSource)[indexPath.row];
NSArray *Children = dictionary[#"Children"];
if(Children.count == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
dvController.CurrentTitle = dictionary[#"Title"];
[dvController release];
}
else {
RootViewController *rvController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
rvController.CurrentLevel += 1;
rvController.CurrentTitle = dictionary[#"Title"];
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
[rvController release];
}
}
AppDelegate
#import "AppDelegate.h"
#import "RootViewController.h"
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSString *Path = [NSBundle mainBundle].bundlePath;
NSString *DataPath = [Path stringByAppendingPathComponent:#"Master.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
self.data = tempDict;
[tempDict release];
[window setRootViewController:navigationController];
[window makeKeyAndVisible];
}
DetailViewController
#import "DetailViewController.h"
#import "AppDelegate.h"
#implementation DetailViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
Having "constraints set everywhere you can" is probably not what you want. It looks like you have set an explicit width and height. Remove all your current constraints and instead set a top, bottom, leading, and trailing constraint with a value of 0 and uncheck 'constrain to margins' (if you want the tableview to fill the view).
EDIT:
After some discussion in chat, I've realize that this has nothing to do with AutoLayout constraints. Rather, since you are using a XIB for your main window, it is being sized explicitly to the size set in the XIB. All you need to do to have it be sized correctly is tell it what the size of your screen is. Anywhere in applicationDidFinishLaunching:, just add [window setFrame:UIScreen.mainScreen.bounds]; and it will resize to the size of the device's screen.

Detail View from TableView, Parsing

Can someone please help me, I looked everywhere to figure this out and nothing worked so far. I need to pass some data from table view to detail view and stick it into labels and Uiimage.
Data tableview is pulling comes from Parse database I created and seems to get pulled fine into the Tableview but I would like to use the same array that tableview is using for its data to fill out the detail view.
I am using 2 columns from parse to fill out this tableview Title and sub, and another tow columns to fill out the label and image. Here is my code so far. There is a bunch of variables that i was using in this code in DetailView
.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface BooksTableViewController : UITableViewController <UITableViewDelegate,NSObject >
{
NSArray * Booksarray;
}
#property (strong, nonatomic) IBOutlet UITableView *bookstableview;
#end
.m
#import "BooksTableViewController.h"
#import "BookDetailViewController.h"
#interface BooksTableViewController ()
#end
#implementation BooksTableViewController
#synthesize bookstableview;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(RetrieveDatafromParse)];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
-(void) RetrieveDatafromParse {
PFQuery * getbooks = [PFQuery queryWithClassName:#"BooksTableView"];
[getbooks findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
Booksarray =[[NSArray alloc] initWithArray: objects];
}
[bookstableview reloadData];
NSLog(#"%#",objects);
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return Booksarray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"Cell";
UITableViewCell * cell = [bookstableview dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell ==nil) {
cell = [[ UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
PFObject * tempObject = [Booksarray objectAtIndex:indexPath.row];
cell.textLabel.text = [tempObject objectForKey:#"Books"];
cell.detailTextLabel.text= [tempObject objectForKey:#"Code"];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
BookDetailViewController * detailVC=[[BookDetailViewController alloc] initWithNibName:#"BookDetailViewController" bundle:nil];
detailVC.BookImage.image=[Booksarray objectAtIndex:indexPath.row];
detailVC.bookDesc.text=[Booksarray objectAtIndex:indexPath.row];
detailVC.bookTitle.text=[Booksarray objectAtIndex:indexPath.row];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController isKindOfClass: [BookDetailViewController class]]) {
BookDetailViewController *destination = segue.destinationViewController;
SEL selector = NSSelectorFromString(#"SetFile:");
if ([destination respondsToSelector:selector]) {
NSIndexPath *indexPath = [self.bookstableview indexPathForCell:sender];
PFObject * object = [Booksarray objectAtIndex:indexPath.row];
PFFile *file = [object objectForKey:#"BooksTableView"];
[destination setValue:file forKey:#"file"];
}
}
}
#end
.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface BookDetailViewController : UIViewController <NSObject> {
}
#property (weak, nonatomic) IBOutlet UIImageView *BookImage;
#property (weak, nonatomic) IBOutlet UILabel *bookTitle;
#property (weak, nonatomic) IBOutlet UILabel *bookDesc;
#property (weak,nonatomic)PFFile *file;
#end
.m
#import "BookDetailViewController.h"
#import "BooksTableViewController.h"
#interface BookDetailViewController ()
#implementation BookDetailViewController
#synthesize BookImage,bookTitle,bookDesc,file,bookInfo,Picture,object2;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(RetrieveObjectsFromParse)];
self.bookTitle.text = [self.file objectForKey:#"Books"];
self.BookImage.image = [self.file objectForKey:#"BookImage"];
self.bookDesc.text =[self.file objectForKey:#"BookDetails"];
}
-(void)RetrieveObjectsFromParse {
PFQuery * GetObjects = [PFQuery queryWithClassName:#"BooksTableView"];
[GetObjects findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
details =[[NSArray alloc] initWithArray: objects];
};
NSLog(#"%#",objects);
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Try this:
#1 create a segue from controller to controller:
#2 Give your segue an Id for example detailSegue.
#3 Perform the segue in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"detailSegue" sender:sender];
}
#4 Implement the segue delegate:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if([segue.identifier isEqualToString:#"detailSegue"]){
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
BookDetailViewController *detailVC = (BookDetailViewController *)segue.destinationViewController;
detailVC.bookDesc.text=[Booksarray objectAtIndex:indexPath.row];
//I uncommented that because it looks like a typo, same value 3 times?
//detailVC.BookImage.image=[Booksarray objectAtIndex:indexPath.row];
//detailVC.bookTitle.text=[Booksarray objectAtIndex:indexPath.row];
}
}
If this is the log you get:
2014-03-21 15:06:20.151 BookStore[25539:90b]
BookIndex= { BookDetails = "Test
test"; BookImage = ""; Books = Languages; Code =
104; }
Then you need to do it like this instead:
detailVC.bookTitle.text=[[Booksarray objectAtIndex:indexPath.row]objectForKey:#"Books"];
detailVC.bookDesc.text= [[Booksarray objectAtIndex:indexPath.row]objectForKey:#"BookDetails"];
detailVC.BookImage.image=[[Booksarray objectAtIndex:indexPath.row]objectForKey:#"BookImage"];
Or to make it shorter:
NSArray *bookAtIndex = [Booksarray objectAtIndex:indexPath.row];
detailVC.bookTitle.text=[bookAtIndex objectForKey:#"Books"];
detailVC.bookDesc.text= [bookAtIndex objectForKey:#"BookDetails"];
detailVC.BookImage.image=[bookAtIndex objectForKey:#"BookImage"];
or even shorter
NSArray *bookAtIndex = Booksarray[indexPath.row];
detailVC.bookTitle.text= bookAtIndex[#"Books"];
detailVC.bookDesc.text= bookAtIndex[#"BookDetails"];
detailVC.BookImage.image= bookAtIndex[#"BookImage"];

filterContentForSearchText and shouldReloadTableForSearchString not called (UITableView & UISearchBar)

I'd like to implement an incremental search of words.
I implemented like below, but filterContentForSearchText and shouldReloadTableForSearchString methods are not called. Why?
WordsIndexViewController.h
#import <UIKit/UIKit.h>
#interface WordsIndexViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>
#end
WordsIndexViewController.m
#import "WordsIndexViewController.h"
#import "WordsShowViewController.h"
#import "Word.h"
#interface WordsIndexViewController ()
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) NSArray *words;
#property (strong, nonatomic) NSMutableArray *searchedWords;
#end
#implementation WordsIndexViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_words = [[app models] objectForKey:#"words"];
_searchedWords = [NSMutableArray arrayWithArray:#[]];
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480) style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
_tableView = tableView;
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
searchBar.delegate = self; // needed?
[searchBar sizeToFit]; // needed?
UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDelegate = self;
searchDisplayController.searchResultsDataSource = self;
_tableView.tableHeaderView = searchBar;
[self.view addSubview:_tableView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - related to UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"START numberOfRowsInSection");
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
return [words count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"START cellForRowAtIndexPath");
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
UITableViewCell *cell = [[UITableViewCell alloc] init];
Word *word = words[indexPath.row];
cell.textLabel.text = word.spelling;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *words = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
words = _searchedWords;
} else {
words = _words;
}
WordsShowViewController *controller = [[WordsShowViewController alloc] initWithNibName:#"ViewController" bundle:nil];
controller.word = (Word *)words[indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
#pragma mark - related to Search Bar
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
NSLog(#"Query: %#", searchString); // NOT CALLED...
[self filterContentForSearchText:searchString
scope:[
[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]
]
];
return YES;
}
- (void)filterContentForSearchText:(NSString*)searchString scope:(NSString*)scope {
NSLog(#"START filterContentForSearchText"); // NOT CALLED...
[_searchedWords removeAllObjects];
for(Word *word in _words) {
NSRange range = [word.spelling rangeOfString:searchString options:NSCaseInsensitiveSearch];
if(range.length > 0) {
[_searchedWords addObject:word];
}
}
}
#end
Word.h
#import <Foundation/Foundation.h>
#interface Word : NSObject
#property (strong, nonatomic) NSString *identifier;
#property (strong, nonatomic) NSString *spelling;
- (id)initWith:(NSString *)spelling;
#end
Word.m
#import "Word.h"
#implementation Word
- (id)initWith:(NSString *)spelling {
self = [super init];
if (!self) {
return nil;
}
CFUUIDRef uuid = CFUUIDCreate(NULL);
_identifier = (NSString *)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
CFRelease(uuid);
_spelling = spelling;
return self;
}
#end
Regards,
Finally, I solved the question myself.
To solve it, I called shouldReloadTableForSearchString in textDidChange then the incremental search worked. A diff belows:
WordsIndexViewController.m
## -16,6 +16,7 ##
#property (strong, nonatomic) NSArray *words;
#property (strong, nonatomic) NSMutableArray *searchedWords;
+#property (strong, nonatomic) UISearchDisplayController *displayController;
#end
## -52,6 +53,7 ##
searchDisplayController.searchResultsDelegate = self;
searchDisplayController.searchResultsDataSource = self;
_tableView.tableHeaderView = searchBar;
+ _displayController = searchDisplayController;
[self.view addSubview:_tableView];
}
## -132,5 +134,9 ##
}
}
}
+- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *) searchText {
+ [self searchDisplayController:_displayController shouldReloadTableForSearchString:searchText];
+}
#end
Thanks,
Make sure you create an iVar for the UISearchDisplayController in your header file.
If you create an UISearchDisplayController using:
UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
it will get released by ARC, it will not call any delegate methods and when you'll call self.searchDisplayController (the UIViewController's property) it will be nil.
So, the fix is:
In your header (.h) file:
#interface MenuViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
UISearchDisplayController* searchDisplayController;
UISearchBar *searchField;
UITableView* tableView;
NSArray* searchResults;
}
and in the implementation (.m) file:
searchField = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 49)];
searchField.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchField contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
tableView.tableHeaderView = searchField;
tableView.contentOffset = CGPointMake(0, searchField.frame.size.height);
When implemented like that, you can call both self.searchDisplayController and searchDisplayController in the rest of your code.

Create a UIPageViewController with a UITableView in each page

I have been reading things on stack overflow for quite a while now but this is my first post, only because it is the first time I have a problem that no one else seems to have fixed yet!
Ok, down to business. It should be a simple matter to put UITableViews inside a UIPageView but I am having difficulties. I have a ViewController and contentViewController. I am using .xibs instead of storyboarding. The contentViewController.xib is a Table View and the ViewController.xib is a View. I am only focusing on iPhone. The UITableView is connected to dataSource, delegate, and Referencing Outlet named theTableView.
The project builds but when I run it I get the following error message:
2013-03-17 16:14:23.026 pageApp[775:c07] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2380.17/UIView.m:5776
2013-03-17 16:14:23.028 pageApp[775:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
*** First throw call stack:
(0x1c93012 0x10d0e7e 0x1c92e78 0xb66665 0x6539f 0x10e46b0 0x228ffc0 0x228433c 0x228feaf 0x1042bd 0x4cb56 0x4b66f 0x4b589 0x4a7e4 0x4a61e 0x4b3d9 0x4e2d2 0xf899c 0x45574 0x4576f 0x45905 0x4e917 0x20eb 0x12157 0x12747 0x1394b 0x24cb5 0x25beb 0x17698 0x1beedf9 0x1beead0 0x1c08bf5 0x1c08962 0x1c39bb6 0x1c38f44 0x1c38e1b 0x1317a 0x14ffc 0x1d2d 0x1c55)
libc++abi.dylib: terminate called throwing an exception
This crashes after -(void)viewDidLoad{} in ViewController.m and I have not yet learned how to fix auto layout/ layoutSubview errors. Does anyone else know how?
I have limited experience with ios development so I am sure that I just don't have the right pieces in the right spots. I used http://www.techotopia.com/index.php/An_Example_iOS_5_iPhone_UIPageViewController_Application to get this far.
My code is as follows:
ViewController.h
#import <UIKit/UIKit.h>
#import "contentViewController.h"
#interface ViewController : UIViewController
<UIPageViewControllerDataSource>
{
UIPageViewController *pageController;
NSArray *pageContent;
}
#property (strong, nonatomic) UIPageViewController *pageController;
#property (strong, nonatomic) NSArray *pageContent;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize pageController, pageContent;
- (contentViewController *)viewControllerAtIndex:(NSUInteger)index
{
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
return nil;
}
// Create a new view controller and pass suitable data.
contentViewController *dataViewController =[[contentViewController alloc]initWithNibName:#"contentViewController"bundle:nil];
dataViewController.dataObject =[self.pageContent objectAtIndex:index];
return dataViewController;
}
- (NSUInteger)indexOfViewController:(contentViewController *)viewController
{
return [self.pageContent indexOfObject:viewController.dataObject];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(contentViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(contentViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageContent count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (void) createContentPages
{
NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
for (int i = 1; i < 4; i++)
{
NSString *contentString = [[NSString alloc]initWithFormat:#"Chapter %d \nThis is the page %d of content displayed using UIPageViewController in iOS 5.", i, i];
[pageStrings addObject:contentString];
}
pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
self.pageController = [[UIPageViewController alloc]initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
pageController.dataSource = self;
[[pageController view] setFrame:[[self view] bounds]];
contentViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:pageController];
[[self view] addSubview:[pageController view]];
[pageController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
contentViewController.h
#import <UIKit/UIKit.h>
#interface contentViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) id dataObject;
#property (strong, nonatomic) NSArray *pageContent;
#end
contentViewController.m
#import "contentViewController.h"
#interface contentViewController ()
#end
#implementation contentViewController
#synthesize theTableView, dataObject, pageContent;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) createContentPages
{
NSMutableArray *pageStrings = [[NSMutableArray alloc] init];
for (int i = 1; i < 4; i++)
{
NSString *contentString = [[NSString alloc]initWithFormat:#"Chapter %d \nThis is the page %d of content displayed using UIPageViewController in iOS 5.", i, i];
[pageStrings addObject:contentString];
}
pageContent = [[NSArray alloc] initWithArray:pageStrings];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createContentPages];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection (NSInteger)section
{
return 4;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath (NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [pageContent objectAtIndex:indexPath.row];
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
So if anyone could straighten me out I would appreciate it.
I was able to solve my own problem, and it seems I was just a bit mixed up.
The comment from rdelmar set me on the right track but I hooked the delegate and data source to the wrong object. I had to connect them to File's Owner in order for it to work.
In addition it seems theTableView was not necessary and when I removed that my code suddenly worked as expected.
If this isn't clear enough for an answer please tell me how I can be more specific. Thank you!

NSMutableArray gets reset after calling modalViewController

IT IS SOLVED BY #T-X. I've marked the changes with "// SOLUTION" in the code!!!
I have the following issue, if I presentModalViewController, the NSMutableArray "projectsArray" gets reset.
Here is my code:
Here the .h file:
// ViewController.h
#import <UIKit/UIKit.h>
#class AddProject;
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
IBOutlet UITableView *projectsTableView;
UIAlertView *reallyDelete;
BOOL candelButtonClicked;
NSIndexPath *actualIndexPath;
NSMutableArray *projectsArray;
NSInteger ID;
NSString *NAME;
AddProject *addProject;
**ViewController *viewContr; // SOLUTION**
}
- (IBAction)addNewProject:(id)sender;
- (void)addToTableView:(NSString *)projectName;
#property (nonatomic, retain) UIAlertView *reallyDelete;
#property (nonatomic) NSInteger cancelButtonIndex;
#property IBOutlet UITableView *projectsTableView;
#property (nonatomic, retain) AddProject *addProject;
**#property (nonatomic, retain) ViewController *viewContr; // SOLUTION**
#end
Here the .m file:
// ViewController.m
#import "ViewController.h"
#import "CustomTableCell.h"
#import "AddProject.h"
#interface ViewController ()
#end
#implementation ViewController
**#synthesize viewContr; // SOLUTION**
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
projectsArray = [[NSMutableArray alloc] init];
ViewController *element = [[ViewController alloc] init];
element->ID = 1;
element->NAME = #"Demo project";
NSLog(#"viewdidload");
[projectsArray addObject:element];
NSLog(#"1. Array length: %d", [projectsArray count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([projectsArray count] < 1) {
NSLog(#"2. Array length: 0");
return 0;
} else {
NSLog(#"2. Array length: %d", [projectsArray count]);
return [projectsArray count];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *customTableCellIdentifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:customTableCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
ViewController *element = [[ViewController alloc] init];
element = [projectsArray objectAtIndex:indexPath.row];
cell.nameLabel.text = element->NAME;
cell.prepTimeLabel.text = [NSString stringWithFormat:#"%i", element->ID];
NSLog(#"3. Array length: %d", [projectsArray count]);
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
reallyDelete = [[UIAlertView alloc] initWithTitle:#"Deleting" message:#"Do your really want to delete that row?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[reallyDelete show];
actualIndexPath = indexPath;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != [reallyDelete cancelButtonIndex])
{
//If "Yes" clicked delete
[projectsArray removeObjectAtIndex:actualIndexPath.row];
NSLog(#"delete row");
[self.projectsTableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:actualIndexPath, nil] withRowAnimation:YES];
[self.projectsTableView reloadData];
}
}
#pragma mark Edit
- (IBAction)addNewProject:(id)sender {
NSLog(#"4. Array length: %d", [projectsArray count]);
AddProject *addProjectView = [[AddProject alloc] initWithNibName:#"AddProject" bundle:nil];
NSLog(#"4.1 Array length: %d", [projectsArray count]);
addProjectView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
NSLog(#"4.2 Array length: %d", [projectsArray count]);
addProjectView.modalPresentationStyle = UIModalPresentationFormSheet;
NSLog(#"4.3 Array length: %d", [projectsArray count]);
**addProjectView.viewContr = self; // SOLUTION**
[self presentModalViewController:addProjectView animated:YES];
NSLog(#"4.4 Array length: %d", [projectsArray count]);
addProjectView.view.superview.frame = CGRectMake(addProjectView.view.center.x/2.5, addProjectView.view.center.y/3, 800, 600);
NSLog(#"4.5 Array length: %d", [projectsArray count]);
}
- (void)addToTableView:(NSString *)projectName
{
NSLog(#"Project Array: %#", projectsArray);
NSLog(#"addToTableView: %#", projectName);
NSLog(#"5. Array length: %d", [projectsArray count]);
ViewController *element = [[ViewController alloc] init];
element->ID = 2;
element->NAME = projectName;
[projectsArray addObject:element];
NSLog(#"6. Array length: %d", [projectsArray count]);
[self.projectsTableView reloadData];
[self.projectsTableView setNeedsDisplay];
}
- (void)viewDidUnload
{
[self setProjectsTableView:nil];
self.projectsTableView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
Here the .h file of the modal presented view:
// AddProject.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AddProject : UIViewController
{
IBOutlet UITextField *projectName;
ViewController *viewContr;
}
- (IBAction)back:(id)sender;
- (IBAction)next:(id)sender;
#property (nonatomic, retain) ViewController *viewContr;
#end
The .m file of the modal presented file:
// AddProject.m
#import "AddProject.h"
#import "ViewController.h"
#interface AddProject ()
#end
#implementation AddProject
**#synthesize viewContr; // SOLUTION I've mist to synthesize it**
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
**// SOLUTION no new allocation/init of ViewController**
}
- (IBAction)back:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)next:(id)sender
{
[viewContr addToTableView:projectName.text];
[viewContr.projectsTableView reloadData];
[viewContr.projectsTableView setNeedsDisplay];
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidUnload
{
projectName = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
I've tested the code by using breakpoints and I could locate the issue when the AddProject class "viewDidLoad" starts loading. But I don't know why it resets the array.
You navigate from ViewController Screen to Add project Screen. In Add Project Screen, you add a new project and want to delegate back this information to the ViewController Screen where you came from.
Now, you are trying to have an instance of ViewController in your AddProject, which you newly instantiate in viewDidLoad method and when project is added, you provide the information to it by calling - (void)addToTableView:(NSString *)projectName on it.
This doesn't work because - The instance of ViewController you allocated is a new instance and not the one you navigated from. When you do alloc + init you get a new object. Thus, although you get addToTableView: called on your ViewController, it doesn't work because the ViewController instance that is receiving this message is a new instance and not the one you navigated from.
Thus, all you needed to do is - have a ViewController property in your Add Project, which you would set before navigating. And then you can send message to this property - As explained in this transcript.
I would like to say - However this thing works, it provides tight coupling between the two controllers.
Ideally, This cane be done by making ViewController (where you go navigate from) as a delegate of Add Project (where you navigate to). And then Add Project can delegate the added project to it's delegate by something like - (void) addProjectController:(AddProjectViewController *) controller didAddProject:(NSString *) projectName. This provides lose coupling between the two view controllers. To understand delegation concept, refer to Delegates and Datasources. Alternatively, search for other Stack overflow posts.
You should allocate projectsArray in the init method of the view controller, not in in viewDidLoad. projectsArray = [[NSMutableArray alloc] init]; allocated a new empty array tach time the view is loaded.
Okay my issue is fixed through the stackoverflow chat. The tips for the final solution came from user #T-X. I've added the tips in the code in my question. They are marked with "// SOLUTION".
In Add Project View controller's viewDidLoad method, remove the line
viewContr = [[ViewController alloc] init];. Now make the variable
viewContr in your ViewController class a property. Also synthesize
it. And finally in the - (IBAction)addNewProject:(id)sender method,
before presenting the controller, do - addProjectView.viewContr = self;. It will solve your issue.
(quote from chat.stackoverflow | #T-X)