Adding NSView to window not on top? - objective-c

I am adding a subview programmatically and adding it to the main windows context view to cover up the entire context view like so:
loadingView = [[LoadingView alloc] initWithFrame:[mainWindow.contentView frame]];
NSLog(#"%#", [mainWindow.contentView subviews]);
[mainWindow.contentView addSubview:loadingView];
NSLog(#"%#", [mainWindow.contentView subviews]);
[mainWindow makeFirstResponder:loadingView];
The NSLog's confirm that loadingView is being added last in the contentView subviews. I have also tried:
loadingView = [[LoadingView alloc] initWithFrame:[mainWindow.contentView frame]];
[mainWindow.contentView addSubview:loadingView positioned:NSWindowAbove relativeTo:nil];
[mainWindow makeFirstResponder:loadingView];
That didn't work either. For some reason the two tableviews (created in IB) at the bottom of the window are on top of the new view I've added. Here's a snapshot of the window, note that the red part is what should be on top with the progress bar and a few labels:
It's also worth noting that the view has it's alpha set to 0.9 which is why you can somewhat see behind it.
GW

If you place one view above another, the objects in the previous view will be visible in above view. What you need do is remove previous views from window and then add a new subview.
Try using:
//Create IBOutlet of your tableview in your .h file
IBOutlet NSTableView* yourTableView;
// Add this line where you are adding your subview to remove the tableview from superview.
[yourTableView removeFromSuperview];
// Then add your loading view as the subview
loadingView = [[LoadingView alloc] initWithFrame:[mainWindow.contentView frame]];
[mainWindow.contentView addSubview:loadingView];
Then whenever your want your tableView back use:
[window contentView]addSubview: yourTableView];

As long as you use nil here, you will not get predictable results.
[mainWindow.contentView addSubview:loadingView positioned:NSWindowAbove relativeTo:nil];
If you have not already done so, put all the other views inside a containing view.
Make the containing view the only view that is a direct child of the window content view.
Now add your subview with the above method and replace nil with a reference to the containing view.

In OS X, overlapping siblings have certain nuances when it comes to drawing. In your case, the loadingView and the two table views are siblings because they are all added as subviews of the window's content view and they overlap hence the nuances are coming into play.
From Apple's Documentation
For performance reasons, Cocoa does not enforce clipping among sibling
views or guarantee correct invalidation and drawing behavior when
sibling views overlap. If you want a view to be drawn in front of
another view, you should make the front view a subview (or descendant)
of the rear view.
I don't have the definitive solution for this but reading these should help improve your understanding for the long term.
http://www.cocoabuilder.com/archive/cocoa/327817-overlapping-sibling-views.html
Is there a proper way to handle overlapping NSView siblings?

Related

Preventing user to access a view in a NSView hierarchy

Maybe this is my strange understanding of the NSView hierarchy, but the question is:
I have a window with a SplitViewController which has the classic two child views.
When the user clic a button on the toolbar I add a 'work in progress' view as follows:
NSView* workingView;
// creating the view
...
[self.view addSubView:workingView];
where self is the SplitViewController object. My understanding (also from iOS programming) is that the split view controller has three views: left and right views and the workingView onto them(?)
The problem is that, even if workingView is covering the entire window, the two views are still accessible. In iOS developing we can set the userInteraction to NO so only the desired view is active. What about OSX?
Can I disable interaction with the split view controller views and let only the workingView be active?
Thanks
EDIT:
I followed #Wain hint and I created a new ViewController that contains my work in progress view.
I presented it using the presentViewController:animator technique. In this way I can hav more control about the positioning of the view.
In the custom animator I simply colored the view to see what is happening. Really the way the animator adds the new view controller seems nearly the same as adding a classic view onto the split view (like did before). In fact the result is the same: even if my working view covers two buttons belonging to the left side of the split view, they are clickable!
-(void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController* bottomVC = fromViewController;
NSViewController* topVC = viewController;
topVC.view.wantsLayer = YES;
topVC.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
topVC.view.alphaValue = 0.0f;
[bottomVC.view addSubview:topVC.view];
topVC.view.layer.backgroundColor = [[NSColor blueColor] CGColor];
topVC.view.frame = CGRectMake(10, 10, 100, 100);
[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context){
context.duration = 1.0f;
topVC.view.animator.alphaValue = 1.0f;
} completionHandler:nil];
}
I know something is missing or wrong... May you help me in understanding what is going on here?
You should create a new view controller to manage your new view and then present it as a form sheet. The display style is slightly different on each platform but this is the correct approach.
An alternative is the screenshot the split view and pass the image to the new controller which is presented full size. It then applies an overlay and adds its own content view in front of the background image.

