Use a UIViewController's Subclass property as IBOutlet in another Subclass - ios

I have a UIViewController's subclass, we call it MySuperClass , this subclass has a UITableView property which is than initalized programmatically.
Now I want to subclass MySuperClass to MySubclass but this time I'd like to design the tableview via Interface Builder rather than programmatically.
What I want is something similar to how UIViewController works, if you subclass UIViewController it has its view property already initalized, but when you bring it into IB you can link it to an Interface Builder's UIView item, how do I do this?
the source code of my superclass is something similar to this one:
//interface
#import <UIKit/UIKit.h>
#interface MySuperClass : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) UITableView *tableView;
//implementation
#import "MySuperClass.h"
#implementation MySuperClass
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self initializeProperties];
}
return self;
}
- (void) awakeFromNib{
[super awakeFromNib];
[self initializeProperties];
}
- (void) initializeProperties{
self.tableView = [[UITableView alloc] initWithFrame: self.view.frame style: UITableViewStylePlain];
self.tableView.separatorColor = [UIColor clearColor];
self.tableView.backgroundColor = [UIColor clearColor];
self.tableView.delegate = self;
self.tableView.dataSource = self;
UIView *tableHeaderView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, self.view.frame.size.width, self.bannerView.frame.size.height+kBannerDistance)];
tableHeaderView.backgroundColor = [UIColor clearColor];
self.tableView.tableHeaderView = tableHeaderView;
}

Just "redeclare" the #property in your subclass.
#import <UIKit/UIKit.h>
#import "MySuperClass.h"
#interface MySubClass : MySuperClass
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#end
The compiler will be clever enough to understand your are referencing the super class property, and IB will have no problem linking to the subclass's one.

