this is my first post on here, though with the help of many questions and answers from members of this community, I have brought my project to near completion.
I have read multiple threads similar to what I'm asking, but the methods were completely different. No code has worked so far.
Basically (I say this because my code involves a lovely snake-like descent into a complicated mess, but applicable snippets will be put up upon request), my problem is that I'm calling
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
and it pushes my viewcontroller in the simulator and NSLogs the string I need changed beautifully, but it pushes a blank view! The code for that run makes the view controller variable a constant:
UIViewController *viewController = [[xSheetMusicViewController alloc]initWithNibName:nil bundle:nil];
So I thought to myself, what am I doing!? So I went back to the old method, which involved making the UIViewcontroller an if-then, if-else-then statement that would push different views depending on whether certain rows were selected (standard stuff). Which pushed a new view with my string loaded perfectly, but it only NSLog'ed one string over and over! And the worst part was the my app would call either SIGABRT, or EXC_BAD_ACCESS when I tried returning to the rootviewcontroller. (here's the applicable code):
UIViewController *viewController = [[[UIViewController alloc]init]autorelease];
if (indexPath.row == 0 && indexPath.section == 0) {
appDelegate.baseURL = #"mussette.pdf";
viewcontroller = [[xSheetmusicViewController alloc]initwithnibname:nil bundle:nil];
}
else if (...)
//pushview, changestring, blah blah//
Now, I would prefer that my view push the PDF like it's supposed to, and have the correct string value (and not give me SIGABRT or EXC_BAD_ACESS, but those are givens), but it seems that compromise is just out of my reach. I know there's probably something stupid I'm doing that could be solved with one line of code, but for now, it seems hopeless.
Thanks in advance.
EDIT: To answer all of your questions, yes, there is no xib, rather an
(id)init
method in the next view.
EDIT 2: to answer lostInTransit's request and add some additional details:
<else if (indexPath.row == 1 && indexPath.section == 0) {
appDelegate.baseURL = #"Importing PDF's.pdf";
Also, if it helps, the output keeps logging:
Application tried to push a nil view controller on target .
When I try to push the view from a tableviewcell, and it did that before when it loaded the PDF right so I ignored it.
Question: why do you first initialize your viewController as a UIViewController and then again as xSheetmusicViewController? I think the problem is with releasing values properly. In one init, you do an autorelease, in the other you don't. So chances are you are releasing a variable twice leading to the BAD ACCESS.
Do you mind posting the "blah blah" :) in the last piece of code?
Do you have a file named xSheetmusicViewController.xib in your application? That will be loaded with your view controller as its owner after you call [[xSheetmusicViewController alloc] initNithNibName:nil bundle:nil]; (it will actually be loaded when the view property is first accessed). If that file doesn’t exist, then the view controller’s -loadView: method will be called to load its view.
If you have a blank view, either you have a blank or mis-named nib (perhaps you renamed the class but not the nib?) or you aren’t creating the right view in -loadView:.
Related
I am trying to manually push a view controller within my iOS 8 app. I have designed it in the Main.storyboard and i have already attached on it an specific identifier.
The code i am using is:
CustomViewController *vc =
[self.storyboard instantiateViewControllerWithIdentifier:#"CustomViewController"];
vc.customField1 = self.customField1;
vc.customField2 = self.customField2;
[self.navigationController pushViewController:vc animated:YES];
but that causes the app's freeze. It does not spit out any logs or something, so I cannot understand what might be wrong.
Can you help me a bit here?
Thank in advance
Do not do these two lines:
vc.customField1 = self.customField1;
vc.customField2 = self.customField2;
The problem here is that you're assigning one text field to be another text field (actually, you're making a text field reference refer to a completely different text field). Instead, copy the contents (e.g. the text) of the fields from your parent view controller to fields that already live in your new CustomViewController:
vc.customField1.text = self.customField1.text;
vc.customField2.text = self.customField2.text;
I'm thinking what is happening here is that the app is hanging when the new CustomViewController appears because it's trying to access fields in the now hidden / pushed-away parent view controller.
I need to work out how to identify what storyboard is active at any given time. I have a specialised nativation in which I need to identify the storyboard (UIView) then change things programmatically depending on what the user presses.
All storyboards have Identifiers.
in the viewDidLoad of the root view I have the following.
- (void)viewDidLoad
self.topViewController =
[storyboard instantiateViewControllerWithIdentifier:#"View1"];
{
What I would like to do is identify which storyboard the user is on and depending on the press do the following sudo-code
- (void)viewDidLoad
if (storyboard.name != RootView)
self.topViewController =
[storyboard instantiateViewControllerWithIdentifier:#"View1"];
{
else if (storyboard.name = View2){
self.topViewController =
[storyboard instantiateViewControllerWithIdentifier:#"View2"];
}
etc....
I have step through the code and seen the StoryboardID however it's which I'm pretty sure your not meant to use....
Thanks in advance
Jeremy
UPDATE: Explanation to Navigation
I'm trying to implement the ECSlideViewController, but It's doing my head in. Effectively adding in the slide to the right function to reveal more options. SO, this thinking was going to be easy turned out icky. I have the master UIViewController<title:HomeView> I then have 4 buttons on the screen which segueway to other UIViewControllers<View1>, UIViewController<View2> etc.
In order to produce the effect on View1,View2,View3,View4 I need to bring the class (ECSlideViewController as per the example) into the UIViewController<HomeView>. However If I change the below code to represent this...
self.topViewController =
[storyboard instantiateViewControllerWithIdentifier:#"HomeView"];
It crashes because it calls itself. Not good, circular coding is a no no.
but if I set it to what was originally there
self.topViewController =
[storyboard instantiateViewControllerWithIdentifier:#"FirstTop"];
( btw firstTop is the title of the view used with the example)
It works but then disregards the UIViewController<HomeView>
This is why I asked if there was a way to identify the self.title of the UIViewController(said storyboard...my bad) as I was going to put conditional logic around it in order to not put the code in if it's on the UIViewController<HomeView>.
It really is hard to explain unless you download the ECSlideViewController and start playing with it. But effectively I just want to test the self.title.....I think...
The other idea was to bring the logic into the UIViewControllers of the four and get it to work there...but It freaks out passing nil as it's expecting an identifier...
Hope this makes sense....
J.
Okay Guys,
Totally ditched ECSlideViewController. I found a few articles explaining that it had issues when you had multiple UiViewControllers not passing data correctly. I ended up using Andrews suggestion. http://www.youtube.com/feed/UCJA_puohXgnze8gPaerTeig It worked for easier for me.
Although I take note of what the design guidelines Apple have an this is usually a no no, but I'm hoping that they won't mind.
Thanks everyone again!
J.
I'm not sure if this helps, but it sounds like you will have to compare instances to get the results you are looking for. I haven't tried this, but I would create properties of each storyboard in your app delegate, then reference the app delegate in your view controller to compare. This might not be the best coding practices, but I'll leave that into your hands.
Something like (untested code):
- (void)testStoryBoard //After awake from nib or viewDidLoad
{
NXAppDelegate *appDelegate = (NXAppDelegate *)[[UIApplication sharedApplication] delegate];
if ([self.storyBoard isEqual:appDelegate.view1StoryBoard])
NSLog(#"View1 Storyboard");
else
NSLog(#"View 2 Storyboard");
}
I am trying to add anew row to a UITableView. The user enters data in a UITextField on a UIViewController and the control goes back to the original UITableView. What method writes to the table when new row is added. i seem to have tried everything. nothing seems to work.
The code from the comment below is:
tableItems = [NSMutableArray arrayWithObjects:#"Persian", #"Rag Doll", #"Siamese", #"Scottish Fold", #"British short hair", nil];
[self.tableView reloadData];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"AddSegue"])
{
UINavigationController *nav = segue.destinationViewController;
AddItemViewController *aivc = [nav.viewControllers objectAtIndex:0];
aivc.TVC = self;
}
}
If you are storing the new row data in an array (for example) which is accessed by the UITableView then just reload the table as following:
[self.tableview reloadData];
I've updated your question with the code that you provided in your comment to another answer.
Bottom line, you don't tell us where your exception is being generated, nor what the exception is. You need to narrow that down if you expect us to help. Also, I'm not sure what to conclude from the fact that you suggest that the exception is being caused after the UITextField is entered and you're returning to the previous controller, but you didn't show us that code, but rather you showed us the code for segueing to the AddItemViewController. Are you getting the error when you first segue, or when you try to pop/dismiss to get back to the view controller with the UITableView?
With those fairly significant questions notwithstanding, I'd suggest two things:
First, make sure you're adding robust checking of your results, to prevent exceptions from happening. For example, a common problem is that one retrieves a value from a property or a NSArray, but for one reason or another, it's not the type of object that the code assumes it was (and requires it to be). In this case, you're assuming that the destinationViewController is a UINavigationController and furthermore, you're assuming that the first element in its viewControllers array is a AddItemViewController. You know your code, and perhaps you are confident of this fact, but the fact that you're getting an exception means that you probably need more robust error checking.
Therefore, I would suggest that you including NSAssert statements that will verify this fact. Using your prepareForSegue as an example, I might suggest altering it thusly:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"AddSegue"])
{
UINavigationController *nav = segue.destinationViewController;
NSAssert([nav isKindOfClass:[UINavigationController class]], #"destinationViewController is not a UINavigationController");
AddItemViewController *aivc = nav.viewControllers[0];
NSAssert([aivc isKindOfClass:[AddItemViewController class]], #"destinationViewController.viewControllers[0] is not a AddItemViewController");
aivc.TVC = self;
}
}
The NSAssert statements are good ways of testing values during testing to make sure that the objects you return are truly of the Class you thought they were. These NSAssert clauses are only used for errors that you're testing for during development, but should never occur in the production application. If your code makes implicit assumptions about what's being returned from a method, you can use NSAssert to validate those assumptions while you're debugging your app.
Now, I have no reason to know whether the values of nav or aivc are your problem, but this is an example of the sort of checking your code should be doing. It's simply a good practice (especially if you're struggling to find an exception), to make sure that the object you've retrieved is the correct type, upon which the rest of your code depends.
I have an ulterior motive here. Your code implies that you're navigating to a view controller that is, itself, embedded in its own navigation controller. You never said as much in your narrative, so these NSAssert statements simply verify this fact.
Second, if all of your robust verification of objects doesn't catch the problem, you must therefore identify the source of the exception yourself. Frequently you can decipher this by looking at the stack trace in the "Debug Navigator" or by reading the error in the console in Xcode. But, another (underappreciated, IMHO) tool, is exception breakpoints which can find the exact line of code that is causing the exception. If I'm encountering any exceptions, I'll routinely add an exception breakpoint on "All" exceptions. That way, if I'm running the program through my debugger, if it encounters an exception, it will stop the code at the offending line, greatly simplifying the process of identifying the source of the problem. It doesn't always work perfectly, but it frequently finds the source of the exception more quickly than other techniques.
Can anyone tell me how I can phrase an if () statement to discover if a segue's destination view controller will appear in the Detail Split or in the Master Split?
I want to put the if() statement inside my prepareForSegue:sender: methods.
EDIT
All my detail views that are relevant to this question (at the moment) conform to a protocol and I am currently performing introspection on the destination controller using:
if ([segue.destinationViewController conformsToProtocol:#protocol(myProtocol)])...
I can see that this would not work if I wanted:
To be able to show the same class in either Master or Detail of the splitView from time to time, and at the same time...
I only want the if() statement to be true when the view is to be presented in the detail split.
Things like segue.destinationViewController.navigationController == ... don't appear to be any use either.
I was hoping that since we need to set "Master Split" or "Detail Split" when we set the segue up... there would be a way to access that information less circuitously.
SECOND EDIT:
The way I have this set up with using introspection does "work". It just doesn't seem very "Object Oriented". I don't think I should be querying the View Controller at all for this information, I can't see why the VC should know anything about which side of the splitView it will be displayed. Surely the object that should hold onto this information is the Segue and, as I say, it appears this is being "set" in the storyboard when we select "Detail" or "Master" split.
Maybe it isn't a property of anything, but I can't see how to get at it.
I suppose I could query the destinationViewController in its viewWillAppear to discover which NavigationController it is in after it appears on screen but, again, it seems a bit "hacky".
There is probably a better more abstract and reusable way to do this that I'm not aware of, but here is a suggestion that could help in your specific project that requires just a bit of special knowledge of your specific project.
If you use introspection in your prepare for segue, you can check to see if methods exist by using the responds to approach.
So for example, in typical implementations of a splitview controller (note - not all) the detail view will implement the methods to handle rotation. So if this is true in your project, you could do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController respondsToSelector:#selector(splitViewController:shouldHideViewController:inOrientation:)]) {
//do something
}
}
You could use this same approach based upon something that was unique but constant in your project related to either the master or detail view.
hope that helps,
be well
My experience is a little limited, but most times I've seen prepareForSegue used, the if() block checks segue.identifier to do anything that needs to be done specifically to handle building the new page. If you set the identifier for all your segues, you could just have code to handle each segue from that controller, and change what the code is depending on if that segue goes to a masterViewController or a detailViewController. Not really a well automated way, but it'll get the job done.
EDIT: oh geez, that wording is kinda confusing. If you want me to I can put a code example up, but it'll have to wait until Monday, as I don't have access to a Mac until then.
The talk of classes and protocols gave me another idea, but again, not sure if it will work - I wanted to test it before posting, but I'm not going to have the time to test anytime soon.
I think you should be able to create 2 new classes, UIMasterViewController and UIDetailViewController, that are subclasses of just UIViewController. Then, for each of your actual screens, instead of making them subclasses of UIViewController directly, make them either a UIDetailViewController or UIMasterViewController. Then, in your prepareForSegue,
if ([segue.destinationViewController isKindOfClass:UIMasterViewController])
{
//do master view specific stuff
}
else if ([segue.destinationViewController isKindOfClass:UIDetailViewController])
{
//do detail view stuff here
}
This should be a pretty dependable way to tell where your segue is sending you, as long as you can set up the custom view controller classes right. This still won't solve the first issue noted in the question
"To be able to show the same class in either Master or Detail of the
splitView from time to time, and at the same time..."
This could be overcome by making 2 copies of all of the views you want to be able to show as either or both, then make one a UIMasterViewController and the other a UIDetailViewController - copy-paste should be good for most of the rest.
Let me know if this works - I'm not exactly sure how to set up the controllers off the top of my head, but I'm pretty sure it could be done. If it can, I can see this being a very useful thing.
Description:
Let's say I've got a class that contains UITabBarController and a bunch of controllers.
Now, one of the controllers (specialController) is not allowed to be selected by UITabBarController. I show him in different way (but I still want to have him in UITabBarController's viewControllers)
By disallowing specialController to be selected, I'm missing init done by UITabBarController. That's why I call init on it by hand.
The problem:
When I run "Build and Analyzie", I receive warning
Incorrect decrement of the reference count of an object that is
not owned at this point by the caller
in the line with myinit. But, the application works. What am I missing?
Code:
in the ClassA.m
//called in viewDidLoad
- (void)makeVoodooOnViewControllers {
//set all variables etc.
for (int i = 0; i<controllersCount; i++) {
UIViewController *tabViewController = [tabBarController.viewControllers
objectAtIndex:i];
//CUT - some irrelevant code
if ([tabViewController isKindOfClass:[specialController class]]) {
//line below throws a warning
specialControllerProperty = [((specialController *)tabViewController) init];
}
}
If I'm not clear, please let me know :). Thanks!
How do the view controllers get into the tabBarController.viewControllers array?
If they are setup in the xib file then init is called as part of reanimating them from the xib and should not be called again. It's almost never the case that you want to call init anywhere except directly after you call alloc. Having an alloc'd but unitialized object hanging around seems like a very (very!) rare and unlikely to be desirable situation....
Fundamentally, having a controller in the tab controller controllers list that isn't part of the tab controller controlled controllers seems like a bad design idea - counter the the expectations and design of the tab controller class... and thus likely to cause problems.