I am currently loading this code below on every view load. Is there a more performance direction I can take, like a singleton, or is this the best way to load ads in iOS?
- (void)viewDidLoad {
buttonsView.orientation = CSLinearLayoutViewOrientationVertical;
buttonsView.verticalAlignment = CSLinearLayoutItemVerticalAlignmentBottom;
self.ad.adUnitID = #"ca-app-pub-3940256099942544/6300978111";
self.ad.rootViewController = self;
self.ad.delegate = self;
self.ad.adSize = GADAdSizeFromNSValue(NSValueFromGADAdSize(kGADAdSizeSmartBannerPortrait));
[self.ad loadRequest:[GADRequest request]];
[super viewDidLoad];
}
There are a few ways you can do this. One method is to create a UIViewController super class. In the viewDidLoad method of the superclass implement the ad code. Any view which needs this ad code should just inherit from the super class and it get's this behavior. If each UIViewController has specific values to identify it as a unique view you can set this values in the before calling [super viewDidLoad] on super class. If this doesn't make any sense let me know and I can put some code together for you.
Another possible approach is to use method swizzling to modify the default behavior of the UIViewController and add your code this way. Just google "method swizzling objective-C". Here's one of the better articles on this that I like: https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
Related
I was working in a client project. I have written lot many view customisation code inside ViewDidLoad. I have models for data store and access.
The project was working fine. They hired a new iOS developer he said the code is not compliant with MVC architecture. The asked the reason why? He said the views are created inside viewDidLoad which is a controller of the Class hence it is not acceptable code. What should we do when its really dynamic views and can not be created using storyboard.
My answer is No
From apple doc
Controller Objects
A controller object acts as an intermediary between one or more of an application’s view objects and one or more of its model objects. Controller objects are thus a conduit through which view objects learn about changes in model objects and vice versa. Controller objects can also perform setup and coordinating tasks for an application and manage the life cycles of other objects.
I think that the controller has the responsibility to manage what the view look like.
In viewDidLoad,it is good to write one-time view customisation code here.
But if you write a lot configuration code to a view. I think it is better to use a subclass of UIView. This makes your code clear and easy to debug and maintain.
You can make differentiate between Controller and View in this way -
Use - (void)loadView {} delegate method to load your view from viewcontroller.
Suppose you have a view class -
CustomView.h
CustomView.m -
- (id)init {
return [self initWithFrame:CGRectZero];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor greenColor];
[self addSubview:self.centerView];
}
return self;
}
Now in your ViewController -
- (void)loadView
{
self.view = [[CustomView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
Please refer below url for more details -
http://matthewmorey.com/creating-uiviews-programmatically-with-auto-layout/
Hope this can be help you.
You would ideally have a dedicated class for your main view (the one that's accessed by self.view inside viewcontroller) - derived from UIView.
Inside this view class (say Myview.m) - you will create its subviews inside various view methods - such as init (for instantiation of subviews), layoutsubviews (for frame setting of subviews) and drawrect (any graphics drawing etc.) If the view has NIB, some processing will go inside awakeFromNib too.
So I know there a bunch of questions like this but none of their solutions seems to be helping me.
Let me start of by saying I am using storyboards so I would use initWithCoder instead of initWithNibName:.
Now that we got that bit out of the way. So, simply I want to pass an object to another view with a custom initializer.
So in my second view I would have code like this:
- (id) initWithMyLifeChangingObject: (MagicType *)object {
//Apparently this is important
self = [super init];
//Do some stuff with my magical object
//You know the story
return self;
}
And as the story goes you need to call your world class init:
[SecondClass alloc] initWithMyLifeChangingObject:object];
But initWithMyLifeChangingObject: isn't recognized and gives me a build error saying it is not in existence... so what do I do.
I spent much time to get a better understanding in delegation in Objective-C. I got it working for most cases, but there is a problem in a specific case, which I find difficult to understand. Let me explain what I am trying to do:
I have a custom view called GridLayoutView, which is subclass of UIView. I also have a view controller SomeViewController, which is the delegate of GridLayoutView.
I have a custom initWithFrame method, and I am conditionally calling another initialization method baseInit. That method calls a delegate method at some time. Here is some code from GridLayoutView:
//
// Delegator
// GridLayoutView.m
//
#implementation GridLayoutView
- (id)initWithFrame:(CGRect)frame
numberOfRows:(NSUInteger)rows
numberOfCols:(NSUInteger)cols
{
self = [super initWithFrame:frame];
if (self) {
self.numberOfRows = rows;
self.numberOfCols = cols;
self.numberOfCells = rows * cols;
if (self.numberOfCells > 0) [self baseInit];
}
return self;
}
- (void)baseInit
{
// do some more initialization stuff here
// ...
// then call a delegate method
[self.delegate someMethod:someObj];
// However, this method is not called because self.delegate is nil
}
and some code from SomeViewController:
//
// Delegate
// SomeViewController.m
//
#implementation SomeViewController
// ...
// in some method
self.gridLayoutView = [[GridLayoutView alloc] initWithFrame:gridLayoutFrame
numberOfRows:rowsCount
numberOfCols:colsCount];
self.gridLayoutView.delegate = self;
// ...
The delegate method never gets called within baseInit, because the delegate is nil at that time and it gets set after initWithFrame and baseInit methods are done. I have confirmed this.
I sense that there is something wrong in my workflow of delegation. I have a solution but I don't think it is the best way to go. The solution is basically passing the SomeViewController instance to the delegator by modifying the initWithFrame method such as:
- (id)initWithFrame:(CGRect)frame
numberOfRows:(NSUInteger)rows
numberOfCols:(NSUInteger)cols
delegate:(id<GridLayoutViewDelegate>)aDelegate
This approach works, but I am uncomfortable due to passing SomeViewController to GridLayoutView in its initWithRect. I am wondering if this is a good way to go with delegation or is there a better approach? I would be very grateful if someone can clear this for me.
If I'm understanding you correctly, there aren't many options here.
Modifying your initializer (as you suggested) to pass in the delegate. There is nothing wrong with that, don't know why you don't like it.
Remove the dependency on the delegate during initialization and instead, send whatever delegate message is appropriate when the delegate property is set by overriding the setter:
- (void)setDelegate:(id<GridLayoutViewDelegate>)aDelegate
{
_delegate = aDelegate;
// send whatever message makes sense to the delegate
[_delegate someMethod:object];
}
EDIT - noticed your comment
Your initialization method should not take any significant amount of time. It's unclear what you mean by 'loading views'. If you simply mean creating and adding subviews to a view then that is fast and there should be no need to communicate progress to a delegate (which you can't do anyway b/c the initialization is on the main thread and UI won't update until all of init is complete).
If you mean loading data that takes a long time, you should disconnect that from initialization and load the data in a background operation, sending progress messages to a delegate.
i would implement the setDelegate function and then call
[self someMethod:someObj]; from there
I want to draw a chart in an UIView. The question is how do I get the data (Points) to the view. If I create a protocol and set the UIViewController as the delegate where in the UIView do I call the delegate methods (initWithFrame? might be to early, and the delegate might not be set, awakeFromNib? but the view is 100% created in code, it has no nib file) ..
initWithFrame? might be to early, and the delegate might not be set.
In fact, the delegate cannot be set by the time you're in initWithFrame:, since the first thing you do with an object after allocation is initialization, i. e. until the init method returns, you can't call (well, it's idiomatic not to do so, at least) any other methods.
What you have to do is have a loadData or reloadData method, that the delegate must call explicitly after having set itself as the delegate of your view. I. e., from the view controller, you can call it like this:
#implementation ChartViewController
- (id)init
{
if ((self = [super init])) {
chartView = [[ChartView alloc] initWithFrame:self.view.frame];
chartView.delegate = self;
[self.view addSubview:chartView];
[chartView reloadData];
}
return self;
}
Then, in your chart drawing view, implement - reloadData as follows:
- (void)reloadData
{
// Call the delegate here,
// then do the drawing
}
A better way is to use UIViewController instead of a UIView. Because your that view has to manage data. Managing data is a UIViewController's job.
make a protocol but dont call it delegate. call it dataSource :D
anyways, call it when you first need the data .... as late as possible.. NOT in init.. maybe in the setDataSource call.
or when you draw for the first time and see you have no data..
look at UITableView to see how he does it and imitate that
I have a UIViewController called DebugViewController that contains a UITextView, and a public method called debugPrint which is used to write an NSString into the UITextView and display it.
Is it possible to write into the UITextView before I open the UIViewController, so that when I open it, the text previously written into it is displayed?
In my parent view controllers viewDidLoad method, I'm calling initWithNibName on the DebugViewController as follows
debugViewController = [[DebugViewController alloc] initWithNibName:#"DebugView" bundle:nil];
I then call debugPrint as follows
[debugViewController debugPrint:#"viewDidLoad"];
And some time later I call the following to open the debugViewController
debugViewController.delegate = self;
debugViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:debugViewController animated:YES];
However all the text previously written is missing.
Please let me know how I can use a view controllers methods before the view controller displayed to the user.
Thanks,
JustinP
What you are doing is a little non-standard. The danger with that as always is that if you don't really have an expert grasp on what you're doing, you can quickly find yourself in difficulty.
If you want something set before the view is displayed to the user, then the best way to do that is to do it in the viewWillAppear method. Put it there rather than in viewDidLoad because a view might loaded once but appear many times. Where you place it depends on whether the data changes from appearance to appearance.
So, if your data is pretty static and won't change, use the viewDidLoad method.
Assuming that you'll go for the viewWillAppear option, let's do the first step by having an ivar in the view controller:
NSString *myText;
set that after init:
debugViewController = [[DebugViewController alloc] initWithNibName:#"DebugView" bundle:nil];
debugViewController.myText = #"My text here";
then, in debugViewController's viewWillAppear method:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
myTextView.text = myText;
}
The view controller life cycle is complex as you can see from the View Controller Programming Guide for iOS. So I'd say best not stray from the path of least resistance unless you have good reason. That said sometimes the best way to learn is by experimentation.