Programmatically Displaying a UILabel from another class - iphone

I'm re-factoring my code and would like to move a whole bunch of UILabels into another class to tidy things up a bit. I'm missing one puzzle piece to be able to do so though (or maybe I'm just tired lol) Anyway here's the simplified code showing my issue. Thanks in advance to anyone who helps :)
#interface MyClass : UIView {
UILabel *classLabel;
}
#property (assign) UILabel *classLabel;
#end
#implementation MyClass
#synthesize classLabel;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
}
return self;
}
- (void)dealloc {[super dealloc];}
#end
#interface LabelTestViewController : UIViewController {
MyClass *myClassInstance;
UILabel *myLabel;
}
#property (assign) UILabel *myLabel;
#end
#implementation LabelTestViewController
#synthesize myLabel;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// this shows a label on the screen as expected
myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 50, 20)];
myLabel.text = #"Hello";
[self.view addSubview:myLabel];
[myLabel release];
// this doesn't show anything on the scree
myClassInstance = [MyClass new];
[myClassInstance drawRect:CGRectMake(10, 50, 50, 20)]; // I suspect I need to call a different method, just don't know which one. initWithFrame is what I used at the time of creation of the label in the previous working scenario. is there an equivalent?
myClassInstance.classLabel.text = #"Goodbye";
[self.view addSubview:myClassInstance.classLabel];
}
- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];}
- (void)viewDidUnload {}
- (void)dealloc {[super dealloc];}
#end

A couple of things:
1) You should never call drawRect directly. Instead, call setNeedsDisplay or setNeedsDisplayInRect. See the Cocoa Drawing Guide or the UIView Class Reference for more info.
2) But that may not be source of your problem. From your code, it is difficult to tell what ends up in classLabel after you are done setting it up, but I expect it's not what you need. In particular, it needs a frame. I would suggest setting a CGRect variable to myClassLabel.frame and seeing what you end up with.

Related

How to add a UIImage with UIGesture to most views in an application.

I want to make an Advertising banner in my app. A bit like iAd's.
I was going to make it by having a UIImage on the view then assigning the banner image. I would then add a touch gesture so the user could click it and go to another view in my app. I Know That I can do this on one view quite easily but I want this to be on most views in the app. Whats the best way for adding the banner to more than one view with out writing the same code more that once?
The below design shows the sort of banner im after.
Thanks
#import <UIKit/UIKit.h>
#class custom;
#protocol adDelegate
- (void)viewAd:(NSString *)adRate;
#end
#interface custom : UIView
#property (strong, nonatomic) UIImage *viewImage;
#property (assign) id <adDelegate> delegate;
#end
// Main class
#import "custom.h"
#implementation custom
#synthesize viewImage;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = viewImage;
[self addSubview:imageView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.delegate viewAd:#"view"];
}
You can Create a UIView Class and call it BannerView for instance.
// in the bannerView.h
#import <UIKit/UIKit.h>
#interface BannerView : UIView{
UIImageView* bannerImage;
}
#property(nonatomic,retain) UIImageView* bannerImage;
#end
//in the bannerView.m
#import "BannerView.h"
#implementation BannerView
#synthesize bannerImage;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
bannerImage=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"banner-image.png"]];
bannerImage.frame=CGRectMake(0, 0, 320, 100);
[self addSubview:bannerImage];
// add a uibutton on top of the uiimageview and assign an action for it
// better than creating an action recogniser
UIButton* actionButton=[UIButton buttonWithType:UIButtonTypeCustom];
actionButton.frame=CGRectMake(0, 0, 320, 100);
[actionButton addTarget:self action:#selector(yourAction) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:actionButton];
}
-(void) yourAction{
// do what ever here like going to an other uiviewController as you mentionned
}
#end
Now you can call this view from any View Controller this way
BannerView* banner=[[BannerView alloc] initWithFrame:CGRectMake(0, 300, 320, 100)];
[self.view addSubview:banner];
Try creating a parent class from UIView where you do all the display and handling of the banner using your UIImageView and gesture recognizers. Then whichever views need this functionality, derive them from this parent class, and override default handling in method so that you can customize the behavior in your child class.
A few suggestions:
First, why not just use a UIButton instead of a UIImage with a Gesture? All you're really doing is replicating button functionality after all...
Second, I'd probably tackle the overall problem by creating a class that includes the UIButton, like so:
#interface YourSuperViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIButton *adButton;
- (IBAction)adTouched:(id)sender;
#end
In the viewDidLoad for this class, create the button, and add it to the view, and add your ad-specific logic to the adTouched action.
Then create the rest of the views in your app as an instance of YourSuperViewController. Like so:
#interface SomeOtherViewController : YourSuperViewController
Now the SomeOtherViewController will auto-magically have the ad button and respond to a touch on it properly. Done!
What everyone else has said is the best way. If you need custom functionality, subclassing is probably the way to go.
I just wanted to add one pedantic thing. Its important to remember that a UIImage is not a view. There has never been a UIImage on the screen, ever. A UIImage is a model object. It is just a collection of data. A UIImageView is a view object and as such, a UIImageView can display itself on the screen.
This might seem overly pedantic and nitpicky, but its important to have these things sorted out in our heads in order to effectively use MVC (model, view, controller)

