View Objects don't connect to the File's Owner - iphone

I am trying to "Connect" my objects in the view to the File's Owner using the interface builder, but the blue line does not 'link' with it. Here is my code:
CalculatorViewController.h:
#import <UIKit/UIKit.h>
#import "CalculatorBrain.h"
#interface CalculatorViewController : UIViewController {
IBOutlet UILabel *display;
IBOutlet UIButton *button;
CalculatorBrain *brain;
BOOL userIsInTheMiddleOfTypingANumber;
}
- (IBAction)digitPressed: (UIButton *)sender;
- (IBAction)operationPressed: (UIButton *)sender;
#end
CalculatorViewController.m:
#import "CalculatorViewController.h"
#implementation CalculatorViewController
- (CalculatorBrain *)brain
{
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
- (IBAction)digitPressed:(UIButton *)sender
{
NSString *digit = [[sender titleLabel] text];
if (userIsInTheMiddleOfTypingANumber) {
[display setText: [[display text] stringByAppendingString:digit]];
} else {
[display setText:digit];
userIsInTheMiddleOfTypingANumber = YES;
}
}
- (IBAction)operationPressed: (UIButton *)sender
{
if (userIsInTheMiddleOfTypingANumber) {
[[self brain] setOperand: [[display text] doubleValue]];
userIsInTheMiddleOfTypingANumber = NO;
}
NSString *operation = [[sender titleLabel] text];
double result = [[self brain] performOperation:operation];
[display setText: [NSString stringWithFormat: #"%g", result]];
}
#end

Try and check if class of your File's Owner is set to your ViewController Class properly in your XIB.
For checking Go to your XIB
Click on File's Owner and Open the Inspector.
In inspector, go to the last(fourth) tab and check whether you have set your class as <yourViewControllerName>
Hope this helps you.
EDIT:
For better understanding I have added an image of where you need to look for the class.
Also please cross check that you have declared the variables with IBOutlet Prefix in your ViewController's header file

Once you have made sure the File's Owner is set to be CalculatorViewController, make sure that the IBOutlet type in the CalculatorViewController.h file matches the UI component type you are trying to connect it to. Your header defines an IBOutlet for a UILabel, which means only a UILabel can be connected to it.
If the component is of a different type, lets say a UIButton, then you would change your header file to include an IBOutlet like so:
IBOutlet UIButton *button;
Once you have defined the UIButton in your header, switch to IB. Double check that file's owner is set to your viewcontroller class, then add a UIButton to the view. Then you should be able to either:
ctrl+drag from the component to the file's owner
right-click on the file's owner and get a HUD styled popup showing all available IBOutlet's. click and drag from the UIButton one to the UIButton in your view.
If you want the UIButton in IB to trigger one of your defined IBActions, you will make a connection to the action. I usually perform this by right-clicking on the File's Owner, which will show all available IBActions and IBOutlets.
Hope this helps!

Did you set the file owner(in interface builder) to the class CalculatorViewController.

Im no expert on Interface Builder or Objective C, but did you use:
#property (nonatomic, retain) IBOutlet UIButton *yourButton;
and then in the .m file:
#synthesize yourButton;
Im also not sure, but you may have to 'build' it first, then
go into Interface Builder and then it may 'connect' up.

Related

Adding NSObject to view - UIButton selector in NSObject causes crash

having a little issue in an ARC environment. Creating an NSObject that adds a view to a parent view - it's basically a 'popup class' that can handle some text and display it.
In a view controller it's instantiated..
CBHintPopup *popup = [[CBHintPopup alloc]init];
[popup showPopupWithText:#"test text" inView:self.view];
And the actual class files..
CBHintPopup.h
#interface CBHintPopup : NSObject {
}
-(void)showPopupWithText:(NSString *)text inView:(UIView *)view;
-(IBAction)closePopup;
#property (nonatomic, strong) UIView *popupView;
#property (nonatomic, strong) UIImageView *blackImageView;
#property (nonatomic, strong) UIButton *closeButton;
#end
CBHintPopup.m
#implementation CBHintPopup
#synthesize popupView,blackImageView, closeButton;
-(void)showPopupWithText:(NSString *)text inView:(UIView *)view {
//CREATE CONTAINER VIEW
self.popupView = [[UIView alloc]initWithFrame:CGRectMake((view.frame.size.width/2)-(225/2),-146,225,146)];
self.popupView.alpha = 0;
self.popupView.backgroundColor = [UIColor clearColor];
//CREATE AND ADD BACKGROUND
UIImageView *popupBackground = [[UIImageView alloc]initWithFrame:CGRectMake(0,0,225,146)];
popupBackground.image = [UIImage imageNamed:#"hintbackground.png"];
[self.popupView addSubview:popupBackground];
//CREATE AND ADD BUTTON
self.closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.closeButton addTarget:self action:#selector(closePopup) forControlEvents:UIControlEventTouchUpInside];
[self.popupView addSubview:self.closeButton];
//CREATE AND ADD LABEL
UILabel *popupTextLabel = [[UILabel alloc]initWithFrame:CGRectMake(22,25,176,93)];
popupTextLabel.text = text;
[self.popupView addSubview:popupTextLabel];
[view addSubview:self.popupView];
}
-(void)closePopup {
NSLog(#"HI");
}
Recieving the following once closePopup is called via pressing the button ('HI' is not printed)..
-[CBHintPopup performSelector:withObject:withObject:]: message sent to deallocated instance 0x246b2fe0
I've tried retaining the button in non-ARC and a load of other methods but simply having no luck. Probably something real simple but i can't nail it. I've removed all the setting up of the labels and images etc to save some space, so ignore alpha's etc.
Any help will be much appreciated, thanks for your time.
Have you implemented the constructor for CBHintPopup,since you have called the constructor
[[CBHintPopup alloc]init];
you have to implement the constructor method like this
in .m file of CBHintPopup
-(id)init{
if(self == [super init]){
// do some initialization here
}
return self;
}
I tried your code and found the crash you mentioned. I found a solution for fixing the crash.
I declared the CBHintPopup *popup; in the viewController's interface. And changed this line
CBHintPopup *popup = [[CBHintPopup alloc]init];
to
popup = [[CBHintPopup alloc]init];
Everything worked fine for me. But I couldn't find the reason behind this. Hope this will help you.
Found a fix for it - instead of CBHintPopup being an NSObject, i simply made it a sub-class of UIView and added self to the parent view (instead of self.popupView). I wouldn't really call this a 'fix' though - more of an alternative method. Surely an NSObject can add a UIView (with a UIBUtton) to a parent view with no problems? Is this a bug?
Make sure you are retaining the object for class CBHintPopup.
I think the crash is coming because object of CBHintPopup deallocates. And hence the action method is not found.

How to make reference to a UIButton in viewDidLoad?

This is my action UIButton:
-(IBAction)favoriteButtonPressed:(id)sender
{
if (favoriteButtonSelected == 0) {
[sender setSelected:YES];
favoriteButtonSelected = 1;
[sender setImage:[UIImage imageNamed:#"favoritedItem.png"]];
[selectedObject setValue:#"Yes" forKey:#"Favorite"];
} else {
[sender setSelected:NO];
favoriteButtonSelected = 0;
[sender setImage:[UIImage imageNamed:#"notFavorite.png"]];
[selectedObject setValue:#"No" forKey:#"Favorite"];
}
}
How to make a reference to the button in viewDidLoad? To make the following code work:
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[selectedObject valueForKey:#"Favorite"] isEqual:#"Yes"]) {
[favoriteButton setImage:[UIImage imageNamed:#"favoritedItem.png"]];
[favoriteButton setSelected:YES];
favoriteButtonSelected = 1;
} else {
[favoriteButton setImage:[UIImage imageNamed:#"notFavorite.png"]];
[favoriteButton setSelected:NO];
favoriteButtonSelected = 0;
}
}
EDIT FOR PROGRESS:
Now I did like this: Ctrl-drag from UIButton to ViewController in Assistant Editor. Connection: Outlet, name: favoriteButton, type: UIButton, storage: weak. But errors still there. + error for synthesize & error in viewDidUnload.. suggestion?
The Assistant Editor header for View Controller with the added property from Ctrl-drag:
#interface DetailViewController : UIViewController {
IBOutlet UIScrollView *viewScroller;
}
#property (nonatomic, strong) IBOutlet UILabel *mylLabel;
#property (nonatomic, strong) NSString *selectedObj;
#property (strong, nonatomic) NSArray *detailsDataSource;
#property int detailIndex;
#property (weak, nonatomic) IBOutlet UIButton *favoriteButton; //The added property
#end
Control drag from your button to the header of the viewcontroller to create a property. (You can do this while in assistant mode, second button on the top right)..
Then you can reference your button from wherever using that property
What you need is an IBOutlet for the button.
You can ctrl+drag the button from XIB file to the header of this class, and create a IBOutlet property named favoriteButton.
Well unless you are using the latest version of Xcode you do need to #synthesize favorite button.
You really should post your error messages you are just making people guess.

Custom UINavigationController with button

I would like the NavigationBar to behave the same but would like to change the appearance of it. I've found so many ways of doing this online but I'm not sure which one is the best result for iOS 5.0. The navigation bar will look like this:
Since you are targeting iOS 5 i would definitely go for customizing UINavigationBar using the Appearance proxy. Then you can easily set your own images and they will apply to all navigation bars in your application without subclassing.
You can also customize the buttons in the navigation bar by customizing UIBarButtonItem. There are method like backButtonBackgroundImageForState:barMetrics: for the back button and backgroundImageForState:barMetrics: for the other buttons.
I had been looking for this thing for ages, too, without finding a straightforward solution! Thanks to an friend of mine, and sample codes, we made it with a custom navigation bar class that can be imported into any view controller class.
The .h file:
#import <UIKit/UIKit.h>
#interface NATitleBar : UIView {
NSInteger tag;
}
#property ( nonatomic) IBOutlet UIImageView *imageView;
#property ( nonatomic) IBOutlet UILabel *label;
#property ( nonatomic) IBOutlet UIButton *back;
#property ( nonatomic) IBOutlet UIButton *home;
/**
* Supports UIButton-style adding targets
*/
#end
The .m file:
#import "NATitleBar.h"
#implementation NATitleBar
#synthesize imageView;
#synthesize label;
#synthesize back;
#synthesize home;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"NATitleBar" owner:self options:nil];
[self addSubview:[views objectAtIndex:0]];
// customize the view a bit
//self.imageView.layer.borderWidth = 1.0;
//self.imageView.layer.borderColor = [UIColor colorWithWhite:0.4 alpha:0.4].CGColor;
//self.imageView.clipsToBounds = YES;
//self.imageView.layer.cornerRadius = 5.0;
}
return self;
}
#pragma mark - Overriden Setters / Getters
- (void)setTag:(NSInteger)aTag {
self.back.tag = aTag;
}
- (NSInteger)tag {
return self.back.tag;
}
#end
and then for the Nib file we have the following:
You can add or delete images in the Nib file to make the GUI as you wish.
Now you must import the class into any view controller you wish to have with custom navigation controller, and also define two methods (or one, if you don't want the 'home' button. in .h :
- (void) back;
in .m:
- (void)back {
[self.navigationController popViewControllerAnimated:YES];
}

How to assign multiple UIButtons to a UITextView

I want to assign two UIButtons to UITextView so that when one of the buttons is pressed the textview content should change from what it had when the previous button was pressed.
MyViewController.h:
#interface MyViewController : UIViewController
{
NSString* savedText;
}
#property(nonatomic, retain) IBOutlet UITextView* textView;
- (IBAction)buttonOnePressed:(id)sender;
- (IBAction)buttonTwoPressed:(id)sender;
#end
Connect the view controller's textView outlet to the text view in the xib file. Connect the buttons to the corresponding actions. (See Apple's Xcode tutorial if you don't know how to do this.)
MyViewController.m:
- (void)buttonOnePressed:(id)sender
{
[savedText release];
savedText = [textView.text copy];
}
- (void)buttonTwoPressed:(id)sender
{
textView.text = savedText;
}
(These aren't the complete files, of course, just the bits to make it do what you're asking.)

UIView subclass with its own XIB [duplicate]

This question already has answers here:
UIView and initWithFrame and a NIB file. How can I get the NIB file loaded?
(5 answers)
Closed 7 years ago.
I created a custom UIView subclass, and would prefer to not layout the UI in code in the UIView subclass. I'd like to use a xib for that. So what I did is the following.
I created a class "ShareView" which subclasses UIView. I created a XIB file with its file's owner set to "ShareView". Then I link some outlets I declared in my "ShareView.h".
Next I have a ViewController, MainViewController, which adds the ShareView as a subview. whith this code:
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:#"ShareView" owner:nil options:nil];
UIView *fv = [[arr objectAtIndex:0] retain];
fv.frame = CGRectMake(0, 0, 320, 407);
[self.view addSubview:fv];
But now I get NSUnknownKeyException errors on the outlets I declared in my ShareView.
The reason I did all this is because I want a UIView, with its own logic in a seperate XIB file. I read in several places that ViewControllers are only used to manage a full screen, i.e. not parts of a screen...
So what am I doing wrong? I want my logic for ShareView in a seperate class, so my MainController class doesn't get bloated with logic from ShareView (which I think is an aption to solve this problem?)
ThomasM,
We had similar ideas about encapsulating behavior inside a custom view (say, a slider with companion labels for min/max/current values, with value-changed events also handled by the control internally).
In our current best-practice, we would design the ShareView in Interface Builder (ShareView.xib), as described by Eimantas in his answer. We then embed the ShareView to the view hierarchy in MainViewController.xib.
I wrote up how we embed custom-view Nibs inside other Nibs in our iOS developer blog. The crux is overriding -awakeAfterUsingCoder: in your custom view, replacing the object loaded from MainViewController.xib with the one loaded from the "embedded" Nib (ShareView.xib).
Something along these lines:
// ShareView.m
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);
if (theThingThatGotLoadedWasJustAPlaceholder) {
// load the embedded view from its Nib
ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0];
// pass properties through
theRealThing.frame = self.frame;
theRealThing.autoresizingMask = self.autoresizingMask;
[self release];
self = [theRealThing retain];
}
return self;
}
You defined owner of the loaded xib as nil. Since file owner in xib itself has outlets connected and is defined as instance of ShareView you get the exception about unknown keys (nil doesn't have outleted properties you defined for ShareView).
You should define the loader of the xib as owner (i.e. view controller responsible for loading the xib). Then add separate UIView object to xib and define it as instance of ShareView. Then when loading the xib.
ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:#"ShareView" owner:self options:nil] objectAtIndex:0] retain];
You can also define shareView as an IBOutlet in view controller's interface (and connect the outlet from file owner to that view in the xib itself). Then when you load the xib there won't be any need for reassigning the shareView instance variable since the xib loading process will reconnect the view to the instance variable directly.
I would like to add to the answer. I hope people would improve this answer though.
First of all it DOES work.
XIB:
Result:
I would like to subclass UIView for a long time especially for tableViewCell.
This is how I did it.
It's succesful, but some part is still "awkward" in my opinion.
First I created a usual .h, .m, and xib file. Notice that Apple do not have the check box to automatically create an xib if the subclass you created is not a subclass of UIViewController. Well create those anyway.
#import <UIKit/UIKit.h>
#import "Business.h"
#interface BGUIBusinessCellForDisplay : UITableViewCell
+ (NSString *) reuseIdentifier;
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz;
#end
Really simple UITableViewCell, that I want to initialize latter with biz.
I put reuseidentifier which you should do for UITableViewCell
//#import "Business.h"
#interface BGUIBusinessCellForDisplay ()
#property (weak, nonatomic) IBOutlet UILabel *Title;
#property (weak, nonatomic) IBOutlet UIImageView *Image;
#property (weak, nonatomic) IBOutlet UILabel *Address;
#property (weak, nonatomic) IBOutlet UILabel *DistanceLabel;
#property (weak, nonatomic) IBOutlet UILabel *PinNumber;
#property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view;
#property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection;
#property (weak, nonatomic) Business * biz;
#end
#implementation BGUIBusinessCellForDisplay
- (NSString *) reuseIdentifier {
return [[self class] reuseIdentifier];
};
+ (NSString *) reuseIdentifier {
return NSStringFromClass([self class]);
};
Then I eliminated most init codes and put this instead:
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz
{
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
if (biz==nil)
{
return self;
}
self.biz = biz;
self.Title.text = biz.Title; //Let's set this one thing first
self.Address.text=biz.ShortenedAddress;
//if([self.distance isNotEmpty]){
self.DistanceLabel.text=[NSString stringWithFormat:#"%dm",[biz.Distance intValue]];
self.PinNumber.text =biz.StringPinLineAndNumber;
Notice that it's really awkward.
First of all the init can be used in 2 ways.
It can be used to right after aloc
It can be used by we having another existing class and then we just want to init that existing cell to another biz.
So I did:
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
Another icky things that I did is when I do [self addSubview:self.view];
The thing is I want self to be the view. Not self.view. Somehow it works nevertheless. So yea, please help me improve, but that's essentially the way to implement your own subclass of UIView.
You can create your custom UIView designed in xib and even make Interface Builder to display it inside other xib files or storyboards in new Xcode 6 using IB_DESIGNABLE. In xib set file owner to your custom class but do not set UIView class to avoid recurrency loading problems. Just leave default UIView class and you will add this UIView as a subview of your custom class view. Connect all your outlets to file owner and in your custom class load your xib like in the code below. You can check my video tutorial here: https://www.youtube.com/watch?v=L97MdpaF3Xg
IB_DESIGNABLE
#interface CustomControl : UIView
#end
#implementation CustomControl
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self load];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self load];
}
return self;
}
- (void)load
{
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:#"CustomControl" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
#end
If you are using autolayout then you might want to change: view.frame = self.bounds; to:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
To use Yang's pattern with Auto-Layout, you need to add the following somewhere in the -awakeWithCoder: method.
theRealThing.translatesAutoresizingMaskIntoConstraints = NO;
If you don't turn off -translatesAutoResizingMaskIntoConstraints it can cause your layout to be incorrect as well as causing a LOT of debugging nonsense in the console.
EDIT: Auto-layout can still be a pain. Certain constraints aren't respected, but other are (e.g. pinning to the bottom doesn't work but pinning to the top does). We're not exactly sure why, but you can work around this by manually passing constraints from the placeholder to theRealThing.
It's also worth noting that this pattern works just the same way with Storyboards as it does with regular .xibs (i.e. you can create a UI Element in a .xib and drop it into a StoryBoard View controller by following your steps.)
Instead of subclassing UIView why don't you subclass UIViewController. Check out the following link. In that made a "RedView" and "BlueView" UIViewControllers with their xibs and added them to the MultipleViewsController view by creating and instance of the former two classes and adding [self.view addSubview:red.view] and [self.view addSubview:blue.view] in the MultipleViewsController's viewDidLoad method
MultipleControllers in one view
Just add (id)sender to the button pressed function in RedView and BlueView in the code of the above link.