So, I am trying to make a shared method in my Brain class which will check the label on a pressed button (buttons of items are in different classes) and then accordingly add a image to a ShoppingList view minding the order of adding (first image added goes to position 0,0, second to position 80,0 etc.).
I have Breakfast class which represents breakfast ingredients and I wanna add a photo of a Tea to another view called ShoppingList.
I wrote a method in Brain that adds an image to a imageView and returns an imageView which is then locally passed to the button pressed.
It builds but when I press the button Tea, application crashes.
Here is my code:
Brain.h
#interface Brain : NSObject {
#private
UIImage *image;
UIImageView *imageView;
}
#property (retain) UIImageView *imageView;
#property (retain) UIImage *image;
- (id)performOperation:(NSString *)operation;
#end
Brain.m
#implementation Brain
#synthesize imageView;
#synthesize image;
- (UIImageView *)performOperation:(NSString *)operation
{
if ([operation isEqual:#"Tea"]) {
image = [UIImage imageNamed:#"Tea_photo.jpg"];
imageView = [[UIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(0, 0, 80, 80);
return imageView;
//[shoppingList.view addSubview:imageView];
//[imageView release];
}
else return 0;
}
#end
Breakfast.h
#interface Breakfast : UIViewController {
IBOutlet ShoppingList *shoppingList;
Brain *brain;
}
- (IBAction)addItemToShoppingList:(UIButton *)sender;
- (IBAction)goToShoppingList;
- (IBAction)goBack;
#end
Breakfast.m
#implementation Breakfast
- (Brain *)brain
{
if (!brain) brain = [[Brain alloc] init];
return brain;
}
- (IBAction)addItemToShoppingList:(UIButton *)sender
{
NSString *operation = [[sender titleLabel] text];
UIImageView *imageView = [[self brain] performOperation:operation];
[shoppingList.view addSubview:self.brain.imageView];
//UIImageView *imageView = [[UIImageView alloc]initWithImage:image];
//imageView.frame = CGRectMake(0, 0, 80, 80);
//[shoppingList.view addSubview:imageView];
//[imageView release];
}
- (IBAction)goToShoppingList
{
[self presentModalViewController:shoppingList animated:NO];
}
- (IBAction)goBack
{
[self dismissModalViewControllerAnimated:NO];
}
#end
PLEASE HELP, its for my thesis.
IBOutlet ShoppingList *shoppingList;
Why make this IBOutlet? Isn't ShoppingList a class that you created. Not that this solves your crash. For that u need to post the crash log....
And also in you code I can't see any allocation for shoppingList so how can you be able to use it. You need to allocate the object in the class Brain otherwise it doesn't make any sense.
I have not gone through your code. But on the basis of your description I can suggest you to create a protocol
Related
I am programmatically creating a view and a imageview while in viewControllerA for an another viewControllerB before I then goto viewControllerB. I have to do this, as I am using an image from the imagePickerController. However by doing this, touchesBegan does not function in viewControllerB
I have set UserInteractionEnabled to true/yes in both the attributes inspector of the xib and programmatically everywhere!
My xib is just a blank canvas, with one View. I have tried different settings with the connections inspector, but still no luck.
Here is my code.
viewControllerA.m
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
view = [[UIView alloc] initWithFrame:screenRect];
image = [info valueForKey:UIImagePickerControllerEditedImage];
SSViewController *psView = [[SSViewController alloc] initWithNibName:#"SSViewController" bundle:nil];
psView.view = [[UIView alloc] initWithFrame:screenRect];
[psView.view setUserInteractionEnabled:YES];
[picker pushViewController:psView animated:YES];
imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(20, 20, 280, 280);
[imageView setUserInteractionEnabled:YES];
[psView.imageView setUserInteractionEnabled:YES];
[psView.view addSubview:imageView];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:psView action:#selector(endPS:) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"done" forState:UIControlStateNormal];
button.frame = CGRectMake(140.0, 330.0, 80.0, 25.0);
[psView.view addSubview:button];
}
viewControllerB.h (SSViewController.h)
#interface SSViewController : UIViewController
{
IBOutlet UIImageView *imageView;
IBOutlet UIView *view;
}
#property (nonatomic,strong) IBOutlet UIImageView *imageView;
#property (nonatomic,strong) IBOutlet UIImage *image;
#property (nonatomic, strong) IBOutlet UIView *view;
- (IBAction)endPS:(id)sender;
#end
viewControllerB.m (SSViewController.m)
#implementation SSViewController
#synthesize imageView;
#synthesize image;
#synthesize view;
:
:
- (void)viewDidLoad
{
[view setUserInteractionEnabled:YES];
[imageView setUserInteractionEnabled:YES];
:
:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesBegan");
:
You're doing a number of things wrong:
Don't push a view controller on to the UIImagePickerController. If you've presented it modally, which I hope you have, you need to dismiss it (in viewControllerA) and push your new view controller (viewControllerB) onto your own navigation controller. The fact that you're pushing onto this highly customised navigation controller is most likely why touchesBegan is not being called.
Why are you manually creating the view for viewControllerB? If you're loading it from a XIB file, it will have a perfectly good view ready for you. And don't define the view property in viewControllerB.h, it's already defined in UIViewController.h.
Don't forget to call [super viewDidLoad] in viewControllerB.m. This is a classic mistake that will cause you many headaches.
EDIT:
What I would personally have done is added a method on viewControllerB as such:
viewControllerB.h
#interface SSViewController : UIViewController
{
- (void)setImage:(UIImage *)image;
}
#end
viewControllerB.m
#implementation SSViewController
{
- (void)setImage:(UIImage *)image
{
// In case the image view already exists, remove it first.
[_imageView removeFromSuperview];
_imageView = [[UIImageView alloc] initWithImage:image];
_imageView.frame = CGRectMake(20, 20, 280, 280);
[self.view addSubview:_imageView];
}
}
#end
This way, in viewControllerA.m you can just create viewControllerB.m and call this setImage: method to let viewControllerB do all the work.
The Problem
I have a UILabel buried in a few layers of subviews and I can't seem to change it's text. The code to update the label is inMyCardSubclass's updateVisualElements method. The really weird thing is that when I log the description of these labels, the text has been updated, but it is not visible. Here's what it looks like:
Inside my View Controller's viewDidLoad method I initialize a UIScrollView and my card object. I add its cardView as a subview like so:
-(void)viewDidLoad
{
[super viewDidLoad];
MyCardSubclass *myCard = [[MyCardSubclass alloc]initWithInfo:info andPoint:CGPointMake(5, runningYValue)];
[myCard setParentViewController:self];
[scrollView addSubview:[myCard cardView]];
//Resize scrollView contentSize
}
My Card
//header
#property (nonatomic, strong) UIView *contentView;
#property (nonatomic, strong) UIView *cardView;
#property (nonatomic, strong) CardInfo *cardInfo;
#property (nonatomic, strong) UIViewController *parentViewController;
//implementaion
-(UIView *)cardView
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIView *toReturn = [[UIView alloc]initWithFrame:CGRectMake(self.point.x,self.point.y, screenRect.size.width-borderOffset*2, cardViewHeight)];
UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(borderOffset, 0, toReturn.frame.size.width-borderOffset*2, titleHeight)];
[titleLabel setText:self.title];
[toReturn addSubview:titleLabel];
[self.contentView setFrame:CGRectMake(borderOffset, titleHeight, screenRect.size.width-borderOffset, contentViewHeight)];
if(self.contentView)
[toReturn addSubview:self.contentView];
else
NSLog(#"no contentView");
//other view stuff
return toReturn;
}
My Card Subclass
//header
#property(nonatomic, strong) UILabel *totalAmountLabel;
#property(nonatomic, strong) UILabel *availableAmountLabel;
#property(nonatomic, assign) double available;
//implementation
-(UIView *)cardView
{
[self setContentView:[self contentViewFromCardInfo:self.cardInfo]];
return [super cardView];
}
-(UIView *)contentViewFromCardInfo:(CardInfo *)cardInfo
{
UIView *toReturn = [[UIView alloc]init];
NSString *amount = #"0";
self.totalAmountLabel = [[UILabel alloc]initWithFrame:CGRectMake(labelWidth,
runningYValue,
labelWidth,
labelHeight)];
[self.totalAmountLabel setText:amount];
[toReturn addSubview:self.totalAmountLabel];
runningYValue +=self.totalAmountLabel.frame.size.height+spacer;
self.availableAmountLabel = [[UILabel alloc]initWithFrame:CGRectMake(labelWidth,
runningYValue,
labelWidth,
labelHeight)];
[self.availableAmountLabel setText:#"0"];
[toReturn addSubview:self.availableAmountLabel];
return toReturn;
}
-(void)updateVisualElements
{
NSString *amount = #"10"
NSString *availableString = #100"
//The log descriptions for these labels are okay.
[self.totalAmountLabel setText:amount];
NSLog(#"%#",self.totalAmountLabel.debugDescription);
[self.availableAmountLabel setText:availableString];
NSLog(#"%#",self.availableAmountLabel.debugDescription);
NSAssert([NSThread isMainThread], #"Not on main thread");
NSAssert(self.totalAmountLabel, #"No label");
}
What I've tried
Calling [self.totalAmountLabel setNeedsDisplay] after setting the text
Calling the method on the main thread using [self.totalAmountLabel performSelector:#selector(setText:) withObject:amount afterDelay:0.0]
You can try one thing, Use Tag property to get the added Label and change its contends
Example:
UILabel totalAmountLabel = [[UILabel alloc]initWithFrame:CGRectMake(labelWidth,
runningYValue,
labelWidth,
[totalAmountLabel setText:amount];
totalAmountLabel.tag = 100;
[toReturn addSubview:totalAmountLabel];
Then in your updateVisualElements()
UILabel *tempTotalAmountLabel = (UILabel *)[self.view viewWithTag:100];
[tempTotalAmountLabel setText:amount];
Fixed it! The problem wasn't actually in my attempts to set the label or draw its view.
I wasn't using my initializers properly to init my content view only once.
I removed this override in my subclass
-(UIView *)cardView
{
[self setContentView:[self contentViewFromCardInfo:self.cardInfo]];
return [super cardView];
}
I also took the code from the -(UIView *)cardView method in my superclass and put into a -(UIView *)buildCardView method and rewrote the cardView property accessor to what it should have been.
-(UIView *)cardView
{
if(!_cardView)
_cardView = [self buildCardView];
return _cardView;
}
Thanks for the views and comments.
How to achieve below effect??
I want that when my UIImage width is smaller then UILabel width then my image should be displayed only once as below.
I already have done some more work with UILabel and UIImage. Refer to my previous question : How to stretch Image to fill the Label Width set in Background in UILabel?.
But now i want some more fun with UILabel... :D
EDIT :
SBLabel *lbl = [[SBLabel alloc] initWithFrame:CGRectMake(0, 50, 320, 28)];
lbl.text = #"Hello World!!!...";
UIImage *img = [UIImage imageNamed:#"cn3.png"];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 50, 320, 28)];
imgView.image = img;
[lbl setNonRepeatingBackgroundImage:imgView];
[self.view addSubview:lbl];
[imgView release];
if (image.size.width < label.size.width ||image.size.height < label.size.height )
{
//rect here is frame of image
[img drawInRect:rect];
label.backgroudColor = [UIColor clearColor];
}
It seems like the most straightforward solution would be to have your UIImage sit behind your UILabel and your UILabel has a transparent background color.
Edit
Assuming you're using ARC...
SBLabel.h
#import <UIKit/UIKit.h>
#interface SBLabel : UILabel
#property (nonatomic, strong) UIImageView *nonRepeatingBackgroundImage;
#end
SBLabel.m
#import "SBLabel.h"
#implementation SBLabel
#synthesize nonRepeatingBackgroundImage;
- (void)setNonRepeatingBackgroundImage:(UIImageView *)aNonRepeatingBackgroundImage {
nonRepeatingBackgroundImage = aNonRepeatingBackgroundImage;
[self addSubview:aNonRepeatingBackgroundImage];
[self sendSubviewToBack:aNonRepeatingBackgroundImage];
[self setBackgroundColor:[UIColor clearColor]];
}
#end
You would need to call this setter method after you add the UILabel to the parent view. I have not tested but I believe it should work, might require some tweaks but hopefully it explains how to Subclass UILabel to make a custom enhancement such as this.
A subclass of UILabel could look like this (without ARC)
CustomLabel.h
#import <UIKit/UIKit.h>
#interface CustomLabel : UILabel {
UIImage *mImage;
}
#property (retain) UIImage *image;
#end
CustomLabel.m
#import "CustomLabel.h"
#implementation CustomLabel
- (void)dealloc {
self.image = nil;
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
[mImage drawAtPoint:CGPointMake(0, 0)];
[super drawRect:rect];
}
- (UIImage*)image {
return mImage;
}
- (void)setImage:(UIImage *)image {
self.backgroundColor = [UIColor clearColor];
if(mImage)
[mImage release];
mImage = [image retain];
[self setNeedsDisplay];
}
#end
Usage
yourCustomLabel.image = [UIImage imageNamed:#"test.png"];
I have a view that only has a button UIButton. First time clicking the button will draw a square above the button and the second time will replace the square with a circle.
I need some help on writing the controller code. Please guide me to the right path. Thanks
design a shape class. Add your shapes as UIImageViews. with each button click, change the alpha on click.
#interface Shape: UIView {
UIImageView *square;
UIimageView *circle;
BOOL isSquare;
}
#property (nonatomic, retain) UIImageView *square;
#property (nonatomic, retain) UIImageView *circle;
#property BOOL isSquare;
-(void)changeShape;
#end
#implementation Shape
#synthesize square;
#synthesize circle;
#synthesize isSquare;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.frame = //frame size.
//assume a square image exists.
square = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"square.png"]];
square.alpha = 1.0;
[self addSubview: square];
//assume a circle image exists.
circle = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"circle.png"]];
circle.alpha = 0.0;
[self addSubview: circle];
isSquare = YES;
}
return self;
}
-(void)changeShape {
if (isSquare == YES) {
square.alpha = 0.0;
cirle.alpha = 1.0;
isSquare = NO;
}
if (isSquare == NO) {
circle.alpha = 0.0;
square.alpha = 1.0;
isSquare = YES;
}
}
//rls memory as necessary.
#end
////////////////////////////////in your view controller class.
instantiate your class and add it as a subview.
#class Shape;
#interface ShapeViewController : UIViewController {
Shape *shape;
}
#property (nonatomic, retain) Shape *shape;
-(IBAction)clickedButton:(id)sender;
#end
////////
#import "Shape.h"
#implementation ShapeViewController;
//add your shape as a subview in viewDidLoad:
-(IBAction)clickedButton:(id)sender {
[shape changeShape];
}
#end
That should give you a good basic implementation idea. Essentially, you design a class and then change its shape with each click. It should alternate from square/circle/square/circle. Hope that helps.
Without getting into OpenGL (Quartz 2D is OK):
Let's say I have an image which I want to move across a map in some fluid way. For instance, an image of a plane "flying" across the map. I've been able to do this using an MKAnnotation, an NSTimer and fiddling with the rate of lat/lon change and timer rate. However, I assume this isn't ideal, although the results look pretty decent. Can you think of a better way?
Now let's say that I want this image to be animated (think: animated gif). I can't do the usual UIImageView with a series of animationFrames because all I have access to in MKAnnotationView is a UIImage. How would you guys tackle this?
I realize that #2 could be handled with a UIImageView on top of the map containing the animationImages. However, then I would have to manually handle translating the movement of the plane or rocket or whatever as the region of the mapview changed, depending on user movements in the real-world, or user zooming (scrolling is not allowed in my app).
What do you think?
I think I've figured out a solution to #2. I subclassed MKAnnotationView and wrote some code to add a UIImageView (with animation images) as a subview.
//AnimatedAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface AnimatedAnnotation : MKAnnotationView
{
UIImageView* _imageView;
NSString *imageName;
NSString *imageExtension;
int imageCount;
float animationDuration;
}
#property (nonatomic, retain) UIImageView* imageView;
#property (nonatomic, retain) NSString* imageName;
#property (nonatomic, retain) NSString* imageExtension;
#property (nonatomic) int imageCount;
#property (nonatomic) float animationDuration;
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier imageName:(NSString *)name imageExtension:(NSString *)extension imageCount:(int)count animationDuration:(float)duration
;
#end
//AnimatedAnnotation.m
#import "AnimatedAnnotation.h"
#implementation AnimatedAnnotation
#synthesize imageView = _imageView;
#synthesize imageName, imageCount, imageExtension,animationDuration;
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier imageName:(NSString *)name imageExtension:(NSString *)extension imageCount:(int)count animationDuration:(float)duration
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
self.imageCount = count;
self.imageName = name;
self.imageExtension = extension;
self.animationDuration = duration;
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:#"%#0.%#",name,extension]];
self.frame = CGRectMake(0, 0, image.size.width, image.size.height);
self.backgroundColor = [UIColor clearColor];
_imageView = [[UIImageView alloc] initWithFrame:self.frame];
NSMutableArray *images = [[NSMutableArray alloc] init];
for(int i = 0; i < count; i++ ){
[images addObject:[UIImage imageNamed:[NSString stringWithFormat:#"%#%d.%#", name, i, extension]]];
}
_imageView.animationDuration = duration;
_imageView.animationImages = images;
_imageView.animationRepeatCount = 0;
[_imageView startAnimating];
[self addSubview:_imageView];
return self;
}
-(void) dealloc
{
[_imageView release];
[super dealloc];
}
#end