How to get canDisplayBannerAds producing viewDidLayoutSubview callbacks? - ios7

In my code, when I set canDisplayBannerAds=YES on my view controller, I get callbacks to viewDidLayoutSubviews when the ad disappears but not when the ad appears. I'm guessing this is because Apple moves the original self.view of the view controller to self.originalContentView when you set canDisplayBannerAds to YES.
My question is, what is a reasonable work around for this?

My solution to this problem is to replace self.view before setting canDisplayBannerAds=YES with a UIView that overrides layoutSubviews.
#protocol LayoutViewDelegate <NSObject>
- (void) layout;
#end
#interface LayoutView : UIView
#property (nonatomic, weak) id<LayoutViewDelegate> delegate;
#end
#implementation LayoutView
- (void) layoutSubviews {
[super layoutSubviews];
if (self.delegate) [self.delegate layout];
}
#end
I do this replacement in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Calendar.viewDidLoad");
// Replace the view, before setting up ads for iOS7, so we can get callbacks for viewDidLayoutSubviews; otherwise, we only get viewDidLayoutSubviews callbacks when ad disappears.
if ([Utilities ios7OrLater]) {
self.layoutView = [[LayoutView alloc] initWithFrame:self.view.frame];
self.view = self.layoutView;
self.layoutView.delegate = self;
}
}
And in viewDidAppear, I do:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([Utilities ios7OrLater]) {
self.canDisplayBannerAds = YES;
}
}
And I add the delegate method:
// This *always* gets called when the banner ad appears or disappears.
#pragma - LayoutViewDelegate method
- (void) layout {
// do useful stuff here
}
#pragma -

Related

UITableViewController<UISearchBarDelegate> not Calling the UISearchBarDelegate methods

Given the following UITableViewController, why can't I get any of the UISearchBarDelegate methods to be called? I'm omitting the UITableViewDelegate and UITableViewDataSource methods.
#import <UIKit/UIKit.h>
#interface OpportunitySearchViewController : UITableViewController <UISearchBarDelegate>
#end
#import "OpportunitySearchViewController.h"
#interface OpportunitySearchViewController ()
#end
#implementation OpportunitySearchViewController
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
NSLog(#"initWithCoder:");
};
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"self=%# self.view=%# ", self, [self view]);
}
- (void)viewDidUnload {
[super viewDidUnload];
NSLog(#"viewDidUnload");
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
NSLog(#"shouldAutorotateToInterfaceOrientation:");
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - UISearchBarDelegate
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSLog(#"searchBar:textDidChange:");
}
#end
I've confirmed that my Storyboard scene is configured to use the OpportunitySearchViewContoller class and I've confirmed that the UISearchBar on the scene has the First Responder set as it's delegate. Although that might be wrong, but I couldn't find a File's Owner in the Storyboard interface.
Help would be appreciated.
Don't connect the first responder to your class as the delegate. Drag from the search bar to the controller object itself and make that the connection to the delegate.

Obj-C: Delegate method not getting called

I'm new to iOS Dev, I'm following the Stanford CS193P classes for Fall 2010. I'm on assignment 3 and I'm setting my delegate to my view and by using the debugger I'm noticing the call to my delegate method won't happen, I don't understand what could be happening. My code is as follows:
GraphViewController.h:
#interface GraphViewController : UIViewController <GraphViewDelegate> {
GraphView *graphView;
float scale;
}
#property (retain) IBOutlet GraphView *graphView;
#property float scale;
- (IBAction)zoomIn;
- (IBAction)zoomOut;
#end
GraphViewController.m:
#implementation GraphViewController
#synthesize graphView, scale;
- (NSString *)functionForGraph:(GraphView *)requestor {
NSLog(#"%#", #"culo peluo");
return #"lol";
}
- (float)scaleForGraph:(GraphView *)requestor {
return self.scale;
}
- (IBAction)zoomIn {
}
- (IBAction)zoomOut {
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.graphView.delegate = self;
self.scale = 20;
[self.graphView setNeedsDisplay];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
GraphView.h:
#class GraphView;
#protocol GraphViewDelegate
- (NSString *)functionForGraph:(GraphView *)requestor;
- (float)scaleForGraph:(GraphView *)requestor;
#end
#interface GraphView : UIView {
id <GraphViewDelegate> delegate;
}
#property (assign) id <GraphViewDelegate> delegate;
#end
GraphView.m:
#implementation GraphView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGFloat cgScale = [self.delegate scaleForGraph:self];
[AxesDrawer drawAxesInRect:self.bounds originAtPoint:self.center scale:cgScale];
}
- (void)dealloc
{
[super dealloc];
}
#end
Put a break point on the line where you set the graph view's delegate.
Inspect the graphView variable. Is it nil?
This happens to me all the time and it's always (well nearly always) because I have failed to connect the outlet to the view in interface builder.
Let's say class YourClass is a delegate for some other class. And delegate methods are not called, despite that delegate property is set up.
Most probably the problem is that your class instance, that is a delegate for your other class is released before the delegate method is called on it. Make it more persistent by making this class a property or instance variable of other class or by using dispatch_once. For example,
Change
YourClass *instance = [[YourClass alloc] init];
by
#property(nonatomic, strong) YourClass *instance;
self.instance = [[YourClass alloc] init];
This problem occurs because in ARC everything that is created inside a method (and is not an instance variable) is released, when method finishes. And delegate methods cannot be called, that are invoked by some background process.
I've written a big blog post on this problem. This is pretty common situation.
http://alwawee.com/wordpress/2013/07/31/on-xmppframework-delegate-method-not-being-called/
Ive got a few pointers for ya on this one:
You don't need an iVar (internal Variable) if you have the variable as a property, it will still work, I've never know it not to.
GraphView
#interface GraphView : UIView {
}
#property (assign) id <GraphViewDelegate> delegate;
#end
GraphViewController
#interface GraphViewController : UIViewController <GraphViewDelegate> {
}
#property (retain) IBOutlet GraphView *graphView;
#property float scale;
- (IBAction)zoomIn;
- (IBAction)zoomOut;
#end
You need to set your delegate in your view to be able to use it.
GraphViewController
#implementation GraphViewController
( ... )
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = self;
}
return self;
}
( ... )
#end
You are not calling the delegate functions anywhere in your GraphView.m. Try testing it out by placing this piece of code in your - (id)initWithFrame:(CGRect)frame method, right before you return.
[self.delegate functionForGraph:nil];
and see if it logs any message to the console.
Of course this is just to test the delegate implementation, you should actually be using this delegate call in some other method in your GraphView.m which is able to process your requestor

