I can't figure out what the problem is here. I have a very simple UIViewController with a very simple viewDidLoad method:
-(void)viewDidLoad {
NSLog(#"making game view");
GameView *v = [[GameView alloc] initWithFrame:CGRectMake(0,0,320,460)];
[self.view addSubview:v];
[super viewDidLoad];
}
And my GameView is initialized as follows:
#interface GameView : UIView {
and it simply has a new drawRect method:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
NSLog(#"drawing");
}
In my console, I see "making game view" being printed, but "drawing" never is printed. Why? Why isn't my drawRect method in my custom UIView being called. I'm literally just trying to draw a circle on the screen.
Have you tried specifying the frame in the initialization of the view? Because you are creating a custom UIView, you need to specify the frame for the view before the drawing method is called.
Try changing your viewDidLoad to the following:
NSLog(#"making game view");
GameView *v = [[GameView alloc] initWithFrame:CGRectMake(0,0,320,460)];
if (v == nil)
NSLog(#"was not allocated and/or initialized");
[self.view addSubview:v];
if (v.superview == nil)
NSLog(#"was not added to view");
[super viewDidLoad];
let me know what you get.
Check if your view is being displayed. If a view is not currently on screen, drawRect will not be getting called even if you add the view to its superview. A possibility is that your view is blocked by the some other view.
And as far as I know, you don't need to write [super drawRect];
Note that even if viewDidLoad is called on a view controller it doesn't necessarily indicate the view controller's view is displayed on screen. Example: Assume a view controller A has an ivar where a view controller B is stored and view controller A's view is currently displayed. Also assume B is alloced and inited. Now if some method in A causes B's view to be accessed viewDidLoad in B will be called as a result regardless whether it's displayed.
If you're using a CocoaTouch lib or file you may need to override the initWithCoder method instead of viewDidLoad.
Objective-C:
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
//Do Stuff Here
}
return self;
}
I had a view that was offscreen, that I would load onscreen and then redraw. However, while cleaning up auto-layout constraints in XCode, it decided my offscreen view should have a frame (0,0,0,0) (x,y,w,h). And with a (0,0) size, the view would never load.
Make sure your NSView has a nonzero frame size, or else drawRect will not be called.
Related
my didAddSubview is not being called.
This is what I am doing.
In my viewController
- (void)viewDidLoad {
[super viewDidLoad];
SomeView *view = [[SomeView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame))];
[self.view addSubview:view];
}
Where my SomeView is a UI view (.h file)
#import <UIKit/UIKit.h>
#interface DyteMeetingView : UIView
#end
in which I have didAddSubview like this
- (void)didAddSubview:(UIView *)subview{
[super didAddSubview:subview];
[self someFunc];
}
I placed debugger in didAddSubview but the debugger never reaches here and hence [self someFunc]; is also not called.
Since didAddSubview is a lifecycle method for UI view, I am also not calling it from anywhere.
Can someone help me in figure out the concept I missed out on/what I could be doing wrong?
PS: I don't have any xib file for UIView.
PS: Intentionally added swift tag as well since there isn't large chunk of code and I am confident that swift developers would be able to understand the question/code and might be able to help me out.
didAddSubview in SomeView will be called when a sub view is added to an instance of SomeView, but that isn't what you are doing here. You are adding a SomeView as a subview of another view.
You want didMoveToSuperview; in this method you can use the superview property of self to identify the view that it moved to.
The didAddSubview will be called in viewController.view, instead of your SomeView, because you using viewController.view.addSubview(subview)
When you create a UINavigationController, you can reveal its default hidden UIToolbar via setToolbarHidden:animated: (or by checking Shows Toolbar in Interface Builder). This causes a toolbar to appear at the bottom of the screen, and this toolbar persists between pushing and popping of view controllers on the navigation stack. That is exactly what I need, except I need the toolbar to be located at the top of the screen. It appears that's exactly what Apple has done with the iTunes app:
How can one move UINavigationController's toolbar to the top to lie underneath the navigation bar instead of at the bottom?
I've tried to implement the UIToolbarDelegate, override positionForBar:, and return UIBarPosition.TopAttached or UIBarPosition.Top after setting the delegate of self.navigationController?.toolbar to self, but this did not even call the delegate method therefore it didn't change the bar position.
Note that I need the toolbar to be preserved between navigation, so I can't simply add a toolbar to a view controller and position it under the nav bar.
The solution for this problem is a two (and a half) step process:
First you have to add an observer to the toolbars 'center' member.
Second, inside your observeValueForKeyPath:ofObject:change:context:, relocate the toolbar to your target position every time it is moved by somebody (e.g. the navigation controller itself for example, when the device rotates).
I did this in my UINavigationController subclass.
To avoid recursion, I've installed an local flag member 'inToolbarFrameChange'.
The last (half) step was a bit tricky to find out... you've to access the toolbars 'frame' member, to get the observer to be called at all... I guess, the reason for this might be, that 'frame' is implemented as an method inside UIToolbar and the base 'frame' value in UIView is only updated when the UIToolbar method is called ?!?
I did implement this 'frame' access in my overloaded setToolbarHidden:animated: method, which does nothing but to forward the call and to access the toolbars 'frame' value.
#interface MMMasterNavigationController ()
#property (assign, nonatomic) BOOL inToolbarFrameChange;
#end
#implementation MMMasterNavigationController
/*
awakeFromNib
*/
- (void)awakeFromNib {
[super awakeFromNib];
// ... other inits
self.inToolbarFrameChange = NO;
}
/*
viewDidLoad
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 'center' instead of 'frame' from: http://stackoverflow.com/a/17977278/2778898
[self.toolbar addObserver:self
forKeyPath:#"center"
options:NSKeyValueObservingOptionNew
context:0];
}
/*
observeValueForKeyPath:ofObject:change:context:
*/
- (void)observeValueForKeyPath:(NSString *)pKeyPath
ofObject:(id)pObject
change:(NSDictionary<NSString *,id> *)pChange
context:(void *)pContext {
if ([pKeyPath isEqualToString:#"center"]) {
if (!self.inToolbarFrameChange) {
//NSLog(#"%s (0): %#", __PRETTY_FUNCTION__, pChange);
self.inToolbarFrameChange = YES;
CGRect tbFrame = self.toolbar.frame;
// maybe some other values are needed here for you
tbFrame = CGRectMake(0, 0, CGRectGetWidth(tbFrame), CGRectGetHeight(tbFrame));
self.toolbar.frame = tbFrame;
self.inToolbarFrameChange = NO;
}
} else {
[super observeValueForKeyPath:pKeyPath ofObject:pObject change:pChange context:pContext];
}
}
/*
setToolbarHidden:animated:
*/
- (void)setToolbarHidden:(BOOL)pHidden
animated:(BOOL)pAnimated {
FLog;
[super setToolbarHidden:pHidden animated:NO];
// Access the 'frame' member to let to observer fire
CGRect rectTB = self.toolbar.frame;
rectTB = CGRectZero;
}
You may create not UITableViewController but UIViewController. In view of UIViewController place UIToolBar below NavigationBar and UITableView. Delegate all necessary list of UITableView to UIViewController and thats all.
Why should you use UIViewController instead UITableViewController? Because tableView will not have statical positions elements. You should have something that not contains ScrollView. In this situation it is only UIView. Also you may do some hack with UIScrollView of tableView but I think described method is easer.
I have custom UIViewController class called MSPageViewController with and associated nib file. I have an IBOutlet which is a UIImageView called pageImage.
Now, I want to use this view controller in another UIViewController which will display a series of my custom MSPageViewController in a UIPageViewController. So, I use the following code:
// alloc and init my custom view controller
MSPageViewController *page1 = [[MSPageViewController alloc] initWithNibName:#"MSPageViewController" bundle:nil];
// I must call this or, the image that I set below will always be null
// why? I guess it's because the view hasn't been drawn yet because it hasn't been displayed, so I need to force the redraw - but this is my question. Is this is the right approach?
[page1.view setNeedsDisplay];
// set the image
page1.pageImage.image = [UIImage imageNamed:#"tutorialPage1.png"];
// make my array of view controllers, it expects an array because could be double-sided
NSArray *viewController = [NSArray page1];
// pass the array that contains my custom view controller
[self.pageController setViewControllers:viewController direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
So am I doing this right? I have to force the redraw so that my outlets exist when I try to assign to them?
It's not the setNeedsDisplay part that you "need", it's the self.view part. By accessing the view property you are forcing the view controller to actually load the NIB. I guess that as a side effect of this, the pageImage property is populated as well (and was nil before you called self.view).
So, just calling self.view; instead of [self.view setNeedsDisplay]; should be enough.
As others have noted, pageImage (a UIImageView?) is likely not loaded from the nib yet when you're accessing it.
If you have a custom getter for pageImage, you could do the following:
- (UIImageView*) pageImage
{
[self view];
return _pageImage; // assuming the property backing ivar is synthesized as _pageImage.
}
But my personal preference would be to not expose the imageview itself and just expose a property for image. Then you can set the image to the viewcontroller regardless of it's loaded state, and internally set it to the imageView once the view loads:
- (void) setImage: (UIImage*) image
{
_image = image;
if ( self.isViewLoaded )
{
self.pageImage.image = image;
}
}
- (void) viewDidLoad
{
[super viewDidLoad];
self.pageImage.image = self.image;
}
If you moved some code to the viewDidLoad method you would be guaranteed that the view had been drawn.
I am designing a custom view controller in interface builder with a XIB file and I have a custom UIView subclass that I want to add to my view controller. Here is how I've put my custom view into the view controller:
My TonerEffectButtonView class is a subclass of UIView (and has nothing to do with UIButton) and here is my code for it:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)prepareWithSelector:(SEL)selector onTarget:(id)targ withFilter:(GPUImageFilter*)filter{
self.gpuImageView = [[GPUImageView alloc] initWithFrame:self.frame];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:targ action:selector];
[self addGestureRecognizer:gesture];
selectedFilter = filter;
[filter addTarget:self.gpuImageView];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
}
initWithFrame: is not called anyway as I'm creating the view through a XIB, and I don't need any customization in initWithCoder: so I haven't implemented it. I have a GPUImageView (that is a part of a library that I'm using) that I'm creating programatically inside my view, and adding camera input to it (if you ask what happens in the prepare method). Prepare method is called from outside, and I've verified my UIView's frame is correct when it is called. I have a breakpoint on [super drawRect:rect]; and it never gets called, and when I run the program, my custom view is not displayed, nor does receive touch events (but prepare method DOES get called so the instance is created with the correct frame). It's like it has never been put there in interface builder. What could be the cause of this? I've seen many posts about this, and they mostly refer to creating views programatically and indicating problems with 'initWithFrame:'. But I have nothing to do with that method, and I want to use the interface builder/XIB couple, please don't advice me to create the view controller programatically. What could be the cause of it?
Thanks,
Can.
UPDATE: I can verify that -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx is also not called in any way.
Ok, I've found the answer. First, I've tried to add another instance programatically, and I've found out that it has autoresizing mask set to none, whereas my original instance has it set to W+H. This clue made me realize that in fact, I was forgetting to add my GPUImageView instance as a subview into my custom view. I've added it, and it worked.
I have a UINavigationController/UITableView and I want to present a UIView over top of it when the table is empty to give the user a prompt on how to add items to the table.
I've never make a UIView (as opposed to a UIViewController before) so I'll step through what I did to make it:
Make a new UIView Class - MakeSentenceHelperView
Make a nib called MakeSentenceHelperView.xib
Set File's owner to MakeSentenceHelperView
Load the nib in the MakeSentenceHelperView:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSLog(#"makesentencehelperview init");
[[NSBundle mainBundle] loadNibNamed:#"MakeSentenceHelperView" owner:self options:nil];
}
return self;
}
and present the MakeSentenceHelperView in the UITableViewController:
//present the placeholder view for sentences
MakeSentenceHelperView *makeSentenceHelperView = [[MakeSentenceHelperView alloc] init];
NSLog(#"present placeholder: self.navigationcontroller.view: %#", self.navigationController.view);
//Something like this:
[self.navigationController.view addSubview:makeSentenceHelperView];
[self.navigationController.view bringSubviewToFront:makeSentenceHelperView];
The class loads and logs ok, but nothing appears in front of the UITableView - where have I gone wrong?
UPDATE: if I add [self.tableView setHidden:YES]; then the tableview disappears and the space is black and empty. I'm assuming this means I'm setting up the View wrong, somewhere.
You can use https://github.com/ecstasy2/toast-notifications-ios/ for showing Toast view liek Android. Check array size and if table view is not showing then called this one and show any custom method.
Thanks to #Aadhira for their link which led me to the problem.
I needed to add awakeFromNib
I was missing [self addSubview:self.view]; at the end of initWithFrame.