Use a nib as a table section header with nib class - ios

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.)

Related

Objective-C Using custom XiB in another XiB

I've really spent my hours for finding any approach about this problem.
I have;
CustomView.h that extends from UIView and is also IB_DESIGNABLE
CustomView.m for implementation of this view with override methods init, initWithCoder and initWithFrame
CustomView.xib that bound to CustomView class with all properties
And, I have :
CustomTableViewCell.h that extends from UITableViewCell
CustomTableViewCell.m that implementation of this cell
CustomTableViewCell.xib that bound to CustomTableViewCell class with all properties&outlets.
CustomTableViewCell.xib includes CustomView...
There is no any error, but the area of CustomView remains blank.
For view you need to do some work around.
Create a property and link your view in the XIB to it;
#property (strong, nonatomic) IBOutlet UIView *xibView;
Then have something like this:
- (void)awakeFromNib{
[super awakeFromNib];
[self commonInit];
}
- (instancetype)init{
self = [super init];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit{
//this will work if the view has the same name as the XIB
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
self.xibView.frame = self.bounds;
[self addSubview:self.xibView];
self.xibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
Basically you have to load the XIB manually and add it to your custom view.

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

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

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.

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.)

iOS Custom Delegate Returns Nil after setDelegate called from viewController

I have been having a strange problem using a custom delegate class in ARC. In the viewcontroller after viewDidLoad I call the toolbars setDelegate method and assign the viewcontroller as the delegate.
Whenever I try to reference delegate, it is nil, even after being set in the
viewcontroller.
//MPToolbar.h
#import <UIKit/UIKit.h>
//Define the protocol for the delegate
#class MPToolBar;
#protocol MPToolBarDelegate <NSObject>
#required
- (void)writeButtonSelected;
- (void)writeButtonDeSelected;
#end
#interface MPToolBar : UIView{
id <MPToolBarDelegate> delegate;
}
#property(strong) UIButton *lastButton;
#property(strong) id <MPToolBarDelegate> delegate;
-(IBAction)buttonSelected:(id)sender;
-(void)resizeForOrientation:(UIInterfaceOrientation)orientation;
#end
//MPToolbar.m
#import "MPToolBar.h"
#implementation MPToolBar
#synthesize lastButton, delegate;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Initialization code
}
NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:#"MPToolBar" owner:self options:nil];
[self addSubview:[nibViews objectAtIndex:0]];
return self;
}
#pragma button actions
-(IBAction)buttonSelected:(id)sender{
UIButton *btn = (UIButton *)sender;
if(lastButton && lastButton != btn)[self deselect:lastButton];
if (btn.selected){
[self deselect:btn];
return;
}
[btn setSelected:YES];
lastButton = btn;
switch (btn.tag) {
case 0:
[self.delegate writeButtonSelected];
break;
}
}
-(void)deselect:(UIButton *)btn{
[btn setSelected:NO];
switch (btn.tag) {
case 0:
[self.delegate writeButtonDeSelected];
break;
}
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
-(void)resizeForOrientation:(UIInterfaceOrientation)orientation{
if (UIInterfaceOrientationIsLandscape(orientation)) {
self.frame = CGRectMake(0, 44, 1024, 60);
}
if (UIInterfaceOrientationIsPortrait(orientation)) {
self.frame = CGRectMake(0, 44, 768, 60);
}
}
#end
//ViewTest.h
#import <UIKit/UIKit.h>
#import "MPToolBar.h"
#interface ViewTest : UIViewController <MPToolBarDelegate>
{
MPToolBar *toolBar;
}
#property(strong) MPToolBar *toolBar;
#end
//ViewTest.m
#import "ViewTest.h"
#interface ViewTest ()
#end
#implementation ViewTest
#synthesize toolBar;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.toolBar = [[MPToolBar alloc] initWithFrame:CGRectMake(0, 44, 1024, 60)];
[self.view addSubview:self.toolBar];
[self.toolBar setAlpha:1.0];
[self.toolBar setDelegate:self];
// Do any additional setup after loading the view from its nib.
}
#pragma mark - MPToolBar Delegate Methods
-(void)writeButtonSelected{
//set new annotation for write mode and size to screen bounds.
NSLog(#"writeButtonSelected");
}
- (void)writeButtonDeSelected{
NSLog(#"writeButtonDeSelected");
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
Ok so I finally found the problem. In my xib file I had set the UIView class to my custom controls class name instead of setting files owner.
once I changed the first UIView in the xib back to a standard UIView and then set the files owner to my custom class all was right with the world again!
-Thanks for everyone who tried to help!
Several things that could be causing problems:
Do you actually declare your view controller as a MPToolBarDelegate in its header? (that code is not present).
These are the delegate functions in your code:
-(void)ButtonSelected;
-(void)ButtonDeSelected;
However, you're actually invoking this one:
[_delegate writeButtonSelected];
which is not part of your protocol.
Try changing your delegate declarations like this:
#interface MPToolBar : UIView{
id delegate;
}
#property (nonatomic, assign) id <MPToolBarDelegate> delegate;
then when you call your delegate method use this:
[self.delegate ButtonSelected];
Also, the original iPad is upgradeable to iOS 5.0. If you are using ARC you can't be compatible with 4.3 anyway, ARC only started at 5.0 as I understand it.
With a similar symptom for me, my problem was that the segue's destinationViewController was NOT the object I was expecting it to be, because it was embedded in a UINavigationController.
Easy fix. See here. Lose pointer to Delegate while going through Navigation Controller

Resources