Why isn't initWithCoder initializing items correctly? - objective-c

I've got a subclass of UITableViewCell. My subclass contains several UILabels. I want my subclass to initialize these labels to some default settings that applies to every table view cell of mine.
I read that I should be using initWithCoder for this. My initWithCoder function is being called, and I can step through each line in my function and it appears to go through the motions. When I run my application I do not see any of the properties being applied, though. Most noticeably, the font is not being changed. I just don't think any of the properties that I'm modifying on my UILabels are actually being saved, or displayed.
I'm using this subclass in conjunction with a Storyboard. I know my changes will not be reflected on the Storyboard, but they're also not being reflected when the application runs - despite my code being executed.
Edit: I wanted to mention that prior to trying to override initWithCoder, I had an instance method in these subclasses that I'd run this logic in. I would just call that instance method inside of cellForRowAtIndexPath. This method was working, but I thought it'd be handy to have this logic in this subclass occur automatically.
Any ideas? My code is below:
#import "ThreadListCell.h"
#implementation ThreadListCell
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
// adjust the font settings of the title
self.title.font = [UIFont fontWithName:#"SourceSansPro-Black" size:16];
self.title.textColor = [UIColor colorWithWhite:0.267f alpha:1.0f];
// adjust the font settings of the subtitle
self.text.font = [UIFont fontWithName:#"SourceSansPro-Light" size:14];
self.text.textColor = [UIColor colorWithWhite:0.267f alpha:0.9f];
self.text.textColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
// adjust the font settings of the location
self.location.font = [UIFont fontWithName:#"SourceSansPro-Light" size:8];
self.location.textColor = [UIColor colorWithWhite:0.267f alpha:0.9f];
// adjust the UILabel settings of the title
self.title.numberOfLines = 0;
self.title.lineBreakMode = UILineBreakModeTailTruncation;
// adjust the UILabel settings of the subtitle
self.text.numberOfLines = 0;
self.text.lineBreakMode = UILineBreakModeTailTruncation;
// adjust the UILabel settings of the location
self.location.numberOfLines = 0;
self.location.lineBreakMode = UILineBreakModeTailTruncation;
self.location.textAlignment = UITextAlignmentRight;
}
return self;
}
#end
And the header:
#import <UIKit/UIKit.h>
#interface ThreadListCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *text;
#property (nonatomic, weak) IBOutlet UILabel *title;
#property (nonatomic, weak) IBOutlet UILabel *location;
#end

None of your IBOutlets is set when you get the message - you need to use "awakeFromNib" or "viewDidLoad" - something like that - to access your outlets. That said, you can set other non-outlet stuff in the initWithCoder.

Related

Can't change the frames of .xib UI objects in code

I am trying to re-write my code to allow me to use xibs to design custom UIViews for re-use throughout my projects. I have created a class called SlidesView which has a .xib of the same name. I have set it up using the following IBOutlets:
#property (nonatomic, retain) IBOutlet UIImageView *ivMain;
#property (nonatomic, retain) IBOutlet UILabel *lHeader;
#property (nonatomic, retain) IBOutlet UITextView *tvMain;
#property (nonatomic, retain) IBOutlet UIImageView *ivIcon;
I have made sure these are all properly connected within the .xib file. I also have a function which I want to use to setup all the initial data for the view:
- (void)setupUsingCenter:(CGPoint)center mainImage:(UIImage *)mainImage mainText:(NSString *)mainText header:(NSString *)header andIconImage:(UIImage *)iconImage
The 'setupUsingCenter:self ...' body looks as follows:
// First check for a main image and image icon
if (mainImage != nil)
{
// Set the image
[self.ivMain setImage:mainImage];
[self.ivMain setFrame:CGRectMake(10, 10, 10, 10)];
// If image icon set that too
if (iconImage != nil)
{
[self.ivIcon setImage:iconImage];
}
}
And finally I load this .xib using the following code:
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"SlidesView" owner:nil options:nil];
for (UIView *view in views) {
if([view isKindOfClass:[SlidesView class]])
{
SlidesView *slidesView = (SlidesView *)view;
[slidesView setupUsingCenter:self.vPlaceholder.center mainImage:slideImage mainText:slideText header:slideTitle andIconImage:slideIcon];
[slidesView setTextColours:[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f]];
[self.view addSubview:slidesView];
self.viewPopup = slidesView;
}
}
I have put in the random setFrame parameters for testing purposes but for some reason the frame doesn't change. However, the image on the ivMain does change. Can anyone explain how I can change the frame for my UI objects? Also, why does it allow me to change the image but the frame seems un-editable, doesn't this mean it must be initialised?
Any help is much appreciated,
Thanks.
Are you sure autolayout is disabled on the .xib ? It is my understanding that you cannot edit frames when autolayout is on.
If that's the case, you can either disable autolayout for that particular .xib, or you can work on constraints instead of the frame property
The use of autosizing may cause some unexpected behaviors also the autoresize subviews.
You can try something like this:
[View setAutoresizingMask:UIViewAutoresizingNone];
And:
[View setAutoresizesSubviews:NO];
Where View is the UIView that you want to apply that behavior.

