UITableViewController not displaying - uitableview

Is there a way to add the tableView portion of a UITableViewController to a UIView?
Here is what I've got...
1) UIViewController - MainViewController
2) UIView - customUIView - Added to MainViewController (This UIView is replaced when a new page is loaded)
When a button is clicked all of the other pages in my app are loaded using (example)
mainViewController.customUIView = [[LoginPage alloc] initWithFrame:mainViewController.view.bounds];
3) UITableViewController - ScoresNotificationsPage - Here is my issue, trying to add the tableView portion of this into the UIView and have it display on the MainViewController, just as if any other UIView was added.
When I try loading the tableView portion of the UITableViewController into customUIView using:
mainViewController.customUIView = [[ScoresNotificationsPage alloc] initWithFrame:mainViewController.view.bounds];
I get the following error
Incompatible pointer types assigning to 'UIView *' from 'ScoresNotificationsPage *'
Any idea of what I could do to get the tableView to show up?
Thank you in advance.
Whew...

Make ScoresNotificationsPage a child view controller and then simply add it: [self.view addSubview:scoresPage.tableView]?

Related

Where to initialize subview?

I am new to iOS development and I am currently reading the book : iOS Programming (Objective C) by Big Nerd Ranch.
I am confused as in where to initialize subviews such as UIButtons, UIImageView while creating views programtically:
Should the intialization be done in the Main UIView i.e in the
initWithFrame method and maintain a additional weak reference to the subview in the UIView.
or
should I do it in the UIViewControllers loadView method and maintain a weak reference to the subview in the uiviewcontroller (Same approach used while creating UIVew using the interface builder).
I have seen both the approaches being used in various stackoverflow posts but no post that explains which approach is the right one.
you can initialize as per your app's requirement. If any view or button or anything is part of initial setup of your app then you should initialize it in viewDidload.
Now, for example there is requirement like user press button and then new view will be created then you can initialize view in button's click method etc.
So, it's depends on your requirement.
Static views which will live from start to and of app should be initialize in viewdidload, because this is the first method getting called of viewcontroller.
hope this will help :)
It dependes on which architecture you are using. Apple raises the flag of Model-View-Controller, but in fact, UIViewControllers are the View.
For Example:
Let's say that you have a pretty LoginViewController. When you instantiate it, you will be doing something like
LoginViewController *loginVC = [[LoginViewController alloc] init];
At this point, no view is loaded. Your ViewController has just executed the init method, nothing else. When the system calls
loginVC.view
the first method to be executed will be
- (void)loadView;
there you should do exactly that, load your view. So, the approach i like is to have an additional LoginView.
- (void)loadView
{
// you should have a property #property (nonatomic, strong) LoginView *loginView;
self.loginView = [[LoginView alloc] init];
self.view = self.loginView;
}
and in the LoginView init method, you should put your code to build up the view.
However, you could eliminate LoginView, and instantiate all your subviews like this:
- (void)loadView
{
self.view = [[UIView alloc] init];
UIButton *button = [[UIButton alloc] initWithTargetBlaBlaBla...];
[self.view addSubview:button];
// add more fancy subviews
}
In my experience, the first approach is much cleaner than the second one. It also makes version control a lot easier (try to merge a xib, I dare you). I always use MyView.m to build the view (a.k.a setup constriants, style) and use MyViewController.m things like animations, lifeCycle. I like to think that MyView.m is the programatic xib, so anything that you can do with xibs, you should me able to do it inside your view.
Hope it helps!!

UITextView is nil when attempting to set for popover

I have two View Controllers: SavePopOverVC and MainVC. I also have a nib file called SavePopOver. SavePopOver has three items, a UIButton, a UIImage and a UITextView. The image and text view have outlets to property fields in SavePopOverVC called captionImage and captionTextView respectively. The button has an outlet to an IBAction in SavePopOverVC.
In MainVC.m I have the following two lines in my class extension.
SavePopOverVC *spvc;
UIPopoverController *popover;
In my viewDidLoad of the same file I have the following lines relating to my popover.
spvc = [[SavePopOverVC alloc] initWithNibNamed:#"SavePopOver" bundle:nil];
popover = [[UIPopoverController alloc] initWithContentViewController:spvc];
In my function that displays my popover, also in MainVC.m, I have the following lines.
[popover setPopoverContentSize:CGSizeMake(600,200)];
[popover presentPopoverFromRect:_header.frame inView:self.view permittedArrowDirection:UIPopoverArrowDirectionAny animated:YES];
[((SavePopOverVC *)popover.contentViewController).captionTextView setText:#"Some text here"];
However, captionTextView is nil when I make the setText: call. The app doesn't crash but the text isn't set. After the popover is displayed and I click on the UIButton to save the string typed in captionTextView I get the string just fine. So, I know the two are ultimately linked correctly, but how can I set captionTextView from when I display the popover?
If it is worth noting, I'm developing solely for iPad with this one.
It is most likely nil because its view isn't loaded at the time you set the text. Unlike most other modern languages, in Objective-C calling a method on a nil object doesn't cause an exception, it just does nothing.
To solve this, you can create a custom NSString property in your SavePopOverVC, e.g.
#property (nonatomic, copy) NSString *caption;
Before you call presentPopoverFromRect:, assign a value to this property. Inside SavePopOverVC, override viewDidLoad and set the captionTextView.text = self.caption;
There might be people who disagree with me, but I don't recommend exposing UI controls as properties in a view controller. This behaviour is one of the reasons for that.

Swapping UITableViews in a single UIViewController

I have 1 view controller where depending on the button that is clicked, a view at the bottom is swapped between 4 different table views. These table views are in their own separate UITableViewController's in the storyboard. I add the tableViews like this:
Tracks_TVC *tracksTVC = [[Tracks_TVC alloc] init];
tracksTVC.view.frame = _postView.frame;
tracksTVC.view.tag = kTagPostView;
[self.view addSubview:tracksTVC.view];
I get this error message when the code is run:
NSInternalInconsistencyException', reason: 'UITableView dataSource
must return a cell from tableView:cellForRowAtIndexPath:
I've implemented the data source in the separate UITableViewController's properly (I believe so) with all the required methods so I'm confused as to why I'm receiving this error. My only thought is that the added table view isn't using the methods in it's own UITableViewController..? Any help on this matter would be greatly appreciated!
You should create the property of TableViewController such as -
#property(nonatomic, strong) Tracks_TVC *tracksTVC;
Now in viewDidLoad initialise the same -
-(void)viewDidLoad
{
[super viewDidLoad];
self.tracksTVC = [[Tracks_TVC alloc] init];
self.tracksTVC.view.frame = _postView.frame;
self.tracksTVC.view.tag = kTagPostView;
[self.view addSubview:self.tracksTVC.view];
}
Note : Also make sure that you are returning the cell in Data source.

dismissWithClickedButtonIndex not works in UIViewController - UIScrollView - ActionSheet relation

I hope you can help me with the next case.
Description:
I have a class that "creates" a dynamic form programmatically, this class inherits from UIViewController and is called "DinamicScreen".
#interface DinamicScreen : UIViewController ...
I have a ViewController (connected to a viewController storyboard), which inherits from "DinamicScreen".
#interface MandatoryInformationViewController : DinamicScreen<UIPickerViewDelegate,UITextFieldDelegate>
In "DinamicScreen" I'm only painting the UIView, but all delegates are passed to the context.
uiTextField = [[UILabel alloc]initWithFrame:....
// Context is "MandatoryInformationViewController" in this case
[uiTextField setDelegate:context];
The form implements a UIScrollView containing UILabels and UITextFields. An UITextField can open an ActionSheet showing an UIPickerView and two buttons (OK, Cancel).
DinamicScreenScrollView *uiScrollView = [[DinamicScreenScrollView alloc]initWithFrame...
[uiScrollView addSubview:uiTextField];....
The problem:
When I click on a "pickerTextField" the ActionSheet appears without problems, but when I click OK or Cancel, it crashes (I think it is the ActionSheet).
When I did a debug, I observed that the flow does not run correctly
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
The snapshot http://s29.postimg.org/8zxkj333b/Captura_de_pantalla_2013_12_17_a_la_s_17_45_38.png
If I do not implement the UIScrollView it works perfectly, but its a must for it to be implemented.
If more information is needed please let me know, it's my first question on StackOverflow
Excuse me for my English, I'm working on it.
Thanks.
Well, after three days to fix it.
The problem was that were painting in the view of "MandatoryViewController" instead of "DinamicScreen", I changed that and voila.
[[context view] addSubview:uiScrollView]; // [wrong]
[[self view] addSubview:uiScrollView]; // [well]
I supposed that this post are a documentation now.
Thanks again.

How to initialize a custom view(controller) so it works both programmatically and in Interface Builder?

Suppose you implement a custom table view and a custom view controller (which mostly mimics UITableViewControllers behaviour, but when initialized programmatically, ...
#interface Foo : MyCustomTableViewController ...
Foo *foo = [[Foo alloc] init];
... foo.view is kind of class MyCustomTableView instead of UITableView:
// MyCustomTableView.h
#protocol MyTableViewDelegate <NSObject, UITableViewDelegate>
// ...
#end
#protocol MyTableViewDataSource <NSObject, UITableViewDataSource>
// ...
#end
#interface MyCustomTableView : UITableView
// ...
#end
// MyCustomTableViewController.h
#interface MyCustomTableViewController : UIViewController
// ...
#end
How should you implement/override init methods in correct order/ways so that you could create and use an instance of MyCustomTableView both by subclassing MyCustomTableViewController programmatically or from any custom nib file by setting custom class type to MyCustomTableView in Interface Builder?
It important to note that this is exactly how UITableView (mostly UIKit for that matter) works right now: a developer could create and use either programmatically or by creating from nib, whether be it File owner's main view or some subview in a more complex hierarchy, just assign data source or delegate and you're good to go...
So far I managed to get this working if you subclass MyCustomTableViewController, where I will create an instance of MyCustomTableView and assign it to self.view in loadView method; but couldn't figure out how initWithNibName:bundle:, initWithCoder:, awakeFromNib, awakeAfterUsingCoder:, or whatever else operates. I am lost in life cycle chain and end up with a black view/screen each time.
Thanks.
It is a real mystery how the UITableViewController loads its table regardless of if one is hooked up in interface builder, however I have came up with a pretty good way to simulate that behavior.
I wanted to achieve this with a reusable view controller that contains a MKMapView, and I figured out a trick to make it happen by checking the background color of the view.
The reason this was hard is because any call to self.view caused the storyboard one to load or load a default UIView if didnt exist. There was no way to figure out if inbetween those 2 steps if the user really didn't set a view. So the trick is the one that comes from a storyboard has a color, the default one is nil color.
So now I have a mapViewController that can be used in code or in storyboard and doesn't even care if a map was set or not. Pretty cool.
- (void)viewDidLoad
{
[super viewDidLoad];
//magic to work without a view set in the storboard or in code.
//check if a view has been set in the storyboard, like what UITableViewController does.
//check if don't have a map view
if(![self.view isKindOfClass:[MKMapView class]]){
//check if the default view was loaded. Default view always has no background color.
if([self.view isKindOfClass:[UIView class]] && !self.view.backgroundColor){
//switch it for a map view
self.view = [[MKMapView alloc] initWithFrame:CGRectZero];
self.mapView.delegate = self;
}else{
[NSException raise:#"MapViewController didn't find a map view" format:#"Found a %#", self.view.class];
}
}
The strategy I've used when writing such classes has been to postpone my custom initialization code as late as possible. If I can wait for viewDidLoad or viewWillAppear to do any setup, and not write any custom code in init, initWithNibName:bundle: or similar methods I'll know that my object is initialized just like the parent class no mater what way it was instantiated. Frequently I manage to write my classes without any overrides of these init methods.
If I find that I need to put my initialization code in the init methods my strategy is to write just one version of my initialization code, put that in a separate method, and then override all the init methods. The overridden methods call the superclass version of themselves, check for success, then call my internal initialization method.
If these strategies fail, such that it really makes a difference what way an object of this class is instantiated, I'll write custom methods for each of the various init methods.
This is how I solved my own issue:
- (void)loadView
{
if (self.nibName) {
// although docs states "Your custom implementation of this method should not call super.", I am doing it instead of loading from nib manually, because I am too lazy ;-)
[super loadView];
}
else {
self.view = // ... whatever UIView you'd like to create
}
}

Resources