How to add multiple Views to UIScrollView

I have a ScrollView (contentScrollView) which should later contain 20 pages with a UIViewController for each page.
Now this is what I get when adding the UIViewControllers view to the contentScrollView (the Button in the upper right corner is from the MainViewController so it doesn't affect the problem)
and I got this code in my MainViewController class where my contentScrollView is in:
Slide1ViewController*test = [[Slide1ViewController alloc] init];
test.view.frame = CGRectMake(0, 0, 1024, 768);
[self.contentScrollView addSubview:[test view]];
when I add a background like this right after setting the frame:
test.view.backgroundColor = [UIColor blackColor];
then I get the black background without that Bar on the top but the Label which is in the Slide1ViewController.xib won't appear.
Hope someone can help me.
If you really need 20 pages beaing each one a view controller you should use a uinavigationcontroller and if you really wanna have a scroll view, use a uitableview and make each cell the view of one of your uiviewcontroller because that way the each view will be loaded when needed, whereas in a scroll view you might get too much contend loaded at the same time.
I'm sorry i didn't realize UINavigationController was still selected so that i created the class while believing it's a UIViewController

Manipulating an NSSavePanel's accessoryView?

I have an NSSavePanel set up with an accessory view that contains a format popup menu and a tabless tabview with various options regarding the selected format. It shows up on the panel correctly, but I cannot seem to adjust its size.
Calling -setFrameSize on the accessoryView results in odd and incorrect resizing once, and then no response for any subsequent calls. Other controls in the view flat out refuse to respond to any calls at all.
Does NSSavePanel work like NSSearchField, which only uses its assigned menu as a template? If so, how can I manipulate the specific instance of the accessory view in the current active save panel?
My experience has been (especially with code that has to run on 10.5) that in order to handle permuting sizes in the accessoryView for NSSavePanel we had to remove (set it to nil) and re-add it. Under 10.7 (and, I believe, 10.6), it appears to be sufficient to call [savePanel layoutIfNecessary] after changing the frameSize.
In this case, since you mention that you are using invisible tab views. Usually a tab view has a consistent size. If you're looking to resize the NSSavePanel based on the contents of the various subviews, you may want to keep them as separate views (in the same or other NIB files) and load them as child views in the NSSavePanel.
I've successfully done this in a situation where the NIBs were dynamically loaded from an on-disk list of plug-in modules, where I used a single overall view that contained the popup menu and then I resized that view using -setFrameSize: in order to change it prior to adding the child view to it. Then I used addSubview: to add the subview to my existing view and called [savePanel layoutIfNeeded] after changing the size and adding the subview.
Here is the snippet of four adding the exportAccessoryViewController (this for us is what changes based on the selection of the popup menu) to our existing exportFormatOptionsView view (which contains the popup menu):
NSSize currentSize = [exportFormatOptionsView bounds].size;
NSView *newView = [exportAccessoryViewController view];
NSSize newSize = NSMakeSize( currentSize.width, currentSize.height+[newView bounds].size.height);
// resize the parent view
[exportFormatOptionsView setFrameSize: newSize];
// move the child view
[exportFormatOptionsView addSubview: newView];
of course, when you switch these out dynamically, you need to make sure you change the view size of the intermediate view back to the original size, so you can add the supplementary view in later, which I did like this:
NSSize currentSize = [exportFormatOptionsView bounds].size;
NSView *oldView = [exportAccessoryViewController view];
// pull it out
[oldView removeFromSuperview];
NSSize newSize = NSMakeSize( currentSize.width, currentSize.height-[oldView bounds].size.height);
[exportFormatOptionsView setFrameSize: newSize];
exportAccessoryViewController = nil;
Hope this helps.

UIScrollView.size = view.size - allAdditionalBars.size (like TabBar or NavigationBar) programmatically

UIScrollView is set programmatically, please dont post answers with using .xib file.
My UIScrollView is situated in my model class, so i want the code to be able to be easly imported to another project eg. for iPad or with rotating screen.
I have a view:
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width;, self.view.frame.size.height;)];
And my UIScrollView. I want to set it's size to cover all screen not counting all bars that my controller class will have. But i dont know how ;)
I though about subtracting self.view.frame.size.height - self.navigationController.navigationBar.frame.height and self.tabBarController.tabBar.height if each exists.
Is there any method that automatically sets UIScrollView size..?
Thank you in advance!
In your UIViewController subclass, you don't need to worry about the size of any UINavigationController or UITabBarController chrome. Those controllers will automatically resize your controller's main view to fit the appropriate content area.
If I'm creating the UIView myself in the controller's loadView, I usually just initially size it at [[UIScreen mainScreen] applicationFrame]. If I were to add a UIScrollView as a subview that would fill up the entire area of the main view (rather than just using the UIScrollView directly as the main view), I would use self.view.bounds as its frame and be sure to set autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;.

