How to get the tabController from subview in tabs - ios

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.

Related

init method for custom SCNView

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.

How do you use UIView with properties or variables inherited from ViewController?

I have UIViewController named ParentViewController.h and .m
Then I added UIView inside this ParentViewController.
I had uiview.h and uiview.h added and assigned to UIView inside ParentViewController.
From
-(void)drawRect:(CGRect)rect {}
which is located in uiview.m, I need to access to properties inside ParentViewController.
How do I do this? Am I using UIView wrong?
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
//I want my uiview to access this variable.
#property (nonatomic, strong) NSMutableArray *usedByUIView;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "uiview.h"
#implementation ParentViewController
- (void)viewDidLoad {
...
}
#end
uiview.h
#import <UIKit/UIKit.h>
#interface uiview : UIView
#end
uiview.m
#import "uiview.h"
#implementation uiview
-(id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self){
}
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"start drawing using the data from usedByUIView");
}
#end
There are a few answers on this subject but, summarizing them, you don't, at least not the way that you're doing it. UIView's do not have access to their view controller's and aren't supposed to need access. Of course, in the real world, sometimes it's not worth the overhead of coding around independent views so people hack in access to the controller access. This can be done by keeping an instance variable in the view, pointing to the controller, and assigning a reference to it after the view has loaded, or by overriding the init so you also pass a view controller, or lots of other ways. But before you do that think through the logic of why you want access to the controller from the view and see if there isn't a different way to do it.

Unable to access self.parentViewController.property

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.

How Do I call a method from a view contained in a view controller ?

I have a UIViewController which contains a UIView Subclass, from the subclass I want to call a method defined in the UIViewController which contains it. I do not want to instantiate a new instance of the view controller, because it contains information that I need within the method I am attempting to call. Here is a diagram trying to further clarify:
(UIViewController) MainView --> has method updateView
(UIView) SubView ---> Has Button that plays animation and has completion block
I want to call UpdateView in the completion block
I think you can set up a protocol in your Subview, which can be implemented by your ViewController
Your SubView.h
#class SubView;
#protocol SubViewDelegate <NSObject>
- (void)actionToPromoteToViewController: (NSString *)exampleString isSelected:(BOOL)exampleBool;
#end
Then, in your ViewController.h:
#interface MainViewController : UIViewController <SubViewDelegate>
and your ViewController.m, implement the method.
- (void)actionToPromoteToViewController: (NSString *)exampleString isSelected:(BOOL)exampleBool{
// Method Implementation
}
For 'correct' implementation you need a reference to view controller in your UIView Subclass:
#interface UIViewSubclass : UIView
...
#property UIViewControllerSubclass *viewController;
#end
Then set this viewController reference to your view controller and use it in completion block.
If you want a local solution (and not to extend UIViewSubclass with property) take a look at this answer: https://stackoverflow.com/a/3732812/326017

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

Resources