Why myView is dealloc after init?
MainViewController:
[MOBubbleView hudWithBody:#"123123" bubblePoint:CGPointMake(220, headerMenu.center.y) hidesAfter:2 show:YES];
MOBubbleView.h:
#interface MOBubbleView : UIViewController
#property (nonatomic, assign) float hudHideDelay;
#property (nonatomic, strong) UIColor *itemColor;
+ (MOBubbleView*)hudWithBody:(NSString*)body bubblePoint:(CGPoint)rect hidesAfter:(float)delay show:(BOOL)show;
#end
MOBubbleView.m:
+ (MOBubbleView*)hudWithBody:(NSString*)body bubblePoint:(CGPoint)point hidesAfter:(float)delay show:(BOOL)show {
MOBubbleView *bubble = [[MOBubbleView alloc] init];
///....
if (show) [bubble addToWindow];
return bubble;
}
- (void)addToWindow {
[[[[UIApplication sharedApplication] delegate] window] addSubview:self.view];
}
- (void)loadView {
CGRect bounds = [[UIScreen mainScreen] bounds];
self.view = [[UIView alloc] initWithFrame:bounds];
[self.view setBackgroundColor:[UIColor clearColor]];
/// .. my animation
}
- (void) dealloc {
NSLog(#"Close myView");
}
#end
You would need to retain the view controller that you are returning from your call, so do:
pMyViewController = [myView hudWithBody:#"123123" bubblePoint:CGPointMake(220, headerMenu.center.y) hidesAfter:2 show:YES];
where pMyViewController is declared somewhere that it won't go out of scope - say a global variable for now:
e.g. myView* pMyViewController;
You have added the view part of the view controller onto the window, so that's retained, but the actual controller part has no references holding on to it, so it gets deallocated.
Related
I have a view like this:
#interface MyView ()
#property (nonatomic, strong) UIView* subView;
#end
#implementation MyView
#pragma mark - init && dealloc
-(instancetype)init
{
self = [super init];
if (self){
[self setupSubView];
[self setupAutoLayoutSubView];
}
return self;
}
#pragma mark - setup
-(void)setupSubView
{
_subView = [UIView newAutoLayoutView];
[self addSubview:_subView];
}
-(void)setupAutoLayoutSubView
{
// autolayout: to centralize the subView
// if you don't use PureLayout, you can use the traditional way
[_subView autoCenterInSuperview];
}
#end
When I add this view into Window, the reference counting on this view will be increased to 3.
MyView* v = [[MyView alloc] init];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 1
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubView:v];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 3 INCORRECT
The strange thing is if I comment the "autolayout" part from method: "setupAutoLayoutSubView", the reference counting is correct ( = 2 )
or if I add my view to a normal view, the reference counting is correct as well
MyView* v = [[MyView alloc] init];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 1
UIView* normalView = [[UIView alloc] init];
[normalView addSubView:v];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 2 : CORRECT
I'm using a contact picker to grab a string, then pass that string to another view controller, however the UILabel is not updating with the data (or any other string).
In the SlingViewController.m logs below, _taggedFriendsNames is being passed successfully.
Perhaps the issue is because the receiving view controller is trying to update the label on another (SlingshotView) view? I don't think that's the case as I've been updating labels in this way in other methods.
The answer is likely related to updating UILabels in general, but I've had no luck after searching.
Things I've checked with no success:
Updating from the main thread asynchronously
#synthesize the label in SlingshotView
calling setDisplay
Included potentially relevant code below. Thanks in advance for any tips!
SlingViewController.m
-(void)updateFriendsPickedLabel:(id)sender {
NSLog(#"updateFriendsPickedLabel: %#", _taggedFriendsNames); // i see this
slingshotView.friendsPickedLabel.text = #"any string"; // i don't see this
}
SlingViewController.h
#class TBMultiselectController;
#class SlingshotView;
#interface SlingViewController : UIViewController
#property (nonatomic, readonly) SlingshotView *slingshotView;
#property(nonatomic) NSString *taggedFriendsNames;
//for friend picker
-(void)updateFriendsPickedLabel:(id)sender;
#end
MultiSelectViewController.m
- (IBAction) sendButton: (id) sender {
NSMutableString *myString = [[NSMutableString alloc]initWithString:#""];
for (int i=0; i < self.selectedContacts.count; i++) {
Contact *myContact = self.selectedContacts[i];
[myString appendString:[NSString stringWithFormat:#"%# %# ", myContact.firstName, myContact.lastName]];
}
SlingViewController *svc = [[SlingViewController alloc] init];
svc.taggedFriendsNames = myString;
[svc updateFriendsPickedLabel:self];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
MultiSelectViewController.h
#protocol TBMultiselectControllerDelegate;
#class SlingViewController;
#interface TBMultiselectController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, TBContactsGrabberDelegate>
#property (nonatomic, assign) id<TBMultiselectControllerDelegate> delegate;
- (IBAction)sendButton: (id) sender;
#end
#protocol TBMultiselectControllerDelegate <NSObject>
-(void)updateFriendsPickedLabel:(id)sender;
#end
SlingshotView.h
#property (strong, nonatomic) UILabel *friendsPickedLabel;
SlingshotView.m
#synthesize friendsPickedLabel;
...
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGRect imageFrame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
contentView = [[UIView alloc] initWithFrame:frame];
[contentView setBackgroundColor:[UIColor whiteColor]];
[contentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[self addSubview:contentView];
self.friendsPickedLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, screenRect.size.height/2-100, screenRect.size.width-20, 200)];
self.friendsPickedLabel.shadowColor = [UIColor darkGrayColor];
self.friendsPickedLabel.numberOfLines = 0;
self.friendsPickedLabel.shadowOffset = CGSizeMake(0.5, 0.5);
self.friendsPickedLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.0];
[self.friendsPickedLabel setTextAlignment:NSTextAlignmentLeft];
self.friendsPickedLabel.textColor = [UIColor whiteColor];
self.friendsPickedLabel.font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:24];
[contentView addSubview:self.friendsPickedLabel];
You are reallocating this..
SlingViewController *svc = [[SlingViewController alloc] init];
svc.taggedFriendsNames = myString;
[svc updateFriendsPickedLabel:self];
Meaning your
slingshotView.friendsPickedLabel becomes nil..
And you are calling/using the delegate the wrong way, i think it suppose to be
[self.delegate updateFriendsPickedLabel:#"YourData To be Passed"];
From your code you are using the -(void)updateFriendsPickedLabel:(id)sender; inside SlingViewController and not the delegate, you are not implementing the delegate either..
Yes the -(void)updateFriendsPickedLabel:(id)sender; method is called, bacause you are calling it directly from the class..
SlingViewController.h
#interface SlingViewController : UIViewController < TBMultiselectControllerDelegate > // for delegate implementation
#property (nonatomic, readonly) SlingshotView *slingshotView;
#property(nonatomic) NSString *taggedFriendsNames;
//for friend picker
//-(void)updateFriendsPickedLabel:(id)sender;
#end
MultiSelectViewController.m
- (IBAction) sendButton: (id) sender {
NSMutableString *myString = [[NSMutableString alloc]initWithString:#""];
for (int i=0; i < self.selectedContacts.count; i++) {
Contact *myContact = self.selectedContacts[i];
[myString appendString:[NSString stringWithFormat:#"%# %# ", myContact.firstName, myContact.lastName]];
}
/*
SlingViewController *svc = [[SlingViewController alloc] init];
svc.taggedFriendsNames = myString;
[svc updateFriendsPickedLabel:self];
*/
[self.delegate updateFriendsPickedLabel:#"YourString"];
// this will call the method in your implementation class
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
Hmm.. I Think you have implemented the delegates the wrong way.
This is suppose to be a comment but its too long..
I'm adding a subview the following way"
SettingsViewController *SVC = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:[NSBundle mainBundle]];
[self.parentViewController addChildViewController:SVC];
[self.view addSubview:SVC.view];
My subview is 300 x 360 and is centered on the screen. The problem occurs when I attempt to remove it (Getting released).
When I press the close button in this new view, I get exc bad access. What's the correct way to add my view? I have a SettingsViewController.xib linked up to a .h and .m file.
Here's what my close button in my settings view looks like:
-(IBAction)close:(id)sender {
[self.view removeFromSuperview];
[self removeFromParentViewController];
}
But even when I comment out everything inside, just triggering the button crashes the app.
This is what my .h file looks like:
#import <UIKit/UIKit.h>
#interface SettingsViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIView *alertView;
#property (nonatomic, strong) IBOutlet UIButton *closeButton;
#property (nonatomic, strong) IBOutlet UILabel *musicLabel;
#property (nonatomic, strong) IBOutlet UILabel *soundFXLabel;
#property (nonatomic, strong) IBOutlet UILabel *vibrateLabel;
- (IBAction)close:(id)sender;
#end
and my implementation file:
#import "SettingsViewController.h"
#interface SettingsViewController ()
#end
#implementation SettingsViewController
#synthesize alertView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.alpha = 0;
self.alertView.layer.cornerRadius = 10.0f;
self.alertView.layer.borderColor = [UIColor whiteColor].CGColor;
self.alertView.layer.borderWidth = 4.0f;
self.closeButton.layer.cornerRadius = 5.0f;
self.closeButton.titleLabel.font = [UIFont fontWithName:#"SourceSansPro-Bold" size:20];
self.musicLabel.font = [UIFont fontWithName:#"SourceSansPro-Bold" size:30];
self.soundFXLabel.font = [UIFont fontWithName:#"SourceSansPro-Bold" size:30];
self.vibrateLabel.font = [UIFont fontWithName:#"SourceSansPro-Bold" size:30];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.view.alpha = 1;
}
completion:^(BOOL finished){
//Display Players View
}];
}
- (IBAction)close:(id)sender {
//[self.view removeFromSuperview];
//[self removeFromParentViewController];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
And last but not least, here's a snapshot of my storyboard / xib order:
create a property for your SettingsViewController where you add & assign it where you create it.
// in .h file or private category on top
#property (nonatomic, strong) SettingsViewController *settingsController;
// your usual code + assigning controller
SettingsViewController *SVC = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:[NSBundle mainBundle]];
[self.parentViewController addChildViewController:SVC];
[self.view addSubview:SVC.view];
self.settingsController = SVC;
Side Note : the reason for this behavior is after the .view property of the ViewController is added to your view, the controller gets deallocated (only the .view is alive now). So when you try to hit the butons on view there is no SettingsViewController to callback those events, hence crash. When you create the property & assign it, the controller lives.
I created a UISegmentedControl programmatically in the navigation bar of a UIViewController and I want to be able to switch view controllers when i toggle the segmented control.
This is what I have so far:
#interface TVExploreViewController : TVViewController
#property (nonatomic, strong) UISegmentedControl *scopeControl;
#property (nonatomic, assign) NSInteger scope;
#property (nonatomic, strong) UIViewController *premiumContentViewController;
#property (nonatomic, strong) UIViewController *trendingContentViewController;
#property (nonatomic, strong) UIViewController *currentViewController;
#end
TVExploreViewController's implementation:
- (void)viewDidLoad {
[super viewDidLoad];
_scopeControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"TV & MOVIES", #"VIRAL CLIPS", nil]];
[_scopeControl setFrame:CGRectMake(80, 0, 200, 30)];
[_scopeControl addTarget:self
action:#selector(scopeChanged:)
forControlEvents:UIControlEventValueChanged];
[[self navigationItem] setTitleView:_scopeControl];
[_scopeControl setSelectedSegmentIndex:0];
_premiumContentViewController = [[TVPremiumContentViewController alloc] init];
_trendingContentViewController = [[TVTrendingFeedController alloc] init];
[self setScope:0];
}
- (void)scopeChanged:(id)sender {
[self setScope:self.scopeControl.selectedSegmentIndex];
}
- (void)transitionToViewController:(UIViewController *)controller
{
[self.currentViewController willMoveToParentViewController:nil];
if (controller) {
[self addChildViewController:controller];
}
controller.view.frame = self.view.bounds;
[self.view addSubview:controller.view];
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
[controller didMoveToParentViewController:self];
self.currentViewController = controller;
}
- (void)setScope:(NSInteger)scope {
if (scope != _scope) {
_scope = scope;
UIViewController *nextController = nil;
if (_scope == 0) {
nextController = self.premiumContentViewController;
} else if (_scope == 1) {
nextController = self.trendingContentViewController;
}
[self transitionToViewController:nextController];
}
}
When I toggle the segmented control in TVExploreViewController, the rest of the view stays white and no view controller is loaded. Anyone know what I'm doing wrong?
This is not an answer, but It is convenient to write some log code here.
Can you show me the log in the method ?
- (void)transitionToViewController:(UIViewController *)controller
{
[self.currentViewController willMoveToParentViewController:nil];
if (controller) {
[self addChildViewController:controller];
}
controller.view.frame = self.view.bounds;
[self.view addSubview:controller.view];
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
[controller didMoveToParentViewController:self];
self.currentViewController = controller;
// can you show me the log here ?
NSLog(#"%#, %#, %#", controller.view, controller.view.superview, self.view) ;
// you should see the subviews that you added.
NSLog(#"subviews:%#", [controller.view subviews]) ;
}
It is really not a good idea to add Viewcontrollers to ViewController,you should always have one ViewController on screen. You could do this using NavigationController with pushing with no animation.
I am following Apress Beginning IOS 6 for school where we were asked by the professor to implement a custom algorithm that switches between 3 views (blue, yellow, and green) with each press of the "Next View" button in the toolbar. My approach was to add the subviews at different indices and shuffle the views in the hierarchy in BIDSwitchViewController.m:
#import "BIDSwitchViewController.h"
#import "BIDYellowViewController.h"
#import "BIDBlueViewController.h"
#import "BIDGreenViewController.h"
NSInteger count;
#interface BIDSwitchViewController ()
#end
#implementation BIDSwitchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.yellowViewController = [[BIDYellowViewController alloc] initWithNibName:#"YellowView"
bundle:nil];
self.greenViewController = [[BIDGreenViewController alloc] initWithNibName:#"GreenView"
bundle:nil];
self.blueViewController = [[BIDBlueViewController alloc] initWithNibName:#"BlueView"
bundle:nil];
//the topmost view is the last one in the stack (2)
[self.view insertSubview:self.blueViewController.view atIndex:2];
[self.view insertSubview:self.yellowViewController.view atIndex:1];
[self.view insertSubview:self.greenViewController.view atIndex:0];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.blueViewController = [[BIDBlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:self.blueViewController.view atIndex:0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.;
self.blueViewController = nil;
self.yellowViewController = nil;
self.greenViewController = nil;
}
- (IBAction)switchViews:(id)sender
{
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:
UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];
switch (count)
{
case 0:
[self.view sendSubviewToBack:self.blueViewController.view];
[self.view bringSubviewToFront:self.yellowViewController.view];
[self.view setBackgroundColor:self.yellowViewController.view.backgroundColor];
break;
case 1:
[self.view sendSubviewToBack:self.yellowViewController.view];
[self.view bringSubviewToFront:self.greenViewController.view];
[self.view setBackgroundColor:self.greenViewController.view.backgroundColor];
break;
case 2:
[self.view sendSubviewToBack:self.greenViewController.view];
[self.view bringSubviewToFront:self.blueViewController.view];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
break;
}
if (++count >= 3)
{
count = 0;
}
[UIView commitAnimations];
}
#end
Here is the code in BIDAppDelegate.m where the root view controller is added as an instance of BIDSwitchViewController:
#implementation BIDAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.switchViewController = [[BIDSwitchViewController alloc]
initWithNibName:#"SwitchView" bundle:nil];
UIView *switchView = self.switchViewController.view;
CGRect switchViewFrame = switchView.frame;
switchViewFrame.origin.y += [UIApplication
sharedApplication].statusBarFrame.size.height;
switchView.frame = switchViewFrame;
//[self.window addSubview:switchView];
self.window.rootViewController = self.switchViewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
And the BIDSwitchViewController.h header file:
#import <UIKit/UIKit.h>
#class BIDSwitchViewController;
#interface BIDAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) BIDSwitchViewController *switchViewController;
#end
The app and logic all work as the views switch from blue -> yellow -> green and back to blue again. As shown in the first picture (BIDBlueViewController's BlueView.xib subview of the BIDSubViewController) each of the views overlap the toolbar slightly. I have double and triple checked all of my simulated metrics in IB with the help of one of my classmates:
Am I using poor practice in shuffling the topmost views through the view hierarchy array, instead of removing each view via the book's method of "removeFromParentViewController()," or is their another, hidden cause for the sub views not properly sitting inside / behind the parent view?
Your UIToolbar control's Z-Order on it's view is behind your blue view controller. Since you created it in IB, you can add an IBOutlet for it, and bring it's view to top of the parent view. You're loading the ViewControllers as subviews, so they are all subviews of self.view of your parent.
In your header definition:
IBOutlet *toolBar UIToolbar;
Bring to front in your implementation.
[self.view bringSubviewToFront:toolBar];
You can do this after you've added the blue controller subview or at the end of all your case statements.