Passing object from controller to a view - iphone

I'm following iPhone dev courses from Stanford Open-University, and I've been blocked for 2 days on assignment3, maybe someone can help me here?
The tasks are:
Create a custom UIView subclass that will display your PolygonShape object
Give your view class access to the PolygonShape object so that it can retrieve the details of the polygon as needed
The problem is: how do I give my view class access to the polygon object defined in my controller?
Here is my implementations if it can help:
CustomView.h:
#import "PolygonShape.h"
#interface CustomView : UIView {
IBOutlet PolygonShape *polygon;
}
- (NSArray *)pointsForPolygonInRect:(CGRect)rect numberOfSides:(int)numberOfSides;
#end
Controller.h:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "PolygonShape.h"
#import "PolygonView.h"
#interface Controller : NSObject {
IBOutlet UIButton *decreaseButton;
IBOutlet UIButton *increaseButton;
IBOutlet UILabel *numberOfSidesLabel;
IBOutlet PolygonShape *polygon;
IBOutlet PolygonView *polygonView;
}
- (IBAction)decrease;
- (IBAction)increase;
- (void)awakeFromNib;
- (void)updateInterface;
#end

And after you figure it out, it might not hurt to touch up on some objective-c basics:
http://www.cocoacast.com/?q=node/103

Found my own answer, I missed a setPolygon method in my CustomView to link both... stupid...
in CustomView.h:
#import "PolygonShape.h"
#interface CustomView : UIView {
IBOutlet PolygonShape *polygon;
}
#property (readwrite, assign) PolygonShape *polygon;
- (NSArray *)pointsForPolygonInRect:(CGRect)rect numberOfSides:(int)numberOfSides;
#end
in CustomView.m:
#implementation CustomView
#synthesize polygon;
...
#end
in Controller.m:
- (void)awakeFromNib {
// configure your polygon here
polygon = [[PolygonShape alloc] initWithNumberOfSides:numberOfSidesLabel.text.integerValue minimumNumberOfSides:3 maximumNumberOfSides:12];
[polygonView setPolygon:polygon];
NSLog (#"My polygon: %#", [polygon description]);
}

I just finished assignement 3 last night. I solved this connection all in Interface Builder. First I created an outlet on the "PolygonView" UIView subclass for the PolygonShape and then connected it to the instance of the Polygon model. From what I have read in the Google Group and on various other sites, I do not think there is one right way to connect this UIView to the model and the controller. But it worked I think there is nothing wrong with the View knowing about the model.

So why aren't you declaring them as properties of the class?

Related

Defining delegate function in separate file (instead of in ViewController)

I have a ViewController, and a UIView.
The UIView has a delegate, and the delegate function is set in the ViewController.
All I want to do, is have the delegate function defined in a separate file. So the UIView.m #imports the separate file, instead of all the ViewControllers which use the UIView.
I believe this is a standard procedure, but keep falling over myself trying to get it to work. :| Would really appreciate some help. Thanks.
myViewController.h
#import <UIKit/UIKit.h>
#import "myUIView.h"
#protocol ModalViewDelegate
-(void)didReceiveMessage:(NSString *)message;
#end
#interface myViewController : UIViewController <ModalViewDelegate>
#property (nonatomic, strong) myUIView *myUIViewItem;
#end
myViewController.m
#import "myViewController.h"
#import "myUIView.h"
#interface myViewController ()
#end
#implementation myViewController
#synthesize myUIViewItem;
- (void)didReceiveMessage:(NSString *)message { //<<< THIS IS WHAT
NSLog(#"Message from button: %#", message); //<<< NEEDS MOVING
}
- (void)viewDidLoad
{
…
myUIViewItem.delegate = self;
…
myUIView.h
#import <UIKit/UIKit.h>
#protocol ModalViewDelegate;
#interface myUIView : UIView {
id<ModalViewDelegate> delegate;
}
#property (nonatomic, strong) id<ModalViewDelegate> delegate;
myUIView.m
#import "myUIView.h"
#import "myViewController.h"
#implementation myUIView
#synthesize delegate;
...
[delegate didReceiveMessage:#"Data from UIView!"];
well, there is one method actually,
Just take one .h file and lets say connectionDelegate.h and declare your protocol init
In connectionDelegate.h:
#import <UIKit/UIKit.h>
#protocol ConnectionDelegate
-(void)getResult:(NSString*)_result;
#end
Then in your view controller:
#import "ConnectionDelegate.h"
#interface myViewController : UIViewController <ConnectionDelegate>
{
id delegate;
}
then in .m file, by just call the method
[delegate getResult:_result];
Edit regarding the warnings:
You need to declare the method in view controller, you need to do like this.
[self getResult:urlDataString];
-(void)getResult:(NSString*)_result{
[delegate getResult:_result];
}
Based on your comment:
I want to have the function 'didReceiveMessage' defined in a SEPARATE
file. So that I don't have to repeat it in every ViewController that
uses the UIView and delegate. e.g. ModalViewDelegate_Action.h and
ModalViewDelegate_Action.m
The way I was given was to use a subclass, and that's been working great for me. In my iOS projects I have a class called BaseViewController, which is a subclass of UIViewController. I put lots of code in it related to HUD management, NSOperations management, etc. Then virtually all my view controllers are subclasses of it.

How to add a popup UIView (of a customized class) on a UIViewController

I'm new on this, and I would like to get some advice because I don't know what I'm doing wrong.
I want to make an app in xcode, with a UIView with some items, and when you do something, another UIView (smaller than the first) pops up above the first UIView. The popup UIView would be a customized class.
I have started with the UIViewController template and the initial UIView, and I have linked all the items in the .storyboard, and it works. But when I create my own UIView class (from objective-C class), put the second UIView over the first in the storyboard and link it to my class, something goes wrong.
The UIView appears, but when I try to set it to hidden, it doesn't answer. It's like it's not receiving the messages, so I think I don't link it well programmatically and just appears because of the storyboard.
I don't know if I have to create another UIViewController instead of the UIView, or if this is the correct path.
Can anybody explain me a little, or just write a little code snippet with the instantiation of the second view and adding it?
Lots of thanks!!
(I paste some code, of the declaration in .h and instantiation in .m)
#import <UIKit/UIKit.h>
#import "EditView.h"
#interface ReleaseViewController : UIViewController <UIWebViewDelegate, UISearchBarDelegate> {
IBOutlet UIWebView *web;
IBOutlet UISearchBar *search;
IBOutlet EditView *evHack;
}
#property (nonatomic, retain) IBOutlet UIWebView *web;
#property (nonatomic, retain) IBOutlet UISearchBar *search;
#property (nonatomic, retain) IBOutlet EditView *evHack;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
search.delegate = self;
web.delegate = self;
evHack = [evHack initWithFrame:CGRectMake(0, 44, 320, 377)];
[evHack setHidden:YES];
}
EditView Class (I still have nothing):
#import <UIKit/UIKit.h>
#interface EditView : UIView
#end
#import "EditView.h"
#implementation EditView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"View created");
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
initWithFrame only works when you alloc/init an app. If its already initialized, in this case by the storyboard, just set its frame:
evHack.frame = CGRectMake(0,44, 320, 377);
I don't know what it looks like in IB, But setting its frame in code may be redundant if you set it in IB too. To check whether evHack is hooked up right, NSLog evHack in viewDidLoad. If you get nil back, it's not hooked up right.

resize the Hosting View through addSubview in Core-Plot in XCode 4

I'm running into a wall trying to resize the Hosting View. The problem is I either get a full-screen plot or a blank screen. I'm hoping to get some leads to fix this problem:
I'm using Xcode 4 | IOS 4.3 | Recently downloaded core plot using hg:
I have two xib files (MainWindow & my ViewController)
My ViewController.xib file contains two objects: a View and Hosting View both at the same level:
+-View
+-Graph Hosting View
I get no erros in my code, but all I get a blank screen. I've searched for 3 days how to get around this problem, but haven't found something that works.
My xAppDelegate.h
#import <UIKit/UIKit.h>
#class CorePlotTestViewController;
#interface CorePlotTestAppDelegate : NSObject <UIApplicationDelegate> {
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet CorePlotTestViewController *viewController;
#end
My ViewController.h
#import <UIKit/UIKit.h>
#import "CorePlot-CocoaTouch.h"
#interface CorePlotTestViewController : UIViewController <CPTPlotDataSource>
{
CPTXYGraph *graph;
NSMutableArray *dataForPlot;
CPTGraphHostingView *graphView;
}
#property(readwrite, retain, nonatomic) NSMutableArray *dataForPlot;
#property(nonatomic, retain)IBOutlet CPTGraphHostingView* graphView;
My ViewController.m
#import "CorePlotTestViewController.h"
#interface CorePlotTestViewController(private)
- (void) configureTableHeader;
#end
#implementation CorePlotTestViewController
#synthesize dataForPlot;
#synthesize graphView;
-(void)dealloc
{
[dataForPlot release];
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self configureTableHeader];
}
- (void) configureTableHeader
{
// here I implement the contents of the Hosting View
graph = [[CPTXYGraph alloc] initWithFrame: CGRectZero];
CPTGraphHostingView *hostingView = [(CPTGraphHostingView *)[CPTGraphHostingView alloc]
initWithFrame:CGRectZero];
[self.view addSubview:hostingView];
...
// etc
...
}
In your code, you initialize the hosting view using a CGRectZero frame, which is basically a frame with an origin at (0,0) and both a width and a height of 0 px. It is the reason why you don't see the graph at all when you run your project.
If you gave a custom size and location to the hosting view in Interface Builder, it is overridden by this code.
By the way, I'm not sure why you want to add the hosting view to the controller's view in the code. You just need to layout both components using Interface Builder, giving the hosting view the size and the location you want it to have.
Last thing : why do you add a 'T' to Core-Plot class names ? CPTPlotDataSource should simply be CPPlotDataSource; CPTXYGraph: CPXYGraph; etc.