Making a UIButton change a UIImageView image

I'm very new to programming and objective c so please go easy on me.
I would like a UIButton (which I'm using as an IBAction) to change an the image in a UIImageView when pressed. I put the UIImageView into a UIScrollView in the storyboard but I hope I can still programmatically change the image.
I have searched absolutely everywhere for an answer for hours but nothing has worked for me either because it wasn't the right solution or I didn't do it properly.
I have tried this code and some more but they always return a "Thread 1: signal SIGABRT" when I press the button:
imageView.image = [UIImage imageNamed: #"Ruler pic inch.png"];
Here is my code so far:
ViewController.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface ViewController : UIViewController <ADBannerViewDelegate, UIScrollViewDelegate> {
ADBannerView *adView;
BOOL bannerIsVisible;
IBOutlet UIScrollView *scrollView;
IBOutlet UIImageView *imageView;
IBOutlet UIButton *proVersion;
IBOutlet UIButton *howToUse;
}
- (IBAction)switchUnit:(id)sender;
#property (nonatomic, assign) BOOL bannerIsVisible;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize bannerIsVisible;
#synthesize scrollView;
#synthesize imageView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Hide status bar:
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// iAd:
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, -50.0f);
[self.view addSubview:adView];
adView.delegate=self;
self.bannerIsVisible=NO;
// Setting scrollview content size to size of image
scrollView.contentSize = CGSizeMake(320,2246);
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, 50.0f);
[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, -50.0f);
[UIView commitAnimations];
self.bannerIsVisible = NO;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)switchUnit:(id)sender {
// CODE THAT MAKES IMAGE CHANGE GOES HERE (I thought...)
}
#end
Help would be greatly appreciated. I'm fed up with it. I've probably done a silly mistake somewhere but I can't work it out. Thanks in advance.
General pointers:
Try to use #property for all your instance variables (it'll server you well in future)
Don't use #synthesize (the compiler does a better job of it for you)
If you have a property bob, access it by using self.bob
Turn on ARC (looks like you don't have it on currently)
Best not to have any spaces in image names
To be clear on properties, when you have a property you don't need to create instance variables in between {} of the #interface. These should be removed in order to prevent multiple definitions. The auto-synthesising done by the compiler will define the instance variables for you but you should always use the property to access the value (as in item 3 above).
That said, your - (IBAction)switchUnit:(id)sender looks fine, if a little empty. The code should be something like:
self.imageView.image = [UIImage imageNamed:#"Ruler_pic_inch.png"];
For it to work, the image Ruler_pic_inch.png would need to be in your bundle (which means it's in Xcode project and set to copy during the build.
Again, that said, your problem would seem to relate to you being confused somewhere. SIGABRT type issues would usually be picked up by the compiler and notified to you as a warning saying something like "some object may not respond to some selector". If you see any of those, look at them and work out why you're trying to ask the object a question it doesn't understand.
If you don't see any compiler warnings, then you've told the compiler that an object is going to be of one type and then actually set it to something else. In this case I'd look at your IBOutlets in Storyboard and check that they are actually set to the correct destination view (disconnect and reconnect them all to be sure).

Objective-C UILabel will not change color

The code I have is pretty much this (no custom views, just a single view application):
viewController.h:
#interface ViewController : UIViewController <UITextFieldDelegate>
...
...
#property (strong, nonatomic) IBOutlet UILabel *statusMessage;
#end
ViewController.m:
#synthesize statusMessage = _statusMessage;
- (void) viewDidLoad {
...
...
...
self.statusMessage = [[UIlabel alloc] initWithFrame:CGRectMake(...)];
[self.statusMessage setTextColor:[UIColor redColor]];
}
I am able to change the background color and I have created other labels that display properly with which ever colors I choose for them, but no matter what I do this label always displays as a grayish color. Any suggestions are appreciated!
Is your label disabled? Check the value of enabled; if it's NO, that could override your color. Ditto for highlighted; if it's YES, it will use the highlighted color.

Setting the initial value of a UILABEL

I'm trying to create a simple Quiz app (I'm a beginner), when I launch the app I want a UILabel to show the first question (of an array of questions). I'm having some trouble with setting the initial value.
I've done a couple of attempts, whiteout success. I my QuizAppDelegate.h file I declare my UILabel like this:
IBOutlet UILabel * questionField;
In my main .m file I have tried the following:
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
questionField = [[UILabel alloc] init];
[questionField setText:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
Another thing I have tried is the following in QuizAppDelegate:
#property (nonatomic, retain) IBOutlet UILabel *questionField;
- (void)changeTitle:(NSString *)toName;
And in the .m file:
#synthesize questionField;
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
[self changeTitle:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
-(void)changeTitle:(NSString *)toName {
[questionField setText:toName];
}
Any tips on how to solve this would be great!
// Anders
Hopefully you're not actually putting code into main.m. On iOS, you rarely modify that file.
Since you're doing everything in the AppDelegate, let's keep it there (as opposed to creating a new UIViewController). Let's start with the basics.
Adding the Label as an instance variable
You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;
Then, declare the corresponding property, and make sure to synthesize it in the .m file.
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#synthesize questionField // in the .m file
Adding the UILabel in Interface Builder
Open up MainWindow.xib. Drag a UILabel from the Library to the Window that represents your app's window. Then Control-Drag from the AppDelegate object (the third icon on the left in Xcode 4; it'll be labelled in the Document window in IB 3). You'll see a little black window come up—select the option called questionField to make the connection.
See this link for screenshots and how to make connections in IB. The same applies in Xcode 4.
Changing the text
You don't need a separate method to change the text—just modify the label's text property.
Pick a method that'll be called when the app launches (applicationDidFinishLaunching:WithOptions: is a good place to do it in), and put the following code:
questionField.text = #"Hello";
And that's it!
Code
QuizAppDelegate.h
#import <UIKit/UIKit.h>
#interface QuizAppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UILabel *questionField;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#end
QuizAppDelegate.m
#import "QuizAppDelegate.h"
#implementation QuizAppDelegate
#synthesize window=_window;
#synthesize questionField;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
[self.window addSubview:self.questionField];
[self.window makeKeyAndVisible];
self.questionField.text = #"Hello";
return YES;
}
- (void)dealloc
{
[_window release];
[questionField release];
[super dealloc];
}
#end
If you're creating the label programmatically, then you have to add the label to the view:
[self.view addSubview:questionField];
This assumes that you have a ViewController. If not, and you're doing this directly in the AppDelegate (a very bad idea, by the way), then do
[self.window addSubview:questionField];
If you're creating it in the IB, make sure you set up the connections.
You should not both add the UILabel in the IB and instantiate it programmatically. Only call alloc if you are creating it programmatically. Otherwise, if using the IB, skip that part. You created it already with the xib.
I suspect that you have either not created your Interface Builder layout properly - either you have missed the control out all together or more likely you have not connected that control to the questionField outlet in yout header file.
You need to drag a UILabel view into the main view and then connect it to the correct line in your header file.
You shouldn't be using your main.m like that at all. In fact, you should almost certainly never do anything with it. Try creating a UIViewController subclass and practicing your quiz with that. (Add the UILabel to the IB file and then connect the outlet.) Perhaps use the View-Based Application template while you are practicing.
This is a good answer:
"You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;"
I was trying to change the value of mylabel.text and the screen didn't update the label with this.value. I included the {IBOutlet UILabel * mylabel} and it works like a charm!
So this answer is valid to change the text of a label programmatically!
Thanks

Why isn't my UIButton responding to touches?

I'm sure I'm overlooking the obvious as I've got countless working buttons...but...for whatever reason this one is not cooperating...
I've added a UIButton (Rounded Rect) to a UIView subclass (DialogView) which is a subview of my view controller's view. This subview is created almost entirely in IB. I've wired up the button to (IBAction)okButtonPressed:(id)sender in IB to Touch Up Inside and created a corresponding method in DialogView. However when I "touch" this button it doesn't trigger the method. userInteractionEnabled is true for the VC's view, DialogView and the UIButton.
Thinking maybe initWithCoder had to do some frame manipulation or something I added the following which successfully logs to console.
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
In further exploration I wired up an IBOutlet to the button and then if I try to change the titleLabel from the view controller I notice that it get's severely truncated. Default text of say "Press Me!" set in IB displays fine when view is first drawn. But if I change the text...
self.DialogView.okButton.titleLabel.text = #"Not Working";
...it gets truncated to "N..."
Dunno if this is related. Probably...
Anyone see what I've screwed up here?
Edit (adding code related to showing UIButton):
From the View Controller:
self.DialogView = [[[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:self options:nil] objectAtIndex:0];;
self.DialogView.myVC = self;
self.DialogView.backgroundColor = [UIColor clearColor];
self.DialogView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
self.DialogView.nameLabel.text = loan.fullName;
self.DialogView.noteLabel.text = loan.summaryOfLoan;
self.DialogView.amountLabel.text = [currencyFormatter stringFromNumber:loan.originalAmount];
self.DialogView.alpha = 0.0;
[self.view addSubview:DialogView];
The UILabels all displaying as expected. As is the problem UIButton. I can see it I just can't interact with it!?!
DialogView's interface:
#class MyViewController;
#interface DialogView : UIView {
IBOutlet UILabel *nameLabel, *noteLabel, *amountLabel;
IBOutlet UIImageView *arrowView;
IBOutlet UIButton *okButton;
MyViewController *myVC;
}
#property (nonatomic, retain) UILabel *nameLabel, *noteLabel, *amountLabel;
#property (nonatomic, retain) UIImageView *arrowView;
#property (nonatomic, assign) MyViewController *myVC;
#property (nonatomic, retain) UIButton *okButton;
- (IBAction)okButtonPressed:(id)sender;
#end
And DialogView's implementation:
#import "DialogView.h"
#import "MyViewController.h"
#implementation DialogView
#synthesize nameLabel, noteLabel, amountLabel, arrowView, okButton;
#synthesize myVC;
- (void)dealloc {
[nameLabel release];
[noteLabel release];
[amountLabel release];
[arrowView release];
[okButton release];
[super dealloc];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
- (IBAction)okButtonPressed:(id)sender {
NSLog(#"pressed DialogView OK button");
[self.myVC.navigationController popViewControllerAnimated:YES];
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
#end
I thought that we should use -setTitle:forState: in order to set button's title ?
An other thought, did you check that the button's frame is not CGRectZero ? And by the way, all the frames for the view in the hierarchy ? And check that one superview in the hierarchy is not user interaction disabled ?
And, I think imageView does not respond to touches, do you have one in your code ?
I was just having more or less the same problem and I found that my containing view did not have "User Interaction Enabled".
Hope this helps.
Do you maybe have two buttons on top of one another? Change the IB project window to the detail view and see if your view has more buttons than you are expecting. Maybe you've wired up a button that's not actually getting the press you're expecting.