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

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.

Related

How to make a multiple view app with a class linked to each view

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!!

TableViewController within a ViewController

I have a UIViewController (StoreViewController), and in it's .xib is a UITableView to the left, and a standard UIView to the right. I have created a UITableViewController called StoreTableController and want to somehow make it the the controller of the table view within the StoreView.xib.
Unfortunately, I need to keep the File Owner of the nib file as StoreViewController. I have a delegate within the StoreTableController which has been set as the StoreViewController (this is for calling certain methods), and within the StoreViewController I have an instance of the StoreTableController.
So far I have tried keeping an outlet of the UITableView within StoreViewController and then doing this:
[self addChildViewController:self.tableController];
[self.tableController setTableView:self.table];
[self.table setDataSource:self.tableController];
[self.table setDelegate:self.tableController];
Where self.table is the outlet, and self.tableController is the instance of the StoreTableController.
However, I do not fully understand how to use UIViewController containment, so this is obviously incorrect.
I have tried variations of this as well, but really don't know what to do.
I have avoided using a UISplitViewController here because not only is the left view larger than the right, but also there are various things I plan to do which mean this must be done in a single .xib file if possible.
Any help is very much appreciated.
First, put a regular UIView instead of a UIScrollView in your .xib. Connect it with an IBOutlet called "tableContentView".
Then, create a new instance of UITableViewController (or your custom class, derived from UITableViewController) in your code, and add its UIView to the tableContentView like so:
- (void)viewDidLoad
{
[super viewDidLoad];
// Add tableView
UITableViewController *someTableViewController = [[UITableViewController alloc] init];
someTableViewController.view.frame = self.tableContentView.bounds;
someTableViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.tableContentView addSubview:someTableViewController.view];
}

How to display a view from a NSViewController?

I'm new at objective-c so please bear with me. I have a class that returns a picture from a webcam and I am trying to display that to the screen. I subclassed NSViewController to get the image from the camera class and set it to an instance NSImageView and set the NSViewController's view to be the NSImageView. I have created a custom view in Interface Builder, dragged a NSViewController object into MainMenu.xib set it's class to be PhotoGrabberController, and control click-dragged from the custom view to the PhotoGrabberController to set it's outlet binding to be the view. (I really don't know how that works behind the scenes, it seems like magic to me). Nothing shows up on the screen and I've been playing around with this forever.
In PhotoGrabberController.h
#interface PhotoGrabberController : NSViewController {
PhotoGrabber * grabber;
NSImageView* iView;
}
#property (nonatomic, retain) NSImageView* iView;
In PhotoGrabberController.m
#implementation PhotoGrabberController
#synthesize iView,grabber;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void) awakeFromNib
{
self = [super init];
if (self) {
NSRect rect = NSMakeRect(10, 10, 200, 100);
iView = [[NSImageView alloc] initWithFrame:rect];
[iView setImageScaling:NSScaleToFit];
[iView setImage:[NSImage imageNamed:#"picture.png"]];
grabber = [[PhotoGrabber alloc] init];
grabber.delegate = (id)self;
//[grabber grabPhoto]; (This usually sets iView but I removed it and manually set iView to be an image from file above for simplicity)
[self setView:iView];
}
}
I toyed around with putting a bunch of different things in the AppDelegate's applicationDidFinishLaunching but the way it's set up right now I think I shouldn't need to put anything in there.
Question
How do I get the main window to show the image?
Is it ok to use PhotoGrabberController.view instead of creating a view subclass?
What is wrong with my understanding of how things work here? I've spent a lot of time trying to figure this out and have gotten nowhere.
Can you direct me to a resource where I can fully understand how this works? I've found Apple documentation too detailed and thick. I just want to understand how windows, views, and view controllers are loaded and interact.
How do I get the main window to show the image?
That's a view's job.
As you already have a view, you just need to get it into the window. A window only holds one view directly, which is called its content view; most probably, you will want to add the view controller's view as a subview of the view that is already the content view, or as a subview of some other grandchild view.
Is it ok to use PhotoGrabberController.view instead of creating a view subclass?
These are orthogonal. The VC's view is an instance of a view class. Creating a class has nothing to do with using an instance.
I don't think creating a view subclass will help you here. You already have two working views: a plain NSView as the VC's immediate view, and a NSImageView within it. No subclass is necessary to add these to the view—with the image view within it—to the window.
What is wrong with my understanding of how things work here?
You've missed the entire concept of the view hierarchy, and the fact that such a hierarchy is how every window displays its content.
Also, you began your question with “I'm new at objective-c” (sic), but your question is about the Cocoa framework (specifically, the AppKit framework), not the Objective-C language.
Can you direct me to a resource where I can fully understand how this works?
The Window Programming Guide (particularly, “How Windows Work”) and the View Programming Guide (particularly, “Working with the View Hierarchy”).

How can I make a common control in Objective-C that can be used in multiple views in a storyboard?

I am just starting in my iOS development using xCode 4.2 and discovered storyboards. They seem great for rapid prototyping.
What I'm wondering is how can I create my own custom control that I can use in multiple views.
For example, let's say I want a custom title bar that is common on every view. I think I should be able to define that control with it's own controller and instantiate that from multiple views. I just don't know how and the tutorials and questions I've read so far don't address this.
Any tips?
I've found one way to do this is to do the following steps:
Create a new xib file and set the Simulated Metrics to "Freeform" to allow resizing. (MyControl.xib)
Populate the control with the Objects I want in the control.
Create a UIViewController for the view. (MyViewController.h & MyViewController.m)
Set the File's Owner of the MyControl.xib to the Custom Class MyViewController
In the xib that I want to include the control, I put a UIScrollView where I want the control (a regular View will work too). (Parent.xib)
Create an IBOutlet for the UISCrollView that I will put the control into in the ParentController.
Create an instance of MyViewController in the ParentController.
In the ParentController, add the view of the MyViewController as a subview of the UIScrollView.
In code, this means
#implementation ParentController
#synthesize myScrollView;
MyViewController* myController;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super init];
if(self)
{
myController = [[MyController alloc] initWithNibName:#"MyView" bundle:nil];
[myScrollView addSubview:myController.view];
}
}
This seems to work and allows me to separate the implementation of the Control and the Parent, but I can't help but think there is a better way.

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.