Warning when communicating context from controller to NSView with custom methods

Coding against UIKit for iPhone.
Setup, with relevant detail:
SomeView.h:
#import <UIKit/UIKit.h>
#interface SomeView : UIView {
SomeObject *myObject;
}
#property (assign) SomeObject *myObject;
-(void) doSomething;
#end
SomeView.m:
#import "SomeView.h"
#implementation SomeView
#synthesize myObject;
- (void)doSomething {
NSLog(#"doing something");
}
- (void) drawRect:(CGRect)rect {
// drawing is based on myObject
}
#end
Controller.h:
#import <UIKit/UIKit.h>
#import "SomeView.h"
#interface Controller : NSObject {
IBOutlet UIView *someView;
}
#end
Controller.m:
#import "Controller.h"
#implementation Controller
-(void)awakeFromNib {
[someView doSomething];
[someView setSomeObject:someObject];
}
#end
I am instantiating the controller object in Interface Builder, and SomeView is the class of one of my custom UIViews in my app's main window.
Now, the questions:
1) when I run the above, I get warnings for both lines: "Warning: 'UIView may not respond to 'doSomething'" and similar warning for setSomeObject. Why? (The code actually seems to work, but I don't like seeing the warnings.)
2) is this the right way of doing things? What I am really after, is making SomeView aware of someObject, so that when drawRect for SomeView is called, it can change its behavior based on current state of someObject. I don't need to have the object directly in SomeView; I could have it in the controller, but the view still needs some information from it that may change at runtime.
You declared someView as an instance of UIView, but doSomething is a method of SomeView. So it is correct — the class you told the compiler that the variable points to does not respond to that message. If you don't want warnings, you'll have to make it a SomeView * instead of a UIView *.
Otherwise, your general architecture looks OK to me.