UIPopOverScreen troubles

Recently I have taken interest in making a popover screen. In my Navigation bar I made this button and when I click on it it should make a popover screen appear.
So I started searching for a usefull tutorial and yet I found mostly tutorials made with interface builder, which is not what I want. So I started experimenting on my own.
This is my result:
First I set the necessary properties in the MainVC.h
Also mind the
#import <UIKit/UIKit.h>
#import "ThePopOverVC.h"
#interface PopoverPrototypeViewController : UIViewController <UIPopoverControllerDelegate>
{
ThePopOverVC *popover;
UIPopoverController *popoverController;
UIButton *popoverButton;
}
#property (nonatomic,retain) ThePopOverVC *popover;
#property (nonatomic,retain) UIPopoverController *popoverController;
#property (nonatomic,retain) UIButton *popoverButton;
- (void)popoverActivation;
#end
Then I set up my view in the MainVC.m
It prepares the view for the popoverscreen when pressing a button.
#import "PopoverPrototypeViewController.h"
#implementation PopoverPrototypeViewController
#synthesize popover;
#synthesize popoverController;
#synthesize popoverButton;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor blueColor];
popover = [[ThePopOverVC alloc] init];
popoverController = [[UIPopoverController alloc] initWithContentViewController:popover];
popoverController.popoverContentSize = CGSizeMake(300, 300);
popoverController.delegate = self;
self.popoverButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 400, 100)];
self.popoverButton.backgroundColor = [UIColor lightGrayColor];
[self.popoverButton setTitle:#"Click me!" forState:UIControlStateNormal];
[self.popoverButton addTarget:self action:#selector(popoverActivation) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.popoverButton];
}
- (void)popoverActivation
{
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:YES];
} else {
UIBarButtonItem *settingsBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.popoverButton];
[self.popoverController presentPopoverFromBarButtonItem:settingsBarButton permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
}
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return YES;
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
NSLog(#"something");
}
Ok, the MainVC should be setup, now it's time to show the popoverContentViewController.
For my experiment I just want it to be an empty view with just a different backgroundColor.
So the popoverVC.h is empty:
#import <UIKit/UIKit.h>
#interface ThePopOverVC : UIViewController
#end
My popoverVC.m is equally empty except for the backgroundColor change:
#import "ThePopOverVC.h"
#implementation ThePopOverVC
- (void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor yellowColor];
}
You would say this shouldn't be too hard. But yet I'm experiencing problems loading the project.
I'm getting the following warning: warning: Unable to restore previously selected frame.
Well here comes the question:
What have I overlooked? What have I done wrong and how can I make this simple prototype work?
//---EDIT---//
OK, I'm a bloody idiot for overlooking this one.
[super loadView];
The super loadView wasn't called and gave me this simple problem.
I edited the code so it works properly now.
Use this as a tutorial on how to make UIPopOverScreens if you will (or whatever).
Ref: http://www.raywenderlich.com/1056/ipad-for-iphone-developers-101-uipopovercontroller-tutorial
[super loadView];
Was missing from ThePopOverVC.m
Pretty silly to overlook.. but yeah, it works now.

