I am trying to create or change a UIimage from another class using a class method. however when I try to address the view in the class controller I can't. e.g. [self.view addsubview:...] is this possible ? what would be the best way to do this ? Thanks in advance.
You import the header file of your UIImage (or better use UIIMageView) in the second class, then define it as an object. Add #property and #synthesize is the second .m. The reference shoul work after as addSubview will know about imported object.
Lets say this is your model class header file and you define your uiimageview:
#property (weak,nonatomic) IBOutlet UIImageView *modelImageView;
and this is your view controller and its implementation file you may use like this:
#import "YourModel.h"
#interface yourViewController() {
YourModel *yourModel;
}
end
#implementation yourViewController
- (void) viewDidLoad {
[super viewDidLoad];
[self.view addSubView:yourModel.modelImageView];
}#end
That was straight way to do and to make something better; just define some methods which controls your UIImageViewlike changeColor setBackgroundColor etc. and reach them from your viewController just using for example:
[yourModel setColor:[UIColor greenColor] placeHolderImage:[UIImage ...]];
So what I'm trying to explain in here you can reach your UIImageView wherever you want and you will just have one Model is reachable from every viewController.
Related
I have created a custom class "CustomSCNView" that inherits from SCNView. I want to use the custom class in another view controller. So I need to create a CustomSCNView object and use it to another class to manipulate things. But how can I create a CustomSCNView object in another class.
This is not working:
CustomSCNView *customView = [[CustomSCNView alloc]init]; //in viewcontroller.m
Sorry forgot to mention I used the interface builder to drag a SCNView to the view controller and then set its class to CustomSCNView.
I'm a bit confused by your question, but I've created a sample project at https://github.com/NSGod/CustomSCNView that may do what you're looking for.
First, the storyboard has 2 CustomSCNViews laid out side by side in the ViewController's view. Like you did, I dragged 2 SCNViews from the IB palette to the view and then set the custom class to be CustomSCNView.
Second, is the CustomSCNView class which is defined as follows:
#import <Cocoa/Cocoa.h>
#import <SceneKit/SceneKit.h>
#interface CustomSCNView : SCNView
#property (nonatomic, assign) BOOL allowsRotation;
#end
You can see, it has an allowsRotation property that any other object can set.
To set a default value for allowsRotation, other than NO, you can override initWithCoder: which is what's used when you set up the views in Interface Builder like you did:
#import "CustomSCNView.h"
#implementation CustomSCNView
- (id)initWithCoder:(NSCoder *)coder {
if ((self = [super initWithCoder:coder])) {
_allowsRotation = YES;
}
return self;
}
#end
The ViewController then has 2 IBOutlets to both CustomSCNViews.
#import <Cocoa/Cocoa.h>
#import <SceneKit/SceneKit.h>
#class CustomSCNView;
#interface ViewController : NSViewController
#property (weak) IBOutlet CustomSCNView *sView1;
#property (weak) IBOutlet CustomSCNView *sView2;
#end
ViewController.m:
#import "ViewController.h"
#import "CustomSCNView.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_sView1.allowsRotation = NO;
_sView2.allowsRotation = YES;
}
#end
You can see that in viewDidLoad, you can set the allowsRotation property of both views to whatever you want. When you run this application, 2 instances of CustomSCNView are created for you automatically (via initWithCoder:), when the storyboard/nib files are loaded. There's no need to create another instance of a CustomSCNView to be able to set the properties of the 2 existing instances you already have.
If you look at the documentation for SCNView it tells you:
You can create a SceneKit view by using its initWithFrame:options:
method or by adding it to a nib file or storyboard.
So you cannot use the init method unless you have implemented your [CustomSCNView init] method to call [super initWithFrame:options:].
If you need access to custom subclass properties from Interface Builder, mark those properties IBInspectable (and possibly implement IBDesignable). That's documented by Apple here, and nicely summarized on NSHipster.
In any initialization path, you must call the superclass's designated initializer. For SCNView, that appears to be initWithFrame:options: (not documented as such, but the header strongly implies it). See this document on multiple initializers and subclassing.
That said, though, subclassing SCNView is a code smell that you might be fighting the framework and working too hard.
I am trying to do the following, and not able to find a straightforward answer.. It is related to this :Passing uitextfield from one view to another. But not exactly.
I have a Firstview.m, from which I push to a Secondview.m. The Secondview.m has a UITextView. I allow the user to edit the UITextView on Secondview.m. Now I want to store this text value in a variable in Firstview.m. One way to to do this is as follows
in Firstview.h
#property (nonatomic) Secondview *secondView;
That is keep a secondView variable in Firstview itself. But this doesn't seem efficient. Ideally I should only have 1 NSString text field in FirstView. What is the right way to do this ? Thanks
You can achieve this by using Delegation in Objective-C.
In your SecondView.h add following right after Header Inclusion
#protocol YourDelegateName <NSObject>
-(void)setText:(NSString *)strData;
#end
Also add delegate property to your header for accessing them in calling class, like below (This goes with other properties declaration in SecondView.h file):
#property (nonatomic, weak) id<YourDelegateName> delegate;
Now, Comes the calling the delegate part. Say, you want to save the text value of UITextView of SeconView in strTextViewData of FirstView class, when the following event occurs:
- (IBAction)save:(id)sender
{
[self.delegate setText:self.txtView.text]; // Assuming txtView is name for UITextView object
}
Now, In FirstView.h add YourDelegateName in delegate list like below:
#interface FisrtView : ViewController <YourDelegateName>
#property (nonatomic, reatin) NSString *strTextViewData;
#end
And then in FisrtView.m file when you create instance of SecondView class, set delegate to self like below:
SecondView *obj = [[SecondView alloc] initWithNibName:#"SeconView" bundle:nil];
obj.delegate = self; // THIS IS THE IMPORTANT PART. DON'T MISS THIS.
Now, Implement the delegate method:
-(void)setText:(NSString *)strData
{
self.strTextViewData = strData;
}
Applying this to your code will do what you want. Also, Delegation is one of the most important feature of Objective-C language, which - by doing this - you will get to learn.
Let me know, if you face any issue with this implementation.
I am struggling to make a simple thing (at least I think it's simple) but I just can't do it!
I will try to explain a little bit.
It's an app which displays information. When the user is inside a view, he can click on a button, which displays a popoverview, where he can choose which information he wants to know.
Actually, I can't create an action that changes the UILabel text I created in the main view when the user clicks on the popoverview's buttons.
Anyone has any idea?
Just for you to know: the main view I created a class for it, and also for the popoverview. Although, the popover view I created its design in a XIB file (I don't know if this is important, that's why I am putting this).
Well, I hope you guys were able to understand my question.
Thanks in advance.
Fernando.
Just create a property from the viewcontroller and access it from the consumer (other viewcontroller )
You will have to use delegation in order to see changes in the main view when you are making different actions inside the popover. First, you need to create a protocol inside your popover controller header file:
#import <Foundation/Foundation.h>
#class MyPopoverController;
#protocol MyPopoverDelegate
- (void)valueChanged:(NSString*) newVal;
#end
#interface MyPopoverController: UIPopoverController
#property (weak) id<MyPopoverDelegate> delegate;
#end
Then in .m you implement it like this:
- (void) someActionOccured
{
if([self.delegate respondsToSelector:#selector(valueChanged:)]){
[self.delegate valueChanged:valueYouWantToSendBack];
}
}
Remember that in your main class you have to implement MyPopoverDelegate protocol:
#interface MainViewController: UIViewController <MyPopoverDelegate>
And when you instantiate your popover controller:
/*
** inside MainViewController.m
*/
// remember to assign it's delegate
MyPopoverController *popoverController = [MyPopoverController alloc] init];
popoverController.delegate = self;
Also, you'll have to implement the protocol's method:
/*
** inside MainViewController.m
*/
- (void)valueChanged:(NSString*) newVal
{
// process the string and display it where you need it
}
I have a class that extends UIViewController called HorizontalModal, which, among other things, I want to automatically generate a back button on each view that extends it. If I put the code to draw the back button in the viewDidLoad method for HorizontalModal, nothing shows up. But when I put that same code in the viewDidLoad method for the class that inherits from HorizontalModal, the button shows up fine.
E.g:
self.backButton = [[BackButton alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
[self.view addSubview:self.backButton];
Seems to me that I'm missing something about class extension and inheritance in objective C. Clearly self.view in HorizontalModal and self.view in the child class refer to two different things. So is there a way to keep that method in the HorizontalModal class, but be able to add the back button to the child class view? IE, can I access a sub-class property from a super class? Or is there a different way of doing this (maybe with categories)?
Thanks
If I put the code to draw the back button in the viewDidLoad method
for HorizontalModal, nothing shows up. But when I put that same code
in the viewDidLoad method for the class that inherits from
HorizontalModal, the button shows up fine.
It sounds like your subclass's -viewDidLoad isn't calling it's superclass's -viewDidLoad, like:
- (void)viewDidLoad
{
[super viewDidLoad];
// subclass-specific view setup stuff here
}
That allows the superclass's version of -viewDidLoad to do its thing first, followed by whatever the subclass needs to do.
can I access a sub-class property from a super class? Or is there a
different way of doing this (maybe with categories)?
If the superclass needs some method that every subclass is expected to provide, then you should put that method in the superclass and let subclasses override it. Normally, the subclass's implementation of the method calls the superclass's version, but if that's required then you should document that fact.
The same is true for properties: if your superclass needs to access some property of the subclass (e.g. backButton), the superclass should define that property itself. The subclass is then free to change the value of the property or even provide different property accessors, and code in the superclass that uses the property will just work. In general, the superclass shouldn't have to know anything about its subclasses.
So, to be more concrete, let's say you have MyModal, a subclass of HorizontalModal:
#interface HorizontalModal : UIViewController
#property(strong) UIView *backButton;
#end
#implementation HorizontalModal
- (void)viewDidLoad
{
self.backButton = [[BackButton alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
[self.view addSubview:self.backButton];
}
#end
#interface MyModal : HorizontalModal
//...
#end
#implementation MyModal
- (void)viewDidLoad
{
[super viewDidLoad];
// more code specific to MyModal's view hierarchy
}
#end
Now the -viewDidLoad method of HorizontalModal will be able to access the backButton property, even if MyModal has changed the value of the property or overridden the accessors. Remember that self points to the exact same object in both versions of -viewDidLoad, so both versions have access to the same information.
The title is what I think I need but i will go back one step. I want to create a class which handles certain things in an iOS app. This class might be called by multiple UIViewcontrollers in an iOS app. The class may need to show a UIView at some stage for user input. So my question is how can I show a UIView when I don't know which subclass of UIViewController is calling it? To what can I add the UIView from this class?
I suppose there are two possible answers either the class finds the current UIViewController or the calling subclass of UIViewController passes itself to the class so the class knows.
How is this supposed to be done.
Thanks guys for your help.
I'm going to expand on #ericleaf's comment regarding using a protocol and subclasses. It sounds like you are asking the following:
How can I create a resusable, generic class that presents a view
within a UIViewController subclass?
A great way to do this is to define a protocol in your generic class and have your view controller subclasses support this protocol. The protocol defines an interface for your custom class to comunicate with it's delegate, in this case a UIViewController subclass. Other than the protocol, the objects don't need to know anything else about the implementation of each other.
Any information your custom object needs to be able to present views within it's delegate would be passed via protocol methods. The specifics of the protocol are up to you based on your needs. You could have the custom object "ask" the delegate for information (e.g. what view should I put a subview in?) or you could have the protocol provide information to the delegate and let the delegate deal with it (e.g. here is a subview you can put wherever you want).
There is a lot of great documentation on protocols available on SO and elsewhere. This is long enough already so I kept the example fairly simple.
custom class .h file with protocol definition
// my custom class that adds adds a view to a view controller that supports it's protocol
// forward class definition for the protocol
#class MyAwesomeObject;
#protocol MyAweseomeObjectDelegate <NSObject>
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject;
#end
// this could be defined such that the delegate *must* be a UIViewController. I've left it generic.
#interface MyAwesomeClassObject : NSObject
#property (nonatomic, weak) id <MyAwesomeObjectDelegate> delegate;
#end
custom class .m file
// MyAwesomeObject.m
#import "MyAwesomeObject.h"
#implementation MyAwesomeObject
// this is a dumb example, but shows how to get the view from the delegate
// and add a subview to it
- (void)presentViewInDelegate
{
UIView *containingView = [self.delegate viewForMyAwesomeObject:self];
if (containingView) {
UIView *subview = [[UIView alloc] initWithFrame:containingView.bounds];
subview.backgroundColor = [UIColor redColor];
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[containingView addSubview:subview];
}
}
MyViewController .h using the custom object
// MyViewController.h
#import "MyAwesomeObject.h"
#interface MyViewController : UIViewController <MyAwesomeObjectDelegate>
#property (nonatomic, strong) MyAwesomeObject *awesomeObject;
#end
MyViewController .m using the custom object
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)init
{
self = [super init];
if (self) {
_awesomeObject = [[MyAwesomeObject alloc] init];
_awesomeObject.delegate = self;
}
return self;
}
// MyAwesomeObjectDelegate
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject
{
return self.view;
}
You can get the class into a string and do a compare.
For example, lets assume your custom UIViewController subclass is CustomViewCon and the UIViewController object reference is myUnknownClassObject, then:
NSString *classString = NSStringFromClass([myUnknownClassObject class]);
Then you can:
if([classString isEqualToString:#"CustomViewCon"]){
//do something like maybe present a particular view
myUnknownClassObject.view = myCustomView; //or anything..
}
Similarly you can check for any class.
Edit: According to the suggestions from comments, you could also do the following(better way):
if([[myUnknownClassObject class] isKindOfClass:[CustomViewCon class]]){
//same as before
}
Why wont you use a block for this?
BaseViewController.h:
#property (copy) void (^addViewBlock)();
- (IBAction)showViewWhenNeeded;
BaseViewController.m:
- (IBAction)showViewWhenNeeded
{
if (self.addViewBlock)
self.addViewBlock();
}
And in your child class, set that block's actions, and call the method when you feel like you should put up a view.
ChildViewController.m
// within some method, propably init or smth
[self setAddViewBlock:^{
[self.vied addSubView:...];
}];
// when need to actually add the view
[self showViewWhenNeeded];