iPhone MVC. Need some help with understanding how to correctly pass data from Controller to View

A little background:
I'm a C# developer starting to mess with the iPhone (have an idea for a simple 2D game). The only MVC programming I've done was for the web (ASP.NET MVC) so although I do have an understanding in MVC, I can't wrap my mind around one thing. Here's an example to illustrate.
Say I have a simple app where all I want to do is display a big circle on the screen. I created a "View Based Application" and it gave me the basic classes to start with:
MVCConfusionAppDelegate
MVCConfusionViewController
Now since I'll be doing some custom drawing (I know I can add a subview and show the circle that way, but this is just a sample of a larger piece) I've added a class called MyCustomView and in Interface Builder set the View of the MVCConfusionViewController to be a MyCustomView.
Now here's the problem. I want to be able to set in code the size of how big the ball on the custom view should be. So I have a property on the MyCusomView like this:
#import <Foundation/Foundation.h>
#interface MyCustomView : UIView {
NSNumber *ballSize;
}
#property(nonatomic,retain)IBOutlet NSNumber *ballSize;
#end
#import "MyCustomView.h"
#implementation MyCustomView
#synthesize ballSize;
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor redColor]set];
float floatValue = [self.ballSize floatValue];
CGRect ballRect = CGRectMake(50.0f, 50.0f,floatValue , floatValue);
CGContextFillEllipseInRect(context, ballRect);
}
#end
Then, here's my MVCConfusionViewController:
#import <UIKit/UIKit.h>
#import "MyCustomView.h"
#interface MVCConfusionViewController : UIViewController {
NSNumber *ballSize;
}
#property(nonatomic,retain)IBOutlet NSNumber *ballSize;
#end
#import "MVCConfusionViewController.h"
#import "MyCustomView.h"
#implementation MVCConfusionViewController
#synthesize ballSize;
- (void)viewDidLoad {
[super viewDidLoad];
MyCustomView *myView = (MyCustomView *)self.view;
myView.ballSize = self.ballSize;
}
And finally, the MVCConfusionAppDelegate:
#import <UIKit/UIKit.h>
#class MVCConfusionViewController;
#interface MVCConfusionAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MVCConfusionViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MVCConfusionViewController *viewController;
#end
#import "MVCConfusionAppDelegate.h"
#import "MVCConfusionViewController.h"
#import "MyCustomView.h"
#implementation MVCConfusionAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
viewController.ballSize = [NSNumber numberWithInt:200];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
As you can see, there's an ugly cast in my viewDidLoad method. I was hoping I'd be able to make the connection of the ballSize properties in IB, but it won't let me.
So my question simply is, what's the correct way of passing this data from my view controller to my view without doing that cast? I know I'm missing something fundamental, but I just don't see it. Any help would be greatly appreciated!
EDIT: Here's the source code. http://bit.ly/uKyp9 Maybe someone can have a look and see if I'm doing anything wrong.
Are you trying to connect one IBOutlet (in the controller) to another IBOutlet (in the view)? Unfortunately, I don't think it's that easy :-)
You're also storing the data (ballSize) in the controller and the view.
I'd make MVCConfusionViewController a data source for MyCustomView, and then let MyCustomView ask its datasource for the ballSize, inside the -drawRect: method.
#class MyCustomView;
#protocol MyCustomViewDataSource
- (NSNumber *)ballSizeForMyCustomView:(MyCustomView *)view;
#end
#interface MyCustomView {
id<MyCustomViewDataSource> dataSource;
}
#property (nonatomic, assign) IBOutlet id<MyCustomViewDataSource> dataSource;
#end
#implementation MyCustomView
- (void)drawRect:(CGRect) rect {
if (self.dataSource == nil) {
// no data source, so we don't know what to draw
return;
}
float floatValue = [[self.dataSource ballSizeForMyCustomView:self] floatValue];
// ...
}
#end
In Interface Builder, hook MVCConfusionViewController up to the view's dataSource property. Then implement the protocol:
#interface MVCConfusionViewController : UIViewController <MyCustomViewDataSource> {
[...]
}
[...]
#end
#implementation MVCConfusionViewController
- (NSNumber *)ballSizeForMyCustomView:(MyCustomView *)view {
return self.ballSize;
}
#end
This way your view controller could also be the data source for multiple MyCustomViews, because the protocol method takes a MyCustomView as an argument.
If you need more than one ball, have a look at the UITableViewDataSource and implement similar methods, something like:
-(NSInteger)numberOfBallsInMyCustomView:(MyCustomView *)view;
-(NSNumber *)myCustomView:(MyCustomView *) ballSizeAtIndex:(NSInteger)index;
Your view should already be set in IB, so you can use it as is. If you want to use MyCustomView, you can do it like this:
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, 320, 480);
MyCustomView *myView = [[MyCustomView alloc] initWithFrame:frame];
myView.backgroundColor = [UIColor greenColor];
self.view = myView;
[myView release];
CGRect rectangle = CGRectMake(20, 20, 20, 20);
[self.view drawRect:rectangle];
}
I couldn't make your drawing code work, I don't know much about that.
One way to avoid the cast would be to add a separate outlet property for the custom view on the controller, and refer to that instead.
In Interface Builder, make an instance of MyCustomView and drag it into the existing view to make it a subview, then attach it to its own outlet on the controller.