NSImageView size does not follow its window's - objective-c

I'm just playing around with OSX app dev. I'm totally noob. I thought it is straight forward like iOS app dev. But after a few days of going at this, it seems it is not that easy.
Here's what I'm trying to do. I have a NSWindow. In it in which I put an Image Well (wth with the naming? lol) Well "Image Well", it is an NSImageView (no pun intended).
So I just want the NSImageView's frame to resize following the NSWindow's size. It's that simple.
Here's what I did that is NOT working:
NSImageView as imageView.
write the delegate method NSWindowDelegate method of NSWindow windowDidResize and windowDidResize: and just resize the framesize of imageView frame of the image view in it. Code: (The NSImageView is in a property called imageView.)
- (void)windowDidResize:(NSNotification *)notification {
// NSLog(#"resized");
NSRect zWindowRect = [[self window]frame];
NSRect zContentRect = [[self window]contentRectForFrameRect:zWindowRect];
[self.imageView setFrameSize:NSMakeSize(zContentRect.size.width,
zContentRect.size.height)];
[self.imageView setNeedsDisplay:YES];
}
This method is called (tested that with NSLog()), however the NSImageView just stays there with the original size. I checked that the IBOutlet connection is OK.
What gives? Why won't the NSImageView resize?

Ok. This is becoming a habit. Answering my own question again.
It seems that this behaviour is due to the "Auto Layout" feature of the Interface Builder.
To fix it, just disable "Auto Layout" in the MainMenu.xib. SEE HERE FOR EXPLANATIONS
In case the site expires: just click on MainMenu.xib, then go to the first tab, File, in the Utilities panel of XCode. And there should be a "Use Auto Layout" checkbox next to it. Uncheck it.

Related

Supporting toolbar visibility in macOS 10.14 Mojave

I was testing how an existing application works on Mojave and I found strange problem with the layout. Toggle window toolbar visibility off then back on makes toolbar to appear on top of the content instead of pushing it down.
Snippet:
CustomPopUp *popup = [CustomPopUp popup];
__weak CustomRevealViewController *rev = self.revealViewController;
__weak NSWindow *window = self.view.window;
// Called when user taps in the popup view
[popup setCloseAction:^(BOOL success){
[rev removeOverlayController];
// !!! After this layout breaks
[window.toolbar setVisible:YES];
}];
[window.toolbar setVisible:NO];
[self.revealViewController showOverlayController:popup];
Any idea what could cause the problem and how to fix it?
Edit 1:
I've created a simple project here.
Steps to reproduce:
Run the project with Xcode 10 beta (Requires Mac OSX Mojave)
Press "Hide toolbar" button. This will hide the toolbar and will
update the button title.
Press "Show toolbar"
Resut:
Toolbar looks broken. It is over the controller.
Expected result:
It should work like on any other OS version. Controller's top constraint should be moved down.
Edit 2:
Maybe I found a clue in the AppKit Release Notes for macOS 10.14 beta under Layer-Backed Views section.
Views that implicitly depend on being redrawn when an ancestor,
descendant, or intersecting sibling is redrawn may not be redrawn. As
before, if a view needs to be redrawn, set its needsDisplay property
to YES. Views that return YES from wantsUpdateLayer will typically be
given an exclusive layer, even if the view's wantsLayer property is
set to NO. Apps targeting macOS 10.14 should prefer the
wantsUpdateLayer property over the wantsLayer property.
I'm still not sure how to fix it.
Edit 3:
With the official release of Mojave this is no longer an issue. Thank you I really appreciate your help.
I have a similar problem. I have a spash window without toolbar and then I fade it out and show another window with a toolbar.
The window with toolbar has broken layout in official release of Mojave (view appears beneath the toolbar). To fix it, I hide and then show again window's toolbar:
- (void)viewWillAppear
{
[super viewWillAppear];
if (_firstTime) {
_firstTime = NO;
NSWindow *window = self.view.window;
window.toolbar.visible = NO;
window.toolbar.visible = YES;
}
}

Auto-resizing window

