Add and control UIImageView Subview from another class - objective-c

I have two classes: MainViewController and PlayerImageController (NSObject)
How would I be able to add the subview of my UIImageView from PlayerImageController to my MainViewController and dictate actions like
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:[PlayerImageController addPlayerImage]];
}
- (void)somethingHappened
{
[PlayerImageController changePlayerImage];
}
and have my methods in my PlayerImageController class like
+ (UIImageView *) addPlayerImage
{
heroPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"hero-still.png"]];
[heroPlayerImageView setFrame:CGRectMake(151, 200, 17, 23)];
return heroPlayerImageView;
}
+ (void) changePlayerImage
{
//change image
}
Thanks!

I think in your case you should use the Delegate pattern.
Declare:
#protocol PlayerImageUpdater
- createPlayerImage;
- changePlayerImage;
#end
Then add:
#interface PlayerImageController <PlayerImageUpdater>
then add to MainViewController ivar and property like:
#property (...) id<PlayerImageUpdater> playerDelegate;
set this delegate like: mainViewController.playerDelegate = playerImageControllerInstance;
and use in code:
[playerDelegate createPlayerImage];
[playerDelegate changePlayerImage];

On one hand, I would not recommend using class methods but instance methods. This way, you could implement as many instances of your class as you need and keep a reference to your instances to update them.
On the other hand, if the UIImageView is the important attribute of your class, I suggest you implement it as a UIView subclass (if it is not, you can do it as an NSObject subclass as well, and get its UIImageView attribute).
Have a look at the following code:
PlayerImageController.h:
#interface PlayerImageController : UIView{
UIImageView *_heroPlayerImageView;
}
-(void) changePlayerImage;
PlayerImageController.m:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_heroPlayerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"hero-still.png"]];
// x = 0 and y = 0 because its relative to its parent view, the object itself.
[_heroPlayerImageView setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[self addSubview:_heroPlayerImageView];
}
return self;
}
MainViewController.h:
#import "PlayerImageController.h"
#interface MainViewController : UIViewController{
PlayerImageController *_player;
}
MainViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
_player = [[PlayerImageController alloc] initWithFrame:CGRect(151, 200, 17, 23)];
[self.view addSubview:_player];
}
- (void)somethingHappened
{
[_player changePlayerImage];
}
I hope it can help you (I haven't actually tried the code above, it could have some syntax errors).
If you are not using ARC, remember to retain and release your variables! Good luck!

Related

How to access NSViews from other classes

Before I start, I am a beginner, so please don't overwhelm me. (even-though I probably did with you sorry) :/
So basically I have 1 main view (NSView) onto which I am loading other views. So my awakeFromNib method works and it loads my start menu view (main menu to a tic tac toe game). On the menu is a single player and a double player button, and depending on which button I click, I would like the program to load further views.
So I have an AppController class which controls which views are loaded through a setViewController method. The issue is that I have not found a way to connect the buttons from my start menu view to the to the AppController class. So I thought if inside the start menu class I create an object of type AppController and then call on the setViewController method when the single or double player button is pressed, it would change the views accordingly, but it turns out it does nothing. However when I call on the setViewController method inside the AppController class, it does work. So I think the issue has to be somewhere with accessing the view from outside its class, but I might be wrong. Any help would be greatly appreciated, I have spent a lot of time trying to figure this out and I have not had any luck with anything I tried. Here is my AppController class and my start
screen class.
AppController.h:
#interface AppController : NSObject
#property (weak) IBOutlet NSView *mainMenu;
#property (strong) NSViewController *mainViewController;
-(void)setViewController:(NSInteger)viewNumber;
#end
AppController.m:
#implementation AppController
#synthesize mainMenu = _mainMenu;
#synthesize mainViewController =_mainViewController;
NSString *const kStartScreen = #"StartScreenViewController";
NSString *const kOnePlayerMenu = #"OnePlayerMenuViewController";
NSString *const kTwoPlayerMenu = #"TwoPlayerMenuViewController";
int test = 0;
enum{
kStartScreenView = 0,
kOnePlayerView,
kTwoPlayerView
};
-(void)awakeFromNib
{
[self setViewController:0];
}
-(void)setViewController:(NSInteger)viewNumber
{
[[_mainViewController view] removeFromSuperview];
if(viewNumber==kStartScreenView)
{
self.mainViewController = [[StartScreenViewController alloc] initWithNibName:
kStartScreen bundle:nil];
}
else if(viewNumber==kOnePlayerView)
{
self.mainViewController = [[OnePlayerMenuViewController alloc] initWithNibName:
kOnePlayerMenu bundle:nil];
}
else if(viewNumber==kTwoPlayerView)
{
self.mainViewController = [[TwoPlayerMenuViewController alloc] initWithNibName:
kTwoPlayerMenu bundle:nil];
}
[_mainMenu addSubview:[_mainViewController view]];
[[_mainViewController view] setFrame:[_mainMenu bounds]];
}
#end
StartScreenViewController.h:
#interface StartScreenViewController : NSViewController
- (IBAction)OnePlayer:(id)sender;
- (IBAction)TwoPlayer:(id)sender;
#end
StartScreenViewController.m:
#interface StartScreenViewController ()
#end
#implementation StartScreenViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (IBAction)OnePlayer:(id)sender
{
AppController *appControllerObj = [[AppController alloc] init];
[appControllerObj setViewController:1];
}
- (IBAction)TwoPlayer:(id)sender
{
AppController *appControllerObj = [[AppController alloc] init];
[appControllerObj setViewController:2];
}
#end
Actually, I figuered it out, all you have to do is make a class method in the AppController which stores the used object (self) as a class variable. Then you can access that object from anywhere. It might not be the most efficient way of doing it, I have no idea as I am just a beginner, but it worked for me! :D
Here's relevant the code:
-(void)awakeFromNib
{
[self setViewController:kStartScreenView];
viewObject = self;
}
+(id)getViewObject
{
return viewObject;
}
viewObject is defined as a class variable of type id.

UIViewController Retaining in ARC

I have a subclass of UIViewController -> MyPopUpViewController
#protocol MyPopUpViewController Delegate;
#interface MyPopUpViewController : UIViewController
{
}
#property (nonatomic, strong) id <MyPopUpViewControllerDelegate> delegate;
-(IBAction) buttonPressed:(id)sender;
#end
#protocol MyPopUpViewControllerDelegate
-(void) popupButtonPressed: (MyPopUpViewController*)controller;
#end
I cannot have this MyPopUpViewController as an instance variable because this comes externally, and there could be many and multiple of these popups can be up. So far I tried this, and it crashes on the delegate call due to not being retained:
MyMainViewController:
-(void)externalNotificationReceived: (NSString*) sentMessage
{
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
popupView.delegate = self;
[self.view addSubview:popupView.view];
[popupView setInfo :sentMessage :#"View" :#"Okay"];
popupView.view.frame = CGRectMake(0, -568, 320, 568);
popupView.view.center = self.view.center;
}
-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
NSLog(#"Popup Delegate Called");
[controller.view removeFromSuperview];
controller.delegate = nil;
controller = nil;
}
Once the popup comes up, and when the ok button is tapped, it crashes and never gets to that NSLog. How can I change
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
..so it would retain without making it an instance variable?
Thanks in advance.
You should be doing proper view controller containment by calling addChildViewController:.
- (void)externalNotificationReceived: (NSString*) sentMessage {
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
popupView.delegate = self;
[popupView setInfo :sentMessage :#"View" :#"Okay"];
popupView.view.frame = CGRectMake(0, -568, 320, 568);
popupView.view.center = self.view.center;
[self addChildViewController:popupView];
[self.view addSubview:popupView.view];
[popupView didMoveToParentViewController:self];
}
This will keep a proper reference to the view controller as well as properly pass various view controller events. Read about this in the docs for UIViewController and the "View Controller Programming Guide for iOS".
BTW - you should name your methods better. Example:
popupButtonPressed::
should be named:
popupButtonPressed:buttonNumber:
Usually delegates are weak-referenced instead of strong. I, myself, would name it something else as to not confuse other people.
Also, the following bit of code will have no effect:
-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
...
controller = nil;
}
the controller would be released (set to nil) automatically at the end of the scope.

How to Access `UItextView` data from another view controller

How I access the body of UITextView from another view controller for email here is my code
EmergencyMessageController *ViewB1 = [[EmergencyMessageController
alloc]initWithNibName:#"EmergencyMessageController" bundle:nil];
NSString *Em1 = ViewB1.Emsg.text;
NSString *body = [[NSString alloc] initWithFormat:#" ",Em1];//my problem is here to access Em1 data
controller.mailComposeDelegate = self;
[controller setSubject:#"Emergency Message"];
[controller setMessageBody:body isHTML:YES];
[controller setToRecipients:recipients];
[self presentModalViewController:controller animated:NO];
As you are creating another instance of "EmergencyMessageController" viewcontroller here, it won't give you any values.
You have to use the same instance variable using which you have set the email body in EmergencyMessageController.
or else as Vertogo said, this EmergencyMessageController should be the parent of this second view controller, so that you can easily access parent view controllers properties.
Hey user2396021 this is a simple example of Parent-Child View Controller,hope this will help you.
ParentViewController.h
Declare your textView as a class variable in parent view controller as below.
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
#property (nonatomic,strong) UITextView *mytextView;
#end
ParentViewController.m
Add textview to parentView Controller as below
- (void)viewDidLoad
{
[super viewDidLoad];
_mytextView = [[UITextView alloc]initWithFrame:CGRectMake(100, 100, 200, 30)];
[_mytextView setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:_mytextView];
}
ChildViewController.h
Make ParentViewController as a superclass(or you can say parentclass) of ChildViewController as below
#import "ParentViewController.h"
#interface ChildViewController : ParentViewController
#end
ChildViewController.m
Now you can easily access any class variable of parent class(ParentViewController) using "super" keyword.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Text value of uitextview from Parent class %#",[[super mytextView]text]);
}

UIGestureRecognizer - Get the reference to the touched UIViewController Instead of its View?

How do I get a reference to the UIViewController of a touched view?
I am using a UIPanGestureRecognizer on the view of a UIViewController. Here's how I initialize it:
TaskUIViewController *thisTaskController = [[TaskUIViewController alloc]init];
[[self view]addSubview:[thisTaskController view]];
UIPanGestureRecognizer *panRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
[[thisTaskController view] addGestureRecognizer:panRec];
In the tiggered action triggered using the gesture recognizer I am able to get the view from the parameter using recognizer.view
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
UIView *touchedView = [[UIView alloc]init];
touchedView = (UIView*)[recognizer view];
...
}
However what I really need is the underlying UIViewController of the view touched. How can I get a reference to the UIViewController that contains this view instead of only the UIView?
I would say that it is more a design issue than just getting a reference. So I would follow several simple advises:
Owner should catch events from its view. I.e. TaskUIViewController sould be a target to UIPanGestureRecognizer which you added to its view.
If a controller has a sub-controller and waits from its sub-controller some responses - implement this as delegate.
You have memory leak in your "handlePan:" method.
Here is a skeleton to solve your issue:
#protocol CallbackFromMySubcontroller <NSObject>
- (void)calbackFromTaskUIViewControllerOnPanGesture:(UIViewController*)fromController;
#end
#interface OwnerController : UIViewController <CallbackFromMySubcontroller>
#end
#implementation OwnerController
- (id)init
{
...
TaskUIViewController *thisTaskController = [[TaskUIViewController alloc] init];
...
}
- (void)viewDidLoad
{
...
[self.view addSubview:thisTaskController.view];
...
}
- (void)calbackFromTaskUIViewControllerOnPanGesture:(UIViewController*)fromController
{
NSLog(#"Yahoo. I got an event from my subController's view");
}
#end
#interface TaskUIViewController : UIViewController {
id <CallbackFromMySubcontroller> delegate;
}
#end
#implementation TaskUIViewController
- (id)initWithOwner:(id<CallbackFromMySubcontroller>)owner
{
...
delegate = owner;
...
}
- (void)viewDidLoad
{
UIPanGestureRecognizer *panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:panRec];
[panRec release];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
...
[delegate calbackFromTaskUIViewControllerOnPanGesture:self];
...
}
#end
[touchedView nextResponder] will return the UIViewController object that manages touchedView (if it has one) or touchedView's superview (if it doesn’t have a UIViewController object that manages it).
For more information, see the UIResponder Class Reference. (UIViewController and UIView are subclasses of UIResponder.)
In your case, since you happen to know that touchedView is your viewController's view (and not, for instance, a subview of your viewController's view), you can just use:
TaskUIViewController *touchedController = (TaskUIViewController *)[touchedView nextResponder];
In the more general case, you could work up the responder chain until you find an object of kind UIViewController:
id aNextResponder = [touchedView nextResponder];
while (aNextResponder != nil)
{
if ([aNextResponder isKindOfClass:[UIViewController class]])
{
// we have found the viewController that manages touchedView,
// so we break out of the while loop:
break;
}
else
{
// we have yet to find the managing viewController,
// so we examine the next responder in the responder chain
aNextResponder = [aNextResponder nextResponder];
}
}
// outside the while loop. at this point aNextResponder points to
// touchedView's managing viewController (or nil if it doesn't have one).
UIViewController *eureka = (UIViewController *)aNextResponder;

iOS - Create UIView subclass for rounded rectangle

I'm trying to create & use a very simple UIView subclass for a rectangle with rounded corners. I've created a new class as follows :
RoundedRect.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface RoundedRect : UIView
#end
RoundedRect.m
#import "RoundedRect.h"
#implementation RoundedRect
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[[self layer] setCornerRadius:10.0f];
[[self layer] setMasksToBounds:YES];
}
return self;
}
#end
I'm using iOS 5.1 with storyboards and have set the custom class property in the IB inspector window to 'RoundedRect', but when I run the app the rectangle still has square corners. Have I missed something obvious?
Thanks
Jonathan
In iOS 5 and up, there is absolutely no need to subclass - you can do it all from Interface Builder.
Select the UIView you want to modify.
Go to the Identity Inspector.
In "User Defined & Runtime Attributes", add "layer.cornerRadius" in Key Path, Type should be "Number" and whatever setting you require.
Also add 'layer.masksToBounds' as Boolean.
Done! With no subclassing, and all in IB.
The other guys have already answered the question but I would refactor it like this to enable use in nibs and in code
#import "RoundedRect.h"
#implementation RoundedRect
- (id)initWithFrame:(CGRect)frame;
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder;
{
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit;
{
CALayer *layer = self.layer;
layer.cornerRadius = 10.0f;
layer.masksToBounds = YES;
}
#end
The initWithFrame method is not called when the view is instantiated from a XIB file. Instead, the initWithCoder: initializer is called, so you need to perform the same initialization in this method.
For views that are loaded from a NIB file, the designated initializer is initWithCoder:. initWithFame: is not called in this case.
If UIView load from Nib, you should use method
- (void)awakeFromNib