How to make a multiple view app with a class linked to each view - objective-c

Apologies if this has been replied already, I've been searching and I quiet cannot find what I am looking for.
I am trying to make a multiple view application (based on the single view template) and I want to have an object linked to each of the views.
So my intention is to have the same I have coming from the single view template: 1 view on the storyboard which is linked to the ViewController class.
I found some tutorials by using Navigation Controllers, Page View controllers, etc... but I don't want to use that functionality, I prefer to create my own buttons and link them to those pages.
I have a few questions, if you can help:
1 - Should I have an array of view objects?
I've seen an old tutorial where the guy creates a new view object when a button is pressed and then set the view as modal (which leads to the other class) and afterwards it destroys it immediately (I guess when the code come back once the modal view is closed).
In my case I would like to have an array of views and initialize all of them at the beginning of the app and just swap from one to the other depending on the user actions.
Which one is a better approach?
2 - How I create the class linked to the other view?
Can I create a normal class and make it subclass of UIViewController, is that enough or I need something else?
3 - Once I have the class created, how can I 'link' it to a view on the storyboard?
Basically I would like to have the outlets of each view in its correspondent class (and actions, etc...).
I am not sure if what I am trying to do is actually the correct way, any comments or help is more than appreciated :)
EDIT: I´ve managed to create a class, link it to a view of the storyboard and lunch it when the user press a button, but the new view appears completely black - any ideas?
Here is my code:
TestView.h:
#import <UIKit/UIKit.h>
#interface TestView : UIViewController
#end
TestView.m:
#import "TestView.h"
#interface TestView ()
#end
#implementation TestView
- (id) init
{
self = [super init];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#end
Then in the storyboard, I dragged in a new View, and in the "Identity Inspector" in the field "Class" I selected the TestView.
The in the file ViewController (the one created by default by the single view wizard), I added an action when a button is pressed:
- (IBAction)goToView1:(id)sender
{
[self presentViewController:view1 animated:YES completion:nil];
}
Also in the ViewController, function 'ViewDidLoad' I initialize the TestView class:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
view1 = [[TestView alloc] init];
}
So when I pressed the button it seems is doing something right or attempting to move to a different view, but the whole screen appears black - Am I missing something?
Thanks a lot in advance!!

Related

Connecting button programmatically XCode9, Objective-C, OSX