I'd like to embed a Tabbar or a TabViewController using a container into a ViewController of a Window for a macOS App. The two ViewControllers of the Tabbar / TabVC should have different heights, a tall one and a small/flat one.
The point is, that I'd like to automatically resize the whole window height when the Tabbar / TabVC changes its ViewController. This way the window is always as small as possible and doesn't show any unused and empty space.
This is my current storyboard, which (of course) doesn't resize the windows:
Does anybody have a hint, how to automatically resize the window according to the size of the selected ViewController in the Tabbar or TabVC?
Try setting the preferredContentSize in viewWillAppear like:
- (void) viewWillAppear
{
[super viewWillAppear];
self.preferredContentSize = CGSizeMake(self.view.fittingSize.width, 194);
}
You can also use self.preferredContentSize = self.view.fittingSize; if that works for you.
But I'm not sure if this will animate the change for you.

NSWindow not resizing to fit NSView - SOMETIMES?

I'm having trouble resizing an NSWindow to fit an NSView. It must be a logic error as it works but not for one action.
I have one NSWindow which is empty, and 3 NSViews with components and are different sizes.
With the following code I resize the NSWindow to fit the NSView and display it:
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
This code works fine.
However in one NSView I have a Back button, and while this displays the correct NSView in the NSWindow, it does not re-size it back. As an example the initial window is a certain size, I click to switch to another view and it resizes correctly, I press the back button, the NSView is displayed but the window stays the same size?
Can anyone explain to me why when I switch back to the original NSView, it doesn't resize the NSWindow?
Thanks in advance everyone. This is the complete code I have:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
}
- (IBAction)switchSubtractionView:(id)sender {
[_window setContentSize:_subtractionView.frame.size];
[_window setContentView:_subtractionView];
}
- (IBAction)switchAdditionView:(id)sender {
[_window setContentSize:_additionView.frame.size];
[_window setContentView:_additionView];
}
// -----------------------------------------------------------------------
// THE FOLLOWING METHOD DISPLAYS THE NEW VIEW CORRECTLY BUT DOESN'T RESIZE
// -----------------------------------------------------------------------
- (IBAction)switchMainMenu:(id)sender {
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
}
Thanks in advance everyone.
EDIT: it seems to me that when getting VIEW.frame.size, if this is repeatedly used, it loses its values? This seems very strange behaviour to me?
A window's content view must always be sized to fill the window's content area. Therefore, when you set the window's content size, you effectively change the size of the current content view. This happens just before you switch the content view, so you are changing the size of the old view.
Try setting the content view to a new, disposable NSView before changing the content size, and then setting the new content view.

Why am I getting different behaviour when replacing a subview created in the Interface Builder vs one created in code?

