How to draw a NSView on a NSView? - cocoa

I am passed a NSView from a class and I need to add on another NSView at a certain point. How do I do that?
Thanks in advance.

You can add a view to another view by sending the addSubview message, like this:
[MyView addSubview:MyOtherView];
Don't forget though, you are responsible for how that view displays. Make sure you set its bounds.

You can position the new view when instantiating it using the initWithFrame: method that'll create the view and position it within the superview (i.e. the one you'll add your view to with the already mentioned addSubview: message).
PS: The View Programming Guide - is your friend.. ;-)

Related

Auto-layout and transforms not working together for UIViewController transition

I have a UIViewController being presented with a UIViewControllerAnimatedTransitioning object to do the animation. The animation has a collection view cell expand and fade out as the new view controller's view fades in, expanding in the same way. So it looks like you have an expanding cell that transforms into the new view controller. But weirdly enough, the subviews of the cell get transformed in weird ways when they use auto-layout. When I don't use auto-layout, this doesn't happen. What might be the issue?
Try adding your cell to [[transitionContext containerView] superview] in - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext, instead of [transitionContext containerView]
For some reason this worked for me

Make compiler aware of NSView subclass methods when building ViewController's view programmatically

I have created a subclass of NSView that allows me to easily change the background color via the method - (void)setBackgroundColor:(NSColor *)aBackgroundColor.
I want my view controller's main view to be this subclass, so I initiate it with self.view = [[BetterNSView alloc] initWithFrame....
On the next line, I try to set its background color: [self.view setBackgroundColor:[NSColor greenColor]]. But the compiler complains, saying that NSView doesn't have the method setBackgroundColor.
To solve this, I need to use typecasting: [(BetterNSView *)self.view setBackgroundColor:[NSColor greenColor]];. That works. But I'd like to not need the typecasting.
How do I tell the compiler that the view property of the view controller is the BetterNSView subclass? Remember, I'm not using IB.
You have two reasonable choices.
Make an accessor on your view controller with the right type that just passes through to the view property.
Deal with the typecast.
Really, this is one of this situations where typecasting is okay.

Drawing with drawRect method - Cocoa

I've been looking for solutions but can't figure out if it is possible to do the following:
I have a drawRect method, what i want to do is to add a graphic element (such as rects and lines) to the current view without refreshing it. I used to call setNeedsDisplay but this method is actually deleting myView an re-drawing it from 0.. Any suggestion to keep the old one and add new content ?
Thank
Every time an update is made in a view the entirety of it is redrawn; so -drawRect needs to redraw the entire view. You have to 'refresh' your view - it's the norm - there's nothing wrong with it. Just draw the old content again.
Or, you could call setNeedsDisplayInRect: if you do want to just redraw a specific section of your view.
That's what drawRect: does - it redraws the rect in the view.
If you want lines and rects drawn on top, try doing it on a different layer.
I guess it's better if you work with layers.
You can make CALayer subclasses for your shapes, and then save them in an array.
By default , the drawRect method will clear the whole content , if your want to dynamic draw some new graphics contents to the view ,you should abstract these graphics element's data structure , for example , you add a line ,this line will have
a start point
a end point
line color
line width
is has a shadow
a line join
so you can put all these property into a struct and define a new Data Class named LineStruct and define a method called
-(void)drawLine:(CGContextRef)ctx withLineStruct:(LineStruct*)lineStruct
to your custom UIView , define a
#property (nonatomic) LineStruct *lineStruct ;
and invoke in it's drawRect method
-(void)drawRect:(CGContextRef)ctx{
CGContextRef ctx = UIGraphicsGetCurrentContext() ;
[self drawLine:ctx withLineStruct:self.lineStruct];
}
so if your have other graphic contents , you can do the draw like that . If you have lots of
contents , you must add a buffer to your UIView ,such as add a NSArray , and in the drawRect method ,you add a for(;;)to draw all the graphics elements
I think maybe you need something like an NSBezierPath to store all your shapes and add new shapes. It's easy to add new shapes to an existing NSBezierPath, see the documentation: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSBezierPath_Class/Reference/Reference.html
Then in your drawrect, you will only have to stroke or fill the NSBezierPath.
This will only work if all your shapes have the same fill color and stroke. Otherwise you could keep some kind of list of multiple NSBezierPaths and stroke/fill them in different ways.
You can 'get around' this by making the view you want to draw in separate from your view with the original iboutlets. Then make your main view background transparent (but do not make the iboutlets transparent). So for this example, I will prevent that the IBOutlets you want to keep (not draw over) are a UITextField, a UILabel and a UIButton.
So you Interface Builder will look like this:
UIVIewController
UIView2 (view with drawRect defined)
UIView (main)
UITextField
UILabel
UIButton
So as you see, when you call 'drawRect' it will still blank out your UIView2 completely, but it won't matter because 'drawRect' won't delete any of the UILabel, UIButton, UITextField or whatever else you want to keep in your UIView1. Hope this helps.