im on OSX, XCode9, Objective C.
I have a viewController layouted in IB. The view contains a button connected to the corresponding viewController
SHTourViewController.h
#property (weak) IBOutlet SHStandardButton *closeButton;
// SHStandardButton is a subclass from NSBUtton.
The view controller gets instantinated by code in another class (i need to instantinate this viewController from other classes cause i need it more than once).
// Get instance of viewController
SHTourViewController* tourViewController = [storyBoard instantiateControllerWithIdentifier:#"tourViewController"];
Now in viewDidLoad method of my viewController, i like to connect the buttons action and target:
SHTourViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.closeButton setAction:#selector(closeButtonClicked:)];
[self.closeButton setTarget:self];
}
- (void)closeButtonClicked:(id)sender {
NSLog(#"CLOSE!");
}
}
When i click the button, the app crashes (Thread 1: EXC_BAD_ACCESS). I can't find the mistake i am doing here.
Any help appreciated.
You are not supplying enough information about what you're doing. But, as it stands, the fact that fact that you are getting a Bad Access would suggest that some important object has vanished prematurely in a puff of smoke. My guess is that that object is self, and that the problem has to do with code after this line:
SHTourViewController* tourViewController =
[storyBoard instantiateControllerWithIdentifier:#"tourViewController"];
You are obtaining a completely new instance of this view controller but then you are not getting its view correctly into the view hierarchy and the view controller itself into the view controller hierarchy, so the view controller (I'm guessing) is released.
But you didn't show us the relevant code, so that's just a guess.

Xcode: How to add Custom View from XIB File in a UIScrollView

Coming from an Android and Java background, I am relative new with Xcode Development.
I am using Xcode 5.0.2 and created my first IOS Application by selecting Create a New Xcode Project -> Single View Application. The initial project structure has been generated and I found that there is a Main_iphone.storyboard file which opens a UI Designer where I can drag and drop items to it. I selected UIScrollView and dragged it into the main window that has been generated.
Now in the Controller header file, I added #property (nonatomic, retain) IBOutlet UIScrollView *scrollView; so that i could access the scrollView from inside my controller code.
I wanted to add an item to the scrollView programatically so I created a template Custom View by adding new file -> Objective-C Class with XIB for User Interface, named it TSTFilesInfoController and designed the XIB by adding a View and a label inside the view. Same with the scrollView above, I created a property to expose the mainView in my controller class.
I hardcoded a loop of 10x inside the controller of the UIScrollView and inside the for loop I am instantiating TSTFilesInfoController and adding the view to the UIScrollView. But when i run the application, nothing is shown or added in the UIScrollView.
Heres the code for adding the CustomView:
- (void)viewDidLoad {
[super viewDidLoad];
for ( int i = 0; i < 10 ; i++) {
TSTFilesInfoController *info = [[TSTFilesInfoController alloc] init];
[self addChildViewController:info];
[self.scrollView addSubview:info.mainView];
NSLog(#"View has been added into the scrollView");
}
}
Can someone please tell me whats wrong with my codes and what would be the correct approach to achieve the output that i wanted? Thank you in advance for the help.
-- EDIT --
This code is auto-generated in TSTFilesInfoController.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
This is a misuse of a UIViewController subclass. Do not use a view controller just as a sort of fishing rod to hook a view that's inside a xib file. Simply load the xib file and grab the view yourself, and stuff it into your interface.
There are complex rules for how to put a view controller's view manually inside your interface, and in general it is something you should be reluctant to do. But making views come and go dynamically and directly is easy and common.
Let's suppose the .xib file is called TSTFilesInfo.xib and it has a top-level UIView subclass object, class MyView, which is the one you want. Then:
MyView* v = (MyView*)
([[UINib nibWithNibName:#"TSTFilesInfo" bundle:nil]
instantiateWithOwner:nil options:nil][0]);
This loads the nib once, instantiating its contents, and handing you a reference to that instance (the UIView in this case). Now plunk v into your interface. Keep that reference in an instance variable (probably weak, since it is retained by its superview) so that you can subsequently configure and communicate with any subviews of MyView to which you have created outlets.
NOTE: However, I must say from the example so far that it sounds to me like what you really want here is a UITableView, not a simple UIScrollView. It comes all set to do just the kind of thing you seem to up to here.

Dismissing the split view popover controller

I have a UISplitViewController with the master view set up like this:
UITabBarController
Tab1:
UINavigationController -> UIViewController -> UIViewController
Tab2:
UINavigationController -> UIViewController
Each of the UIViewControllers is a table view, and when the user chooses a row in the last one, an image is shown in the detail view, which contains a UIScrollView.
The tab bar Controller is the UISplitViewControllerDelegate and handles putting up the button on a toolbar at the top of the scroll view.
The problem is, I want to add code to dismiss the popover when the user makes their choice. The pointer to the popover has to be saved in the tab bar controller when the button goes up, and then used to dismiss the popover several view controllers down the line when the user makes their final selection. There doesn't seem to be any way for the view controller that needs that pointer to get at it, without doing something gross like storing it in the App Delegate.
I don't see other people asking this question, which leads me to believe that I've once again overlooked something simple. Please enlighten me!
It sounds like your tab bar controller is already a subclass of UITabBarController, which means that you've already got some custom code in there. I would suggest that the tab bar controller is the primary owner of the popover, and it is the table view controller's responsibility to simply notify the tab bar controller that a selection has been made. The tab bar controller can respond to that message by dismissing the popover. You can take advantage of the fact that UIViewController already has a method for accessing the tab bar controller that contains a given controller.
So it would look something like this:
#interface MyTabBarController : UITabBarController
- (void)itemWasSelected;
#end
#implementation MyTabBarController {
UIPopoverController *popover;
}
- (void)itemWasSelected {
[popover dismissPopoverControllerAnimated:YES];
}
#end
//////////////
#implementation TableController
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)path {
// Do whatever else you want to do
MyTabBarController *tabController = (MyTabBarController *)self.tabBarController;
[tabController itemWasSelected];
}
With this solution, the table controller doesn't have to know anything about the popover; it just has to know that it's going to be presented inside a MyTabBarController, which seems a reasonable thing for it to know.
You could create a singleton class to track your popover status and then make it available to all classes equally and easily. That way it could easily be updated and accessed from any code without having to go straight to overburdening the app delegate even though thats basically the same idea but a bit cleaner in its own singleton.

Custom back button UINavigationController across my entire app

I am looking to replace the back button in the UINavigationController throughout my application. My requirements is that this back button be defined in one XIB and if possible, the code to set it is in one place.
I have seen various methods that set the property self.navigationItem.backBarButtonItem to be a UIBarButtomItem with the custom button as it's view, e.g. [[UIBarButtonItem alloc] initWithCustomView:myButton];
My first thought was to create a global category (not sure if that's the term, I'm new to Objective-C as you might have guessed) that implements 'ViewDidLoad' for all my UINavigationControllers, and setting this property. My problem is loading the XIB to this button that I create at runtime.
Does anyone have a suggestion on a neat way of doing this (I guess it must be a common thing to do, and I can't imagine repeating code in all my screens). I have considered creating a UINavigationController subclass, however I wasn't sure how this would effect my custom implementations of ViewDidLoad.
Any advice much appreciated. Also I need to target >= iOS4 (the appearance API is iOS5 only).
I prefer to not force inheritance where possible so you could do this with two categories
#interface UIViewController (backButtonAdditions)
- (void)ps_addBackbutton;
#end
#implementation UIViewController (backButtonAdditions)
- (void)ps_addBackbutton;
{
// add back button
}
#end
#interface UINavigationController (backButtonAdditions)
- (void)ps_pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
#end
#implementation UINavigationController (backButtonAdditions)
- (void)ps_pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
{
[viewController ps_addBackbutton];
[self pushViewController:viewController animated:animated];
}
#end
Now #import these files as appropriate and instead of using
[self.navigationController pushViewController:aViewController YES];
use
[self.navigationController ps_pushViewController:aViewController YES];
Disclaimer
I free styled this in the browser so you may need to tweak it
I had the same issue in my current project, the solution I came up with was to create a MYBaseViewController base class without xib and there in viewDidLoad programmatically (if you want to init barButtonItem with custom view, you are not able to create in xib anyways) create a customBackButton (and of course release it and set to nil viewDidUnload.
This works good for me because this way I can create xibs for all my other viewControllers that are subclasses of MYBaseViewController(if you created a view for base class in nib you would not be able to create a nib for a subclass).

iPhone subview design (UIView vs UIViewController)

I'm designing a simple Quiz application. The application needs to display different types of QuizQuestions. Each type of QuizQuestion has a distinct behavior and UI.
The user interface will be something like this:
alt text http://dl.getdropbox.com/u/907284/Picture%201.png
I would like to be able to design each type of QuizQuestion in Interface Builder.
For example, a MultipleChoiceQuizQuestion would look like this:
alt text http://dl.getdropbox.com/u/907284/Picture%202.png
Originally, I planned to make the QuizQuestion class a UIViewController. However, I read in the Apple documentation that UIViewControllers should only be used to display an entire page.
Therefore, I made my QuizController (which manages the entire screen e.g. prev/next buttons) a UIViewController and my QuizQuestion class a subclass of UIView.
However, to load this UIView (created in IB), I must[1] do the following in my constructor:
//MultipleQuizQuestion.m
+(id)createInstance {
UIViewController *useless = [[UIViewController alloc] initWithNibName:#"MultipleQuizQuestion" bundle:nil];
UIView *view = [[useless.view retain] autorelease];
[useless release];
return view; // probably has a memory leak or something
}
This type of access does not seem to be standard or object-oriented. Is this type of code normal/acceptable? Or did I make a poor choice somewhere in my design?
Thankyou,
edit (for clarity): I'd like to have a separate class to control the multipleChoiceView...like a ViewController but apparently that's only for entire windows. Maybe I should make a MultipleChoiceViewManager (not controller!) and set the File's Owner to that instead?
You're on the right track. In your QuizController xib, you can create separate views by dragging them to the xib's main window rather than to the QuizController's main view. Then you can design each view you need according to your question types. When the user taps next or previous, remove the previous view and load the view you need based on your question type using -addSubview on the view controller's main view and keep track of which subview is currently showing. Trying something like this:
[currentView removeFromSuperView];
switch(questionType)
{
case kMultipleChoice:
[[self view] addSubview:multipleChoiceView];
currentView = multipleChoiceView;
break;
case kOpenEnded:
[[self view] addSubview:openEndedView];
currentView = openEndedView;
break;
// etc.
}
Where multipleChoice view and openEndedView are UIView outlets in your QuizController connected to the views you designed in IB. You may need to mess with the position of your view within the parent view before you add it to get it to display in the right place, but you can do this with calls to -setBounds/-setFrame and/or -setCenter on the UIView.
Yeah, IB on iPhone really wants File's Owner to be a UIViewController subclass, which makes what you want to a bit tricky. What you can do is load the nib against an existing UIViewController instead of instantiating one using the nib:
#implementation QuizController
- (void) loadCustomViewFromNib:(NSString *)viewNibName {
(void)[[NSBundle mainBundle] loadNibNamed:viewNibName owner:self options:nil];
}
#end
That will cause the runtime to load the nib, but rather than creating a new view controller to connect the actions and outlets it will use what you pass in as owner. Since we pass self in the view defined in that nib will be attached to whatever IBOutlet you have it assigned to after the call.