I have a cocoa WebView inside of an NSSplitPane as a subview of one of the split pane's Custom Views. This serves as a preview of some HTML content. To smooth the transition when updating the preview
I make an NSImageView from the WebView
Replace the WebView with the NSImageView
Load the new html into the WebView
Replace the NSImageView with the updated WebView when the html has finished loading
This is the gist of the code is:
From the header
NSImageView *previewImageView;
NSString *content;
#property (strong) IBOutlet NSView *previewContainer;
#property (strong) IBOutlet WebView *previewWebView;
From the class
- (void)updatePreview
{
previewImageView = [self imageViewFromWebView:previewWebView];
[[previewContainer animator] replaceSubview:previewWebView
with:previewImageView];
[[previewWebView mainFrame] loadHTMLString:content baseURL:nil];
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
[[previewContainer animator] replaceSubview:previewImageView
with:previewWebView];
}
This code does not work correctly if the WebView is defined in the xib file with the referencing outlet set to the previewWebView and the frame load delegate set. The web view is initially shown correctly, gets swapped for the image view ok, but when swapped back does not get displayed.
If I instead define the WebView in code
// inside of viewDidAppear
NSRect frame = [previewContainer frame];
NSRect webViewFrame = NSMakeRect(0, 0, frame.size.width, frame.size.height);
previewWebView = [[WebView alloc] initWithFrame:webViewFrame];
[previewWebView setUIDelegate:self];
[previewWebView setFrameLoadDelegate:self];
[previewWebView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[previewContainer addSubview:previewWebView];
and not the interface builder, the swapping code works as expected. Any ideas as to what may be different about how I'm defining the WebView in code that makes it work but not when define in the interface builder?
It's very possible that Interface Builder archives the WebView object with some settings that are different to the defaults when creating a WebView programmatically.
You should probably try a few things:
[previewWebview setHostWindow:yourWindow];
This associates the WebView with your window. This is required if you remove the WebView from the window, otherwise the WebView will stop operating. The WebView will retain your window, so you should make sure you set the host window to nil before closing your window.
[previewWebView setShouldUpdateWhileOffscreen:YES];
This will ensure the web view actually loads content when it's offscreen.
[previewWebView setShouldCloseWithWindow:NO];
This will prevent the WebView from "closing" when its host window closes. If you don't do this, the WebView will call its close method, which essentially shuts it down, clearing all content and caches and preventing it from being used again. I'm pretty sure this is the default when you instantiate the WebView in Interface Builder, so you want to make sure it doesn't happen.
You may find that you don't need to do this if you've set the host window specifically.
Note that you will need to call [previewWebView close] when you do actually close your window if you do this.

NSView's context NSMenu is never shown even though all the right methods are being called

I have an NSCollectionView with a bunch of NSViews in it, stacked vertically, to make it look a bit like UIKit's UITableView. Everything works as expected, except for one thing:
When right-clicking any one of the NSViews, I expect the NSMenu I set to be view's menu to be shown, but alas - nothing happens.
The crazy part is all the right methods are being called, exactly as could be expected: -rightMouseDown:, -menuForEvent: and finally -menu.
When I set up any object as the NSMenu's delegate, menuWillOpen: is not called, so it seems to me something fails over on Apple's side of things, just in between asking for the menu, and actually showing it.
Would anyone be able to shed a light on this?
Thanks in advance.
PS. For what it's worth, NSMenus I present manually (without relying on Apple's right-click handling) using popUpMenuPositioningItem:atLocation:inView: are shown.
Edit / Update / Clarification
The NSCollectionView in question is inside an NSWindow that's being shown when an NSStatusItem is clicked, like CoverSutra/TicToc/what have you. Some code from the MyWindow NSWindow subclass:
- (void)awakeFromNib {
[self setStyleMask:NSBorderlessWindowMask];
[self setExcludedFromWindowsMenu:YES];
}
- (BOOL)canBecomeMainWindow {
return YES;
}
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)isMovable {
return NO;
}
- (void)presentFromPoint:(NSPoint)point {
point.y -= self.frame.size.height;
point.x -= self.frame.size.width / 2;
[self setFrameOrigin:point];
[self makeMainWindow];
[self makeKeyAndOrderFront:self];
}
presentFromPoint: is the method I use to present it from any point I like, in my case from just below the NSStatusItem. (Not really relevant to this problem)
My application has LSUIElement in its Info.plist set to YES by the way, so it doesn't show a menu bar or a Dock icon. It lives in the status bar, and has a window that's shown when the NSStatusItem is clicked.
The view hierarchy is as follows:
MyWindow => contentView => NSScrollView => NSCollectionView
The NSCollectionView has an NSCollectionViewItem subclass connected to its itemPrototype property, and the NSCollectionViewItem subclass has an NSView subclass connected to its view property.
The NSView subclass, in turn, has an NSMenu connected to its menu property.
And last but not least: This NSMenu has one NSMenuItem sitting inside it.
Both the NSCollectionViewItem subclass and the NSView subclass do nothing interesting as of now, they're just empty subclasses.
The NSMenu connected to the NSView's menu property is what should be shown when the NSView is right-clicked, but as I hope I have made clear: It isn't actually shown.
Update
I still have no idea what caused this problem, but I've decided to 'move on' from NSCollectionView, as it wasn't really fit for what I was trying to do anyway, and I am now using TDListView which works like a charm.