Very Simple Custom UIView, drawRect not getting called

I have this super simple example, and I'm not sure why it is not working. drawRect Never gets called. I just want a square to draw and be red. What am I doing wrong?
//Controller.h
#import <Foundation/Foundation.h>
#class CustomView;
#interface Controller : NSObject
#property (nonatomic, retain) CustomView *cv;
#end
//Controller.m
#import "Controller.h"
#import "CustomView.h"
#implementation Controller
#synthesize cv;
- (void) awakeFromNib {
NSLog(#"awakeFromNib called");
CGRect theFrame = CGRectMake(20, 20, 100, 100);
cv = [[CustomView alloc] initWithFrame:theFrame];
UIWindow *theWindow = [[UIApplication sharedApplication] keyWindow];
[theWindow addSubview:cv];
[cv setNeedsDisplay];
}
#end
//CustomView.h
#import <UIKit/UIKit.h>
#interface CustomView : UIView
#end
//CustomView.m
#import "CustomView.h"
#implementation CustomView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSLog(#"initWithFrame called");
}
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"drawRect called");
self.backgroundColor = [UIColor redColor];
}
#end
You aren't drawing anything in your drawRect. You are just setting a property on the view. If you have overridden drawRect, nothing will be drawn - try calling [super drawRect:rect] (after setting your background colour) or simply draw the square yourself using:
[[UIColor redColor] set];
[[UIBezierPath bezierPathWithRect:self.bounds] fill];
EDIT:
I see your drawRect is not even being called. I'm not sure of your nib structure, but try adding cv as a subview to self.view in your controller rather than adding it to the window. Also, note that you are not retaining cv (use self.cv = rather than cv =) but this shouldn't be an issue since your view will retain it.
Rather than doing a forward reference to your CustomView class in your Controller implementation:
#class CustomView;
Trying importing the class header file:
#import "CustomView.h"
Because you require access to the API you have defined when you call:
cv = [[CustomView alloc] initWithFrame:theFrame];
A forward reference tells the compiler that there will be an implementation for the class you are using at compile time and it is best used in header files. In implementation files, I find it best to import the header.

How can I view subview with an activity indicator?

