Unable to access self.parentViewController.property - ios

I have implemented a subclass of ECSlidingViewController.
InitialViewController.h
#interface InitialViewController : ECSlidingViewController
#property (nonatomic, strong) WebViewController *webVC;
#property (nonatomic, strong) MenuViewController *menuVC;
#end
I need access to the .webVC from the .menuVC, but am unable to access them from self.parentViewController.webVC getting the error:
"UIViewController does not have a member named webVC"
but when I check in the debugger
po self: <MenuViewController>
po self.parentViewController: <InitialViewController>
po self.parentViewController.webVC: error: property 'webVC' not found on object of type 'UIViewController *'
However, when I navigate the object tree of self in the local variable window, I can go
-self <MenuViewController> 0x0000001
-UITableViewController
-UIViewController
-_parentViewController <InitialViewController>
-webVC <WebViewController
-menuVC <MenuViewController> 0x0000001
Where self and self.parentViewController.menuVC have the same memory address
When I navigate down and right-click->print Description, I get
Printing description of self->_parentViewController->menuVC:
<MenuViewController: 0x8e8c720>
Those '->' and the '_' make me think I'm seriously missing something involving pointers and ivars.
I'm also interested if there is a better way to implement this functionality. This kind of smells like I'm breaking MVC with a child Controller knowing about itself and its parent. However, selections on the menu directly manipulate the webView I have loaded.

Answer:
UIViewController does not have this property built in so you need to cast it:
[(InitialViewController*)self.parentViewController webVC]
Explanation:
This is a question of Classes and SubClasses. Say I have a class MyView which is a subclass of UIView. This class has a couple methods on it, -(void)method1; and -(void)method2;. The .h would look something like this:
#interface MyView : UIView
- (void)method1;
- (void)method2;
#end
I then create a MyView* named thisView and assign it to my ViewController's view property:
#import "MyView.h"
- (void)viewDidLoad
{
MyView* thisView = [[MyView alloc] init];
[self setView: thisView];
}
The UIViewController class's property, view is declared like this:
#property (...) UIView* view;
Pay extra special attention to the UIView* there.
Because view is declared as a UIView*, it will accept values that are subclasses of UIView*, but when I access it later, it will give me a MyView in a box labeled UIView.
In other words, consider this code:
- (void)viewDidLoad
{
MyView* thisView = [[MyView alloc] init];
[self setView: thisView];
UIView* returnedView = thisView; //This is perfectly fine
}
UIViewController will return something along the lines of returnedView, which is a MyView in a UIView container.
When your program goes to access the view, all it knows is that has received something in a box labeled "UIView", so it only knows that it can run methods on it that are present in the UIView class. We have to explicitly tell it "No, this is a MyView," and we do that with casting:
[self.view method1]; //This will break
[(MyView*)self.view method1]; //This will work
Back to your problem
When you access self.parentViewController, you are given a InitialViewController in a UIViewController box, so you have to explicitly tell your program that it is an InitialViewController. You do that by casting:
InitialViewController* parentVC = (InitialViewController*)self.parentViewController;
[parentVC.webVC doSomething];
Warning:
Your VC that is accessing webVC must have a declaration of (InitialViewController*), so you'll need to watch out for forward declaration errors.
If you are unfamiliar with this, it is when Class A imports Class B, but Class B also imports Class A:
A
--->Import B
B
--->Import A
Which results in
A
--->Import B
------->Import A
----------->Import B
and so on.
A class cannot import itself.

Related

ios Passing TextView from PushView to PresentingView

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.

Access subclass property from super class

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.

How to create a class with a UIViewController when the subclass of UIViewController is unknown

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];

Override UIViewController.view with specific type

