I have few common views in most of my viewControllers. What I noticed is that I can reuse single code for all viewControllers which is absolutely wise. For this I decided to create a class Utils which has static methods like
+(void)createCommonViews:(float)yAxis:(NSString*)text;
In my case common views are three labels and two images.
Problem : I am not able to add these views from Utils. I am wondering how can I send self as a parameter so that I may add the views from Utils. It may be wrong to add views outside the viewController. In that case what can be the solution? Taking all these views in a UIView, setting return type of Utils method as UIView and then adding UIView to viewController (after calling method from viewController) might solve my problem. But what I am looking for is some other solution.
+(void) createCommonViews:(float)yAxis withText:(NSString*) text toTarget:(UIViewController*) target
{
//create views
[target addSuview:view];
}
But I think returning a Uiview and then adding it in the UIViewController afterwards, is a far better solution.
The method you're attempting is to have your view object as a singleton. This is uncommon at best, at worst a crash waiting to happen. Better design is for each of your view controller classes to have its own instance of your custom view, like so:
#interface MyCommonView : UIView
// ...
#end
#interface MyViewController_A : UIViewController {
MyCommonView *commonView;
}
#property (strong, nonatomic) IBOutlet MyCommonView *commonView;
#end
// Meanwhile somewhere else...
#interface MyViewController_B : UIViewController {
MyCommonView *commonView;
}
#property (strong, nonatomic) IBOutlet MyCommonView *commonView;
#end
Create a viewController that acts as a parent view for all your common stuff, call it CommonViewController then implement this in all the viewcontrollers you want it to appear
-(void) viewDidLoad
{
[self.view addSubView:[[CommonViewController alloc] initWithRect:..];
}
Or alternatively using xib files
Related
I have written a third party framework that displays a UIView on top of the existing view of the calling app, i.e., the app calls the framework/SDK and the SDK shows a UIView on top of the view that is currently on screen.
This is done by the use of delegates. The app sets the current view on screen as the delegate to the framework. The framework then uses this delegates and adds its own UIView as a subview using the code:
[[self delegate] addSubview:myVc.view];
where myVc is the ViewController in the framework.
Now I need to pass a method back to the calling app saying the view was shown on screen. Since, the delegate is a UIView, how do I pass a message to the calling class?
The reason why I have asked for a UIView delegate is because my UIView takes only a part of screen and I need the other part of the screen to show the remaining part of the app and be active. When I used ViewController as a delegate, it resulted in the other part of the screen being black instead of transparent.
So my question is how do I pass a message to the calling app, which calls the SDK and sets its view as a delegate. Thanks
You can post a notification that the caller listens for or you can accept a block as a parameter from the caller and execute that when the view is shown.
If I understood your question correctly, then you simply need to define protocol :
.h
#protocol YourFrameWorkViewDelegate <NSObject>
- (void)yourFrameWorkViewDidAppear;
#end
#interface YouFrameWorkView : UIView
#property (weak, nonatomic) id <YourFrameWorkViewDelegate> delegate;
#end
.m
#implementation YouFrameWorkView
- (void)didMoveToWindow
{
if ([self.delegate respondsToSelector:#selector(yourFrameWorkViewDidAppear)]) {
[self.delegate yourFrameWorkViewDidAppear];
}
}
#end
or you could call delegate from your VC like
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ([self.view.delegate respondsToSelector:#selector(yourFrameWorkViewDidAppear)]) {
[self.view.delegate yourFrameWorkViewDidAppear];
}
Of course any app that wants to use you frame work needs to implement that protocol. This would probably be rootVCs view would have to be subclass and implement your protocol. This is standard practice. Hope it helped.
Follow up after your comment:
Masure that view implements your protocol
.h
#interface ViewThatUsesYourFrameWork : UIView <YourFrameWorkViewDelegate>
#end
.m
#implementation ViewThatUsesYourFrameWork
- (void)yourFrameWorkViewDidAppear
{
}
#end
and also confirm that delegate is indeed set.
before you call respondsToSelector
if(!self.delegate)NSLog(#"Delegate is not set")
I want to maintain data encapsulation and have separated an NSObject class (.h and .m file) from my ViewController.m.
I have the Objective-C working correctly where my class is instantiated in the ViewController's viewDidLoad and I can set, get and NSLog the private values via my NSObject's methods.
What I can't do is in the MainStoryboard assign the Connection Outlets and Received Actions. My IBOutlets (a UILabel and UIButton) aren't showing in the Connection Inspector. However, I have many Objects in my ViewController's .[hm] file that I can setup the Outlet Connections to. It's just this new file's Objects that I can't view in the storyboard tool.
What am I doing wrong?
// GameTimer.h
#import <Foundation/Foundation.h>
#interface GameTimer : NSObject {
UILabel *gameTimerLabel;
NSTimer *gameTimer;
unsigned int gameTimerTicks;
}
#property unsigned int gameTimerTicks;
#property (nonatomic, retain) IBOutlet UILabel *gameTimerLabel;
#property (nonatomic, retain) IBOutlet UIButton *startButton;
// instantiate the timer
- (IBAction)onStartPressed:(id)sender;
// Update the gameTimerLabel, show new value to user
- (void)gameTimerShow;
// selector func for our timer, manages the tick count for all our timers
- (void)gameTimerEvent;
#end
// FirstViewController.m
#import "FirstViewController.h"
#import "GameTimer.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
GameTimer *myGameClock;
- (void)viewDidLoad
{
[super viewDidLoad];
myGameClock = [[GameTimer alloc] init];
[myGameClock setGameTimerTicks:33*10];
[myGameClock gameTimerShow];
unsigned long myticks = myGameClock.gameTimerTicks;
NSLog(#"Ticks=%lu", myticks);
}
I think you may be confusing encapsulation the role of a controller. Since the Cocoa Touch framework is a Model-View-Controller, the Controller is the "manager" that sits between the View's user interpretation and the Model's data and business rules. Therefore you must put your IBOutlets and IBActions in your UIViewController subclasses.
Build your timer into a separate class. The timer would then be considered part of the model that other controllers or other objects of the model can instantiate as needed. Let your Controller instantiate a "Timer". Then use the Controller to manage the "Timer" operations. If you need to display the elapsed time, then the Controller should get the elapsed time from the "Timer" object and put it in the appropriate control. If you need to set the length of time in the "Timer" then the Controller will get the value from a View's control and put it in the "Timer".
Hope this helps
I have been given a project to edit. I think this is a simple question but want to explain it in detail.I usually set up iPhone projects with interface builder and then have a view controller h and m file.
However this has been set up in a different way I am new to, the view has been coded.
The h file is a simple viewcontroller class like this:
#interface MainViewController : UIViewController
{
}
- (id)init;
#end
And then the m file has this:
#interface MainView : UIView
{
NSUinterger firstinterger;
}
- (id)initWithImages:(NSArray *)inImages;
#end
And then it has the #implementation MainView just after that with lots more code.
Further down however is where I need to add my code just after
#end
#implementation MainViewController
But I need to access the NSUinterger named first integer and I am unable to. I have tried a few ways of synthesizing etc. but I think I am doing it wrong. How I would get the value of it? I can access it in the code before the #implementation MainViewController but not after which is where I need it.
Synthesize the variable in MainView. Have an instance of the MainView in MainViewController and then you can access it by
MainView *mv = [[MainView alloc] init];
mv.firstInteger // gives you the variable.
NSUinterger firstinterger in your code shows no ';' at the end of that line, do you get compilation errors or is it just a typo in your question?
Okay, I've tried to figure this out over and over again.
I know the best practice is to have the App Delegate pass the managed object context to the first view controller in an application, and then have each subsequent view controller pass the managed object context down. However, when I'm using a Tab Bar Controller in my application, I can seem to wrap my head around that extra layer.
The only way I've been able to figure out how to do it is have the root view controller of each tab "Reach Back" into the app delegate to grab the context, but as I understand it this is poor form.
You can use interface builder to achieve the same thing.
Here is a slightly modified (for some additional clarity) version of Rog's original suggestion - notice the IBOutlet's
#interface AppDelegate : NSObject <UIApplicationDelegate> {
ViewController1 *vc1;
ViewController2 *vc2;
ViewController3 *vc3;
}
#property (nonatomic, retain) IBOutlet ViewController1 *vc1;
#property (nonatomic, retain) IBOutlet ViewController2 *vc2;
#property (nonatomic, retain) IBOutlet ViewController3 *vc2;
Then on the implementation file:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
vc1.managedObjectContext = self.managedObjectContext;
vc2.managedObjectContext = self.managedObjectContext;
vc3.managedObjectContext = self.managedObjectContext;
// Continue with your implementation logic
}
Then from within Interface Builder ctrl drag from your App Delegate to the View Controller nested within the Tab Bar Controller and hook up the relevant View controller from the contextual menu that appears.
The key was, in the end, not to rely on interface builder to build the tab bar controller. By doing it manually in code I'm able to easily pass the managed object context to the view controller as I create them in applicatoinDidFinishLaunchingWithOptions:
I used this article as my basis: http://www.iphonelife.co.uk/creating-a-uitabbarcontroller-programmatically/
You can also just do something like this in your AppDelegate:
CoreDataUsingViewController *vc = (CoreDataUsingViewController *)[[tabBarController viewControllers] objectAtIndex:1];
vc.managedObjectContext = self.managedObjectContext;
I was adding coreData to an existing project with a few different build targets and didn't want to recreate all the different UITabBarControllers from scratch. It was pretty easy to do this way, though I'm not sure if it's the most artful way to do it or not.
See also
How to share a ManagedObjectContext when using UITabBarController
Not sure if I understand your issue correctly but why not simply pass the MOC to the other view controllers in the same manner? Here's an example:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
ViewController1 *vc1;
ViewController2 *vc2;
ViewController3 *vc3;
}
// Declare properties as per normal
Then on the implementation file:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
vc1.managedObjectContext = self.managedObjectContext;
vc2.managedObjectContext = self.managedObjectContext;
vc3.managedObjectContext = self.managedObjectContext;
// Continue with your implementation logic
}
I hope it helps!
Rog
I have a UIScrollView that houses a gallery of images the user can scroll through. This view needs to be visible on each of three separate UIViewControllers that are housed within a UITabBarController. Right now, I have three separate UIScrollView instances in the UITabBarController subclass, and the controller manages keeping the three synchronized (when a user scrolls the one they can see, programmatically scrolling the other two to match, etc.), which is not ideal.
I would like to know if there is a way to work with only ONE instance of the UIScrollView, but have it show up only in the UIViewController that the user is currently interacting with. This would completely eliminate all the synchronization code. Here is basically what I have now in the UITabBarController (which is where all this is currently managed):
#interface ScrollerTabBarController : UITabBarController {
FirstViewController *firstView;
SecondViewController *secondView;
ThirdViewController *thirdView;
UIScrollView *scrollerOne;
UIScrollView *scrollerTwo;
UIScrollView *scrollerThree;
}
#property (nonatomic,retain) IBOutlet FirstViewController *firstView;
#property (nonatomic,retain) IBOutlet SecondViewController *secondView;
#property (nonatomic,retain) IBOutlet ThirdViewController *thirdView;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerOne;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerTwo;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerThree;
#end
#implementation ScrollerTabBarController
- (void)layoutScroller:(UIScrollView *)scroller {}
- (void)scrollToMatch:(UIScrollView *)scroller {}
- (void)viewDidLoad {
[self layoutScroller:scrollerOne];
[self layoutScroller:scrollerTwo];
[self layoutScroller:scrollerThree];
[scrollerOne setDelegate:self];
[scrollerTwo setDelegate:self];
[scrollerThree setDelegate:self];
[firstView setGallery:scrollerOne];
[secondView setGallery:scrollerTwo];
[thirdView setGallery:scrollerThree];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self scrollToMatch:scrollView];
}
#end
The UITabBarController gets notified (as the scroll view's delegate) when the user scrolls one of the instances, and then calls methods like scrollToMatch: to sync up the other two with the user's choice.
Is there something that can be done, using a many-to-one relationship on IBOutlet or something like that, to narrow this down to one instance so I'm not having to manage three scroll views? I tried keeping a single instance and moving the pointer from one view to the next using the UITabBarControllerDelegate methods (calling setGallery:nil on the current and setGallery:scrollerOne on the next each time it changed), but the scroller never moved to the other tabs.
Thanks in advance!
Certainly you should use only one instance of your scroller view. And it will works fine without any troubles. Use method setGallery: like you did, just ensure you add your singleScrollerForAll view to view of current controller in setGallery method:
-(void)setGallery:(UIView *)aScrollerView{
[self.view addSubview:aScrollerView];
}
and call:
[firstView setGallery:singleScrollerForAll];
or
[secondView setGallery:singleScrollerForAll];
and no need to do anything in other two controllers, because when you call addSubview: the subView will be automatically removed from previous superview.