I need to view a subview with an activity indicator.
This is my code but the subview doesn't appear:
#interface ProgressViewController : UIViewController {
IBOutlet UIActivityIndicatorView *myActivityIndicator;
}
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView *myActivityIndicator;
#end
#implementation ProgressViewController
#synthesize myActivityIndicator;
- (void)viewDidLoad {
[myActivityIndicator startAnimating];
[super viewDidLoad];
}
- (void)viewWillDisappear:(BOOL)animated {
[myActivityIndicator stopAnimating];
}
#end
#import "ProgressViewController.h"
#interface MyViewController : UIViewController {
ProgressViewController *progressViewController;
}
#property (nonatomic, retain) ProgressViewController *progressViewController;
#end
#implementation MyViewController
#synthesize progressViewController
- (void)viewDidLoad
{
progressViewController = [[ProgressViewController alloc] initWithNibName:#"ProgressViewController" bundle:nil];
[self.view addSubview:progressViewController.view];
sleep(4);
[progressViewController.view removeFromSuperview];
[super viewDidLoad];
}
#end
There could be several causes, and it's still a bit unclear from the code you sent, which one it is.
First, you shouldn't use sleep(4) in your code - it messes up the application engine iOS runs to support user input, screen refresh, etc.
Your code could easily be changed to:
[self performSelector:#selector(removeMyProgressView:) withObject:progressViewController.view afterDelay:4.0];
and have removeFromSuperview in your removeMyProgressView: function.
Also, this line of code is buggy:
progressViewController = [[ProgressViewController alloc] initWithNibName:#"ProgressViewController" bundle:nil];
It should be
self.progressViewController = [[ProgressViewController alloc] initWithNibName:#"ProgressViewController" bundle:nil];
Otherwise you don't call the setter function (#sythesized property), and the object isn't retained. It could be that it is released, and therefore you don't see it.
If this none of this is right, we'll keep pounding at it :)
Good luck!
Oded.
Everything in your -viewDidLoad method happens in one runloop. This means that you add and remove the activity indicator without giving the system a chance to actually draw it. The 4 seconds of sleep don't help. Those just make the runloop take longer to finish.
call [super viewDidLoad] before anything in - (void)viewDidLoad methods

help understanding interaction between UIVIewController and UIView subclass

here's the current situation:
TestViewController.h:
#import <UIKit/UIKit.h>
#interface TestViewController : UIViewController {
}
#end
TestViewController.m:
#import "TestViewController.h"
#import "draw.h"
#implementation TestViewController
- (void)viewDidLoad {
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
[draw setNeedsDisplay];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#end
draw.h:
#import <UIKit/UIKit.h>
#interface draw : UIView {
UIColor* rectColor;
}
#property (retain) UIColor* rectColor;
#end
draw.m:
#import "draw.h"
#implementation draw
#synthesize rectColor;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
- (void)drawRect:(CGRect)rectangle {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef myColor = [UIColor colorWithHue:0 saturation:1 brightness:0.61 alpha:1].CGColor;
CGContextSetFillColorWithColor(context, myColor);
CGContextAddRect(context, CGRectMake(0, 0, 95.0, 110.0));
CGContextFillPath(context);
}
- (void)dealloc {
[super dealloc];
}
#end
then in the Interface Builder I've created a 90x115 UIView and set it's Class Identity to "draw".
When I run the application (without the two non-compiling lines) it displays a nice red rectangle in the middle of the screen. My goal is it be able to change the color of the rectangle from within my TestViewController. However when I compile the test app I get the following compiler errors:
error: accessing unknown 'setRectColor:' class method
error: object cannot be set - either readonly property or no setter found
warning: 'draw' may not respond to '+setNeedsDisplay'
I know I am missing "a link" between my TestViewController and draw, but can't figure out how to go about implementing it. I have tried various tricks, but nothing worked.
Could someone please explain what needs to be done in order for
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
[draw setNeedsDisplay];
to compile and work ?
(I would use this knowledge to create a different method in TestViewController, I am just testing it inside viewDidLoad)
In the code you posted, you set the rectColor property and call setNeedsDisplay: on draw, but draw is your class. You need to do it to the instance. You shouldn't need to create a new property for this because UIViewController defines the view property. Just use self.view instead of draw in your viewDidLoad method. Also, remove the .CGColor at the end of the line.
Second, your drawRect: method ignores that color and uses its own. Change
CGColorRef myColor = [UIColor colorWithHue:0 saturation:1 brightness:0.61 alpha:1].CGColor;
to
CGColorRef myColor = self.rectColor.CGColor;
The big problem here is that you haven't declared a property for draw; you have to do this and link it up to your Interface Builder object if you want to access it. The solution is to create a property that will hold the draw object, and synthesize its setters and getters using #synthesize. (that last piece is why you're getting the error.)
Once you change the code to what I have below, you'll need to make the proper connection in Interface Builder, or else you'll find that your code changes simply have no effect, even though they're error free.
Capitalize your class names (always!), and then try this:
TestViewController.h:
#import <UIKit/UIKit.h>
#class Draw; //forward class declaration
#interface TestViewController : UIViewController {
IBOutlet Draw *drawCopy;
}
#property (nonatomic, retain) Draw *drawCopy;
#end
Then, for TestViewController.m:
#import "TestViewController.h"
#import "Draw.h"
#implementation TestViewController
#synthesize drawCopy;
-(void) viewDidLoad {
self.drawCopy.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1];
[self.drawCopy setNeedsDisplay];
[super viewDidLoad];
}
- (void)dealloc {
[drawCopy release];
[super dealloc];
}
#end
I think that should do the trick.
you definde UIColor* rectColor; but u write an CGColor too it
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
try
self.drawCopy.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1];