Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting NSScrollView Contents to Top Left Instead of Bottom Left When Document Swapping

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 IBOutlets:

  • 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 documentViewof 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.

like image 815
Hooligancat Avatar asked Jan 15 '11 01:01

Hooligancat


2 Answers

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.

like image 157
Hooligancat Avatar answered Nov 07 '22 02:11

Hooligancat


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.

like image 4
arri Avatar answered Nov 07 '22 01:11

arri