Why is this UIImageView autocentering itself?

I have a UIImageView in a UIScrollView in another UIScrollView (based on Apple's
PhotoScroller sample code). When the UIScrollView calls back to its controller to dismiss itself, it calls this method:
- (void)dismiss {
[scrollView removeFromSuperview];
ImageScrollView *isv = [self currentImageScrollView];
UIImage *image = isv.imageView;
image.frame = [self.view convertRect:image.frame fromView:isv];
[self.view insertSubview:image belowSubview:captionView];
[(NSObject *)delegate performSelector:#selector(scrollViewDidClose:)
withObject:self
afterDelay:2.0];
}
Now here's the weird part: the image view jumps to a different position right after this method executes, but before the scollViewDidClose method gets called on the delegate. If the image is larger than its new super view, it jumps so that its left edge is aligned with the left edge of its super view. If it's smaller than its new super view, it jumps to the very center of the view. There is no animation to this change.
So my question is, how do I prevent it from doing that? I've tweaked both the super view (self.view) class and the image view class to see what methods might be called. Neither the frame nor the center is set on the image view after this method is called, and while the layoutSubviews method is called on the super view, that is not what jumps the image to the center or left side of the superview. I've also tried turning off autoResizesSubviews in the super view, and setting the autoresizingMask of the image view to UIViewAutoresizingNone, with no change in behavior.
So where is this happening, and why? And more importantly, how do I make it stop?
I've been beating my head on this for days. Any pointers or guidance would be greatly appreciated.
ImageScrollView is the one centering your UIImageView. Set a breakpoint in ImageScrollView layoutSubviews and you'll see how your UIImageView is being centered.
You're taking ImageScrollView's internal imageView and placing it into another view. That's not going to work because ImageScrollView still retains ownership of that UIImageView instance and is still managing its layout.
You'll either need to copy the image into another UIImageView instance, or you'll need to change ImageScrollView to allow it to relinquish ownership of its imageView.
You're not setting up the frame of the 'image' view when you insert it as a subview. You probably want to do that explicitly if you want the view to appear at a particular position in the scroll view.