Let's consider an application with highly customized or complex views.
We'll have a specific kind of view-controller sending methods to a specific kind of UIView, where the UIView is itself composed of a number of other views.
The view should have a rich, domain-specific interface, allowing the controller to act is a thin "glue" layer between it and a similarly rich model.
So we override our controller's view property as follows:
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
#property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
Ovverriding makes sense over defining another accessor, and I can just send messages to the specific type of UIView without having to cast.
Problem: The only problem is that it results in a compiler warning:
property type 'PlaybackView *' is incompatible with type 'UIView *' inherited from 'UIViewController'
. . and I like to build code that doesn't have any warnings. This way a valid warning doesn't get missed by being buried amongst other warnings.
Question:
Is there a way to suppress this particular warning?
Why is this part of the default settings, when most modern OO languages will happily allow overriding a property or method in a sub-class so that it returns a more specific sub-class of the type declared in the super-class?
The problem here is not not the override of the property, its using a forward declaration of the class type.
So this...
#class PlaybackView;
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
will give you the mentioned warning because the compiler cannot know the inheritance hierarchy of PlaybackView. UIViewController has a contract to provide a UIView from its view property
Its telling you that it thinks PlaybackView is not a UIView
The simple solution here is to use a #import instead to give the compiler full knowledge of PlaybackView...
#import "PlaybackView.h"
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
alternatively (but really bad form as the PCH is an optimising feature and shouldn't manage dependancies ) is to add #import "PlaybackView.h" to your projects PCH
As suggested in another answer using #import instead of #class will clear the warning but it is advised to import as little as possible in the header, so I would recommend leaving the view unchanged and having an additional PlaybackView * playbackView:
It is perfectly fine to have both view and playbackView pointing to the same view.
Classes that need to have knowledge of your specialized view have to import your controllers header, so they could just use playbackView in the first place.
More important, if you want to embed your specialized view as a subview in the future (which happens often like adding a UIScrollView superview), you won't have to refactor other code and classes!
It's plain cleaner.
I do not think override UIViewControllers view property is a good way .
I think it is better to do like this :
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
//#property (nonatomic, strong) PlaybackView* view; //you do not need this property
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
and in the .m file .
- (void)loadView
{
PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
// set the mainView
self.view = mainView;
}
and you can use your PlaybackView like this .
((PlaybackView *)(self.view)).oscClient
or
((PlaybackView *)(xxxViewControler.view)).oscClient
Perhaps you could declare another method that provides the cast for you, in a sense.
#implementation PlaybackViewController
- (void)viewDidLoad {
[super viewDidLoad];
// use view_ property instead of view
self.view_.foo = 1;
}
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
self.view = [[PlaybackView alloc] initWithFrame:frame];
}
- (PlaybackView *)view_ {
return (PlaybackView *)self.view;
}
Not exactly the cleanest approach, but it does avoid the cast on self.view (by not using self.view, though)
[UPDATE]
Finally I've probably found a solution that suit the problem:
This is a quick and dirty just to suppress the warning, try to wrap your code between these lines
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//YOUR CODE
#pragma clang diagnostic pop
or -Wall
To see more about compiler warning suppression Clang Manual
[OLD ANSWER]
I'd like to give my 2 cents.
If I understood well you are trying to create a sort of Abstract factory, that gives you a specialized version of a view based on the view controller funcionality. In my opinion storyboards doesn't work well in that kind of design, but I'd like to give you my vision about it.
First I will create an abstract class for your view controller where in the interface you declare all property you need in all your VC sublcasses, such as :
OSClient
AbstractStageLayoutView
PlaybackView as weak
playbackProperty
The PlaybackView class is a class cluster such as NSNumber, you call a factory method on it, that will return an object that could be different from case to case. If you inspect an NSnumber it returns a different object if you create a float or an integer, but they are all subclasses of NSNumber, and NSNumber declares all the property of its subclasses, but it doesn't implement them.
Now what you can do in the -viewDidLoad method of the abstract class is call a method like that
PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;
The playbackProperty can be valued inside the User defined runtime attibute in the viewcontroller storyboard editor.

How to get the tabController from subview in tabs

I have a Class: myTabController, in this class I have a UITabBarController, which has 4 subviews in this UITabBarController.
Now I am in my first subview, say it's tab1,
I can call self.parentViewController to get the UITabBarController, and this UITabBarController is owned by myTabController, but How can I get myTabController then? Cause I want to access some data in myTabController.
Thanks Ahead,
Regards
From your wording I am assuming that you have not subclassed UITabBarController. I would suggest having a property on all four view controllers, something like theTabController, which points to an instance of your class. Declare this property like this (in the subviews):
#class myTabController;
...
#interface MySubview : UIView {
...
myTabController * theTabController;
...
}
...
#property (nonatomic, assign) myTabController * theTabController;
Then, in each subview's implementation, add a synthesize statement. It's also a good idea to import the header of myTabController in the .m, even though we have an #class in the subview's header. I used an #class to prevent circular imports.
#import "myTabController."
...
#implementation MySubview
#synthesize theTabController;
...
#end
From myTabController, you need to set this property on each subview like this:
subview1.theTabController = self;
subview2.theTabController = self;
...
subviewx.theTabController = self;
Finally, use the theTabController property sub inside each subview with self.theTabController.
I also have to point out: it's never good to have a class name that starts with a lower case letter. myTabController should really be MyTabController.

Resources