This is probably not be the best solution, but it should get it done.
Define - initFromSubClassWithNibName: bundle:; in MySuperClass.h and implement it like this:
- (id) initFromSubClassWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
}
and in MySubClass
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
}
This way you can escape MySuperView's implementation of the init method and use UIViewController's implementation. You can take the same approach withawakeFromNib`. This will avoid creating a table view programmatically.
Then you can take GuillaumeA's answer to initialize tableView from the IB.

Related

Table view controller with static cells

i have a problem with my application,so when i put this code in InfoViewController and i run the application,the view appears to be empty.I have a table view setted to static cells,3 sections,style grouped with header for every section,the first cell contain a text field that i have used as a search field with google,and a search button,the second and the third contains a label.I have connected all the text field button and labels in the storyboard file,and the delegate in file’s owner.Should I Implement any other method?
Thanks for the advice.
#import <UIKit/UIKit.h>
#interface InfoViewController : UIViewController <UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *googleSearchTextField;
#property (weak, nonatomic) IBOutlet UIButton *searchButton;
#property (weak, nonatomic) IBOutlet UILabel *Label1;
#property (weak, nonatomic) IBOutlet UILabel *Label2;
-(IBAction)googleSearch;
#end
#import "InfoViewController.h"
#end
#implementation InfoViewController
#synthesize googleSearchTextField;
#synthesize searchButton;
#synthesize Label1;
#synthesize Label2;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.Label1.text = #“Label1”;
self.Label2.text = #“Label2”;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
return YES;
}
-(IBAction)googleSearch
{
NSString *searchString=googleSearchTextField.text;
NSString *urlAdress = [NSString stringWithFormat:#"http://www.google.com/search?q=%#",
searchString];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlAdress]];
}
#end
You need to add your tableViewDelegate methods and TableViewDataSource methods
Check https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/CreateConfigureTableView/CreateConfigureTableView.html#//apple_ref/doc/uid/TP40007451-CH6-SW10

Performing method on another UIViewController

I am using following code to set the text of a UILabel in SViewController, From another ViewController FViewController
SViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"SViewController"];
[vc setTextForLabel:[NSNumber numberWithInt:1];];
[self.navigationController pushViewController:vc animated:YES];
The setTextForLabel method:
-(void) setTextForLabel: (NSNumber *)text {
textLabel = [[UILabel alloc] init];
[textLabel setText:[NSString stringWithFormat:#"%#",[text stringValue]]];
NSLog(#"The text is %#", textLabel.text);
}
I have declared the property for textLabel in .h file is as follows:
#property (nonatomic, retain) IBOutlet UILabel *textLabel;
And I have also set the IBOutlet in storyboard.
Now in console I see "The text is 1", but the UI is not showing anything!
What am I doing wrong?
remove this line
textLabel = [[UILabel alloc] init];
Also, you need to link the UILabel on interface to your #property (nonatomic, retain) IBOutlet UILabel *textLabel;
Or you may add this label manually to view
textLabel = [[UILabel alloc] init];
[textLabel setText:[NSString stringWithFormat:#"%#",[text stringValue]]];
[self.view addSubView:textLabel];
First of all go put
[vc setTextForLabel:[NSNumber numberWithInt:1]];
after you push the vc.
In your xib/storyboard if your label has something written in it, remove it, leave it blank.
If that doesn't work ... leave the setText method before the push one (as you wrote it initialy) and create a NSNumber member that will hold your value and apply it in viewDidAppear like so
-(void) setTextForLabel: (NSNumber *)text{
numberValue = text; // declared in .h as NSNumber *numberValue (only make it a property if you need acces to it from outside your class)
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated]; //this is important
[textLabel setText:[NSString stringWithFormat:#"%#",[numberValue stringValue]]];
}
Your problem is linked to the view lifecycle of SViewController. When you call [.. setTextForLabel] you don't know if the controller has loaded its views (And thus your UILabel have a good chance of not being created yet)
What you should do is use a NSString property on your controller, set it with the desire text and assigned it to the UILabel in the viewDidLoad of SViewController.
in SViewController.h
#propery (strong, nonatomic) NSString *textToDisplay;
in SViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
// Here you know all Outlet are loaded and connected
textLabel.text = textToDisplay
}
I did like this
#import "FViewController.h"
#import "SViewController.h"
#interface FViewController ()
#end
#implementation FViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)do:(id)sender
{
SViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"SViewController"];
[self.navigationController pushViewController:vc animated:YES];
[vc setTextForLabel:[NSNumber numberWithInt:1]];
}
// in SViewController.h
#import <UIKit/UIKit.h>
#interface SViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *label;
-(void) setTextForLabel: (NSNumber *)text;
#end
// SViewController.m file
#import "SViewController.h"
#interface SViewController ()
#end
#implementation SViewController
#synthesize label;
- (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.
}
-(void) setTextForLabel: (NSNumber *)text{
// label.text = [text stringValue];
NSLog(#"%#",label);
[label setText:[text stringValue]];
}
#end
also check your label properties in storyboard are properly set

Classical MVC in Cocoa

I'm trying to structure some view-code out of the controller (obviously without using a nib). Therefore, I tried a simple example, but for some reason, I can't add the target to the button in the controller, rest is fine. Here's what I'm trying:
Controller.h:
#import <UIKit/UIKit.h>
#import "IndexView.h"
#interface IndexController : UIViewController
{
}
#property (nonatomic) IndexView *contentView;
#end
Controller.m
#import "IndexController.h"
#import "IndexView.h"
#interface IndexController ()
#end
#implementation IndexController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.contentView = [[IndexView alloc]init];
[self.view addSubview:self.contentView];
[self connectUIElements];
}
return self;
}
- (void) connectUIElements
{
[self.contentView.testButton addTarget:self action:#selector(testButtonClicked) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark --
#pragma mark UIHandler
- (void) testButtonClicked
{
NSLog(#"testbutton clicked");
}
#end
View.h
#import <UIKit/UIKit.h>
#interface IndexView : UIView
#property (nonatomic, strong) UIButton *testButton;
#end
View.m
#import "IndexView.h"
#implementation IndexView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setUpView];
}
return self;
}
- (id) init
{
self = [super init];
if (self) {
[self setUpView];
}
return self;
}
- (void) setUpView
{
[self setBackgroundColor:[UIColor whiteColor]];
self.testButton = [[UIButton alloc]initWithFrame:CGRectMake(10, 50, 100, 50)];
self.testButton.backgroundColor = [UIColor greenColor];
[self.testButton setTitle:#"hello, world" forState:UIControlStateNormal];
[self addSubview:self.testButton];
}
#end
I'm just exploring possibilities to get closer to a classical MVC pattern and a little farther away from Apples mediator-interpretation. Any idea what's wrong?
You have to create your view using initWithFrame:, which is the default initializer for UIView. (The reason that is does not work with a simple init - but I am guessing here! - might be that the views size is zero and therefore the touch events do not work.)

Use a nib as a table section header with nib class

I want to create a section header that loads a nib file and sets that as the header UIView. This nib file will also have an associated class where the outlets and actions are connected to so I want to load that class with the nib like normal.
I've scoured the web and found several similar answers but I can't get any to work for me. After playing around for a few days I managed to get the view to show correctly but it doesn't do anything despite adding connections and telling the text to show differently.
For example it should clear the section title text if it's init with nil and it does that yet it still shows the same text, attempts to change it doesn't reflect either and any connections made with the button aren't triggered.
Here is my view controller for the view
#interface ADUNewRowHeaderViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *sectionTitleField;
#property (strong, nonatomic) IBOutlet UIButton *addRowBtn;
#property(strong,nonatomic) NSString* sectionTitle;
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
title:(NSString*) titleOrNil;
#end
and here is the implementation
#implementation ADUNewRowHeaderViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil title:(NSString *)titleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
if(titleOrNil != nil)
{
[self setSectionTitle:titleOrNil];
}
else
{
[self setSectionTitle:#""];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)setSectionTitle:(NSString *)sectionTitle
{
_sectionTitle = sectionTitle;
[[self sectionTitleField] setText:sectionTitle];
}
#end
in the actual table view controller it's listed as
#property(nonatomic, strong) ADUNewRowHeaderViewController* secHeader;
and in the implementation file under viewDidLoad as
[self setSecHeader:[[ADUNewRowHeaderViewController alloc] initWithNibName:nil bundle:nil title:nil]];
[[[self secHeader] addRowBtn] addTarget:self action:#selector(addNewRow:) forControlEvents:UIControlEventTouchUpInside];
and
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return [[self secHeader] view];
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return [[self secHeader] view].bounds.size.height;
}
this is the method declaration for the action
- (IBAction)addNewRow:(id)sender;
You should make a UIView, instead of a UIViewController, subclass. The .m would contain:
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self setUp];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self setUp];
}
- (void)setUp
{
[[NSBundle mainBundle] loadNibNamed:#"YourNibName" owner:self options:nil];
CGRect frame = self.frame;
self.view.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
[self addSubview:self.view];
... do other initialisations here ...
}
And the .h:
#interface ADUNewRowHeaderView : UIView
#property (nonatomic, strong) IBOutlet UIView* view;
Then in the XIB: Make File's Owner of class ADUNewRowHeaderView, as usual. And connect the XIB's top-level View's Referencing Outlet to the view property above (i.e. in File's Owner).
You then have a UIView subclass you can place on another XIB (as a UIView which for which you set the Class to ADUNewRowHeaderView), or instantiate in code and add as subview.
(Alternatively you could create the UIView and it's subviews (buttons, labels, ...) in code; then you would not need a XIB. But this only works of course if the UIView is simple and has little own logic, and few UI elements that are easy to layout in code.)

Adding a Navigation Controller but navigation item not displaying

Originally I had a custom NavigationBar, without a navigation controller. I have a sliding menu.
On the home screen there is a tableview, with a list. I would like to be able to add items to this list. So, I had to add in a navigation controller after the fact. I added the navigation controller to the storyboard and linked it to my main view controller [the one with the list]. Then I dragged in a view controller. I dropped in a bar button item into the navigation item, and pushed it to the recently added view controller.
I ran my app and the app shows no navigation bar or navigation item whatsoever now. Originally I had a custom navigation bar.
I'm not too sure what's going on here. Thanks for any help.
- (void)customizeAppearance {
UIImage *NavBG = [UIImage imageNamed:#"nav bar.png"];
[[UINavigationBar appearance] setBackgroundImage:NavBG forBarMetrics:UIBarMetricsDefault];
init View Controller code from .m
#import "initViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#interface initViewController ()
#end
#implementation initViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Main"];
}
from .h
#import "ECSlidingViewController.h"
#interface initViewController : ECSlidingViewController
#end
from main.m
#import "MainViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
{
NSArray *toDoList;
NSIndexPath *selectedIndexPath;
}
#synthesize menuBtn;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
from main.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
}
#property (strong, nonatomic) UIButton *menuBtn; // go to MenuViewController.m and synthesize
#end
Second view controller.m file
#import "SecondViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize menuBtn;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Below is the same stuff pasted from MainViewController.m
// Do any additional setup after loading the view.
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowOpacity = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 10, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
}
Here are all the arrays I use
From the image you are showing, it doesn't look like any of the VCs have the starting arrow to its left. If that is the case, go to SB, click on the Nav controller, go to Attributes inspector on the right,scroll down and check the "Is initial View Controller".
Hope this answers it.
Update: Guessing at your code, you are trying to modify an immutable NSArray. Change your declaration to NSMutableArray *toDoList.

Resources