I had one of my colleagues come to me today with a problem of how can he load or view-swap a documentView
of an NSScrollView
so that the loaded view appears to fix to the top-left corner instead of the bottom-let corner.
He had spent a while searching the web and floundering and didn't have a solution despite reading the documentation at Apple, on StackOverflow and various other places.
Here's the issue piece-by-piece:
In Interface Builder drag an NSScrollView
into the project.
Also in Interface Builder drag two custom views into the project and add some textfields, buttons etc.
Create a controller class (e.g. myController) with the following IBOutlet
s:
IBOutlet NSScrollView * myScrollView
IBOutlet NSView * myCustomView1
IBOutlet NSView * myCustomView2
Connect the outlets to the controls in Interface Builder.
Create an NSView
subclass to flip the documentView
:
@implementation myFlippedView
-(id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
-(void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
}
-(BOOL)isFlipped {
return YES;
}
Select the documentView
of the NSScrollView
in Interface Builder and make it a subclass of myFlippedView
. In Interface Builder you would select the NSScrollView and then click in it again to get to the documentView
, or change the IB Library to show a tree view and select the child custom view of the NSScrollView
.
In the myController
class swap the views with the following methods:
-(void)awakeFromNib {
[myScrollView setDocumentView:myCustomView1];
}
-(IBAction)swapViews:(id)sender {
if ([myScrollView documentView] == myCustomView1) {
[myScrollView setDocumentView:myCustomView2];
} else {
[myScrollView setDocumentView:myCustomView1];
}
}
Finally hook up a button in your project to the action swapViews
, build and run.
The problem is that the coordinates were not being resolved as was expected with the isFlipped.
There is a simple, but apparently often overlooked reason for this.
Although isFlipped
is subclassing the custom view in Interface Builder, the documentView is getting replaced on the view swap, the first of which gets used on awakeFromNib
.
The solution is to subclass myCustomClass1 and myCustomClass2 to the myFlippedView class.
Do this and test it and you will notice that the views now appear in the top left of the scroll view. It has, however, created a new problem. All of the content in the custom view is now laid out from bottom to top (because by default all NSView
layouts are from bottom left, so flipping also flips their coordinates).
Fortunately there is another easy fix for this problem. Read on :-)
In Interface Builder, highlight all the controls in myCustomView1
and from the Layout
menu, select Embed Objects In --> Custom View. Resize as you need to, do the same for myCustomView2 and rebuild.
Voila. An NSScrollView
with the content view swapping and appearing at the top-left of the scroll view instead of the bottom left.
To correctly make the documentView
initially align to the top of the scrollview AND to also pin it to the top when the documentView
is smaller than it's clipview, this is what works for me:
1 – In Interface Builder enclose all controls in your documentView in a custom NSView
instance. (NOT a flipped view)
2 – Create an NSView
subclass to be used for the documentView
, and override these methods:
- (BOOL)isFlipped {
return YES;
}
- (void)resizeWithOldSuperviewSize:(NSSize)oldSize {
CGFloat superViewHeight = self.superview.frame.size.height, height = self.frame.size.height;
if(superViewHeight>height) {
[self setFrameOrigin:NSMakePoint(self.frame.origin.x, superViewHeight-height)];
}
}
The subclassed view should be the scrollViews' documentView
which contains second (normal) NSView that contains all controls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With