UITextView member variable is always NIL in my UIViewController [duplicate]

In my nib, I have a UITextView component.
In my UIViewController I have a UITextView member field and I have used IB to make sure that the two are connected (at least I think I did that by dragging from "Files Owner" and dropping it on the UITextView in IB and selecting my member variable).
Now, when I update the text via setText:, the UITextView still remains blank.
When I break into my code, the member variable (called textView) is nil.
So I am guessing I have not used IB correctly. How can I make sure that this variable is connected to the UITextView graphic element?
Here is the code
// My interface looks like
#import <UIKit/UIKit.h>
#interface DetailsController : UIViewController
{
UITextView *textView;
}
#property (nonatomic, retain) IBOutlet UITextView *textView;
#end;
// and the implementation
#import "DetailsController.h"
#implementation DetailsController
#synthesize textView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
// used pressed the "Done" button
- (IBAction)done
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)setDetails: (NSString *)detail withQuote:(NSString *)quote
{
NSLog(#"%#", textView);
[textView setText:detail];
}
#end
save nib file after connecting outlet, build project and run, it should work if it's connected
I can't see agere you are calling setText, but I think that you try to do it in the initializer. GUI code should not be done before the viewDidLoad in the case of using XIB's or loadView if you make your GUI programatically, or the other viewWill/viewDid... methods.
The reason is because the views are not loaded before they are actually needed, it is called lazy loading. You should Google that for more info.

UINavigationController: Apply Common Padding/Margin to all Popped View Controller's Views

I have a UINavigationController and I would like the view of every view controller that is popped onto the stack to have a common padding/margin (e.g. 25 pixels on all sides). What is the best way to accomplish this?
I originally thought that I could implement UINavigationControllerDelegate and inside the navigationController:didShowViewController:animated or navigationController:willShowViewController:animated methods, simply change the frame of the view controller that was about to be displayed. This does not seem to have an effect though.
I tried to do the same thing inside the view controller's viewDidAppear and viewWillAppear methods, but this also did not work. Ideally, I don't want to put any logic in the controllers anyway, as they may not always be used inside a navigation controller.
One last idea that I haven't tried yet is to create a "wrapper" UIViewController that would actually get pushed onto this stack. This wrapper would add the real view controller's view as a subview with a frame that would provide the desired margin. The downside here is that I would need to subclass UINavigationController and override pushViewController:animated, where the wrapper would be initialized and pushed. Apple's documentation indicates that UINavigationController is not meant to be subclassed.
Thanks in advance.
I solved this by putting a "wrapper" UIView around the UIViewController's view instead of the UIViewController itself. The wrapper view then pads the subview by setting the subview's frame in the layoutSubviews method.
I've attached the code I used for convenience. To use, replace your UINavigationController with the PaddedNavigationController, and set the PaddedNavigationController's insets property.
PaddedNavigationController.h:
#import <Foundation/Foundation.h>
#interface PaddedNavigationController : UINavigationController
{
UIEdgeInsets _insets;
}
#property (nonatomic, assign) UIEdgeInsets insets;
#end
PaddedNavigationController.m:
#import "PaddedNavigationController.h"
#interface PaddedView : UIView
{
UIView *_view;
UIEdgeInsets _insets;
}
#property (nonatomic, assign) UIEdgeInsets insets;
+ (PaddedView *) wrapView:(UIView *)view withInsets:(UIEdgeInsets)insets;
- (id) initWithView:(UIView *)view insets:(UIEdgeInsets)insets;
#end
#implementation PaddedNavigationController
#synthesize insets = _insets;
- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
//check if the UIViewController's view has already been wrapped by the PaddedView; don't want to wrap it twice
if(![viewController.view isKindOfClass:[PaddedView class]])
{
viewController.view = [PaddedView wrapView:viewController.view withInsets:self.insets];
}
[super pushViewController:viewController animated:animated];
}
- (void) setInsets:(UIEdgeInsets)insets
{
_insets = insets;
//loop through this navigation controller's view controllers and set the new insets on any PaddedViews
for(UIViewController *viewController in self.viewControllers)
{
if([viewController.view isKindOfClass:[PaddedView class]])
{
PaddedView *padded = (PaddedView *)viewController.view;
padded.insets = insets;
}
}
}
#end
#implementation PaddedView
#synthesize insets = _insets;
+ (PaddedView *) wrapView:(UIView *)view withInsets:(UIEdgeInsets)insets
{
return [[[PaddedView alloc] initWithView:view insets:insets] autorelease];
}
- (id) initWithView:(UIView *)view insets:(UIEdgeInsets)insets
{
if(self = [super initWithFrame:view.frame])
{
_insets = insets;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_view = [view retain];
[self addSubview:view];
}
return self;
}
- (void) dealloc
{
[_view release];
[super dealloc];
}
- (void) layoutSubviews
{
//apply the insets to the subview
_view.frame = CGRectMake(self.insets.left, self.insets.top, self.frame.size.width - self.insets.left - self.insets.right, self.frame.size.height - self.insets.top - self.insets.bottom);
}
- (void) setInsets:(UIEdgeInsets)insets
{
_insets = insets;
//we need to re-layout the subviews as the insets have changed
[self layoutSubviews];
}
#end

changing UILabel text on a subview from main view

Ok, so I'm a relative noob with Objective-C/iOS programming, so hopefully someone with more knowledge here can help me out.
I have an iPad application using the SplitViewController template (with Core Data). I created another UIViewController (with xib file) called PlayerViewController. This View has several UILabel components on it.
I have a list of players that show up in the RootViewController (UITableView) and when you select a player, I programmatically create a PlayerViewController (in DetailViewController), pass it the NSManagedObject that was passed to the DetailViewController, try to set the text of one of the labels on the PlayerViewController's view, and then add it as a subview to the DetailViewController.
All of this works great except for the setting the text of the label on the PlayerViewController's view. I'm not sure what I'm doing wrong. I have used NSLog to confirm that the NSManagedObject is not nil and that the NSManagedObject property I'm trying to use has the correct text.
I'm at a loss here. Any help would be greatly appreciated. (Code follows):
This method is in the DetailViewController.m file:
- (void)configureView {
// Update the user interface for the detail item.
PlayerViewController *player = [[PlayerViewController alloc] init];
player.player = detailItem;
[self.view addSubview:player.view];
}
This method is called when the user selects an item from the RootViewController (This functionality, calling of configureView, is setup by the template and I haven't changed it).
Setting the player property of the PlayerViewController to object detailItem is handled in the setPlayer method of that class.
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
I then have a configureView method as well in PlayerViewController that sets the text of the label:
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
Ok, so the first NSLog statement prints the desired value, but the text of the UILabel (called nickName) returns nil.
The following is the full PlayerViewController.h & .m files:
PlayerViewController.h:
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface PlayerViewController : UIViewController {
NSManagedObject *player;
IBOutlet UILabel *nickName;
IBOutlet UILabel *goalCount;
IBOutlet UILabel *assistCount;
IBOutlet UILabel *timeInGame;
}
#property (nonatomic, retain) IBOutlet UILabel *nickName;
#property (nonatomic, retain) IBOutlet UILabel *goalCount;
#property (nonatomic, retain) IBOutlet UILabel *assistCount;
#property (nonatomic, retain) IBOutlet UILabel *timeInGame;
#property (nonatomic, retain) NSManagedObject *player;
#end
PlayerViewController.m:
#import "PlayerViewController.h"
#implementation PlayerViewController
#synthesize nickName, goalCount, assistCount, timeInGame, player;
#pragma mark -
#pragma mark Managing the detail item
/*
When setting the player item, update the view
*/
- (void)setPlayer:(NSManagedObject *)managedObject {
if (player != managedObject) {
[player release];
player = [managedObject retain];
// Update the view.
[self configureView];
}
}
- (void)configureView {
nickName.text = [[player valueForKey:#"Nickname"] description];
NSLog(#"Nickname %#", [[player valueForKey:#"Nickname"] description]);
NSLog(#"Nickname %#", nickName.text);
}
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
I'm sure I'm just missing something trivial, but I can't figure it out, and haven't been able to find any answers searching the web.
Thanks for any help!
Ok, so after playing with this for a bit and searching and searching around, I have gotten the answer to my problem. It turns out all the code I had was fine except the location of one statement. My call to configureView in PlayerViewController.m needed to be in viewDidLoad() not in the setPlayer() method. It all works great now.
Change the configureView method to that :
- (void)configureView {
nickName.text = (NSString*)[player valueForKey:#"Nickname"];
}
Yes, better place to call method is
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self configureView];
}
(void)setPlayer:(NSManagedObject *)managedObject called before your nib files loaded.