NSView added as subview doesn't show

I have a puzzling problem. Working on a cocoa app in mac os x 10.7.
My app main window contains a split view. In a certain use context in one of the subviews of the split view is loaded a custom view with some labels (nstextfield) and a split view (instantiating a view controller that loads a nib and getting view from that controller). Frame of the custom view is set to split view subview bounds and everything works fine.
Problem is that one of the subviews of the second split view should be loaded (same method: view controller-nib-view-frame/bounds) with a custom view containing a table view and a button, but in this case nothing shows. Everything is done the same way but last custom view is not visible. Any idea?
Thanks
(edit)
this is the code I use to instantiate controller for the view to be added, get the view, and add it as subview to a subview of the split view
- (void)loadSubview {
self.subviewToAddController = [[viewController alloc] initWithNibName:nil bundle:nil];
//nib name is coded in the controller class definition
[[self.subviewToAddController view] setFrame:[self.splitViewContainerSubView bounds]];
//container subView is an outlet
[self.splitViewContainerSubView addSubview:[self.subviewToAddController view]];
}
However I don't think the problem is in this code because if I ask the container subview for its own subviews I can see the new subview is present in the list. It just doesn't show. If I add it as a subview of the split view (a test a just made) or as subview of the subview of the most external split view it is correctly showed too (sorry for the confused explanation, I would need a diagram but in this moment I can't make it)
To elaborate more my doubt (I didn't want to misled so I didn't mention before) can't it be a problem of coordinates, so view is correctly loaded and added as subview but is not visible because hidden by something or showed out of visible area?
(update)
Sorry it took so long to post an update.
After more testing I found out the problem is related to autolayout. No idea what the exact problem is and how to solve it. I ended up turning it off for the nibs the were in troubles and use the old way to set interface objects position and size/resize. Not the best way but for now I can go on.
My best guess is that you didn't set the autoresizing masks of the view properly.

Cocoa: NSView drawRect painting over IBOutlets

I have an NSView in IB which sits above the app window. I have a subclass of NSView (AddSource) which I assign to the NSView.
On awakeFromNib I instantiate the view:
//add a new Add Source class
addSourceView = [[AddSource alloc] initWithFrame:NSMakeRect(0.0, 959.0, 307.0, 118.0)];
[[winMain contentView] addSubview:addSourceView];
in addSourceView's drawRect method I am adding a white background to the view:
[[NSColor whiteColor] set];
NSRectFill(rect);
[self setNeedsDisplay:YES];//added this to see if it might solve the problem
In winMain's contentView I have a NSButton that when clicked slides the addSourceView onto the window:
NSRect addSourceViewFrame = [addSourceView frame];
addSourceViewFrame.origin.y = 841.0;
[[addSourceView animator] setFrame:addSourceViewFrame];
But it seems as if the app is painting over the IBOutlets I placed on the NSView in IB. If, in IB, I repoistion the NSView so that it is on screen when the app launches everything works fine, the IBOutlets are there as well as the background color.
I'm not sure why this is happening. I've done this before with no problems. I must be doing something different this time.
Thanks for any help.
*note - on the 3rd screen capture, when I say this is what the app looks like when opened, that's when I hard code the Y position of the NSView. When it is functioning correctly it should open as screen capture 1.
Most likely your buttons and custom view are siblings, i.e. they are both subviews of your window's content view. Since siblings are "Stacked" depending on the order in which they are added, when you add the view in code it is being added on top of the buttons. You should be able to fix it by explicitly specifying where the view should be positioned relative to its new siblings like so:
[[winMain contentView] addSubview:addSourceView positioned:NSWindowBelow relativeTo:nil];
which should place it below any existing subviews of your window's content view. Also, remove the setNeedsDisplay: line in drawRect, that leads to unncessary, possibly infinite, redrawing.
EDIT: OK I see what you're doing.
I would suggest creating a standalove view in the NIB by dragging a "Custom View" object into the left hand side (the vertically-aligned archived objects section) and adding your controls there, that should ensure the controls are actualy subviews of the view, then you can just create a reference to the archived view in code, and add/remove it dynamically as needed.
Honestly though, you should probably be using a sheet for these kinds of modal dialogs. Why reinvent the wheel, and make your app uglier in the process?
You added TWO AddSource views to the window. You added one in IB - this view contains your textFields and buttons that are connected to the IBOutlets and it is positioned outside the window.
Then in -awakeFromNib you create another, blank AddSource view (containing nothing) and animate it into the window.
I can't recommend highly enough the Hillegass as the best introduction to IB and the correct way to build Cocoa Apps.
Also, Assertions can be useful to make sure what you think is happening is actually what is happening.
If you are certain you added a button to your view in IB, assert it is so:-
- (void)awakeFromNib {
NSAssert( myButton, #"did i hook up the outlet?");
}
NSAssert is a macro that has zero overhead in a release build.
Calling [self setNeedsDisplay:YES] from -drawRect just causes the same -drawRect to be called again. This will give you big problems.

Resources