Step 1: Create a xib file with a NSWindow in it, this window is the "document window".
Step 2: Drag and drop a NSView into the view of the window.
Step 3: Select the inner NSView and choose (Xcode 7) menu item "Editor -> Embed in -> Scroll View". This step is important, it sets up internals such that the inner NSView is connected correctly to the NSScrollView. Do not try to do this manually by dragging and dropping a NSScrollView into the window!
Step 4: Create your document view, I will call it LineView. This class inherits from NSView. This line view will need access to the document, for this I will add a reference to it (the document is created in step 7):
var document: LineDocument! {
Step 6: Also in the xib file, add constraints to LineView to position and size it. Do the same for the scrollview inside its superview. By default I add four constraints such that each view completely fills its superview.
The above will set up a functioning scrollview. But since I want to open a variable size document in it, here are the next things to do.
Step 7: Create your own document class by creating a new class (LineDocument) and subclass from NSDocument.
Step 8: Create a document window controller, inherit from NSWindowController. The window controller (LineDocumentWindowController) will be used to give the LineView inside the scrollview access to its line document object. In order to do so add the following to the implementation to the window controller:
@IBOutlet weak var lineView: LineView!
override func windowDidLoad() {
lineView.document = document as? LineDocument
}
Step 10: In the xib file, connect the lineView outlet in the file owner to the LineView in the scroll view.
Step 11: In our document (LineDocument) add the following code to create the document window:
override func makeWindowControllers() {
self.addWindowController(LineDocumentWindowController(windowNibName: "LineDocument"))
}
Note: this assumes that the xib file is called "LineDocument.xib"
Of course you will need to add code to your document to manage the data that makes up the document.
And of course you will need to add code to your document view to actually create the view content (in drawRect).
There is however one more thing to do: if the users adds content to the document you will need to update the frame of the document view. This is best done by a notification, raised by the document, observed by the view.
So far so good, but if you want a background pattern (for example alternating line background colours) another problem will crop up: when the scrollview is sized to be bigger than necessary for the document, the background pattern will only be drawn directly behind the document content and not in the surrounding space.
To fix this it is necessary to artificially change the size of the frame of the document view to include the "empty" space in the document view.
For this, I use a function "adjustFrame" which is called in drawRect. A better solution would be to call that function only when the window is resized and when the size of the document changes. But simply including it in drawRect will suffice.
The adjustFrame function in the document view class looks as follows:
/// Resizes the frame to always fill out the available space. This ensures that the whole frame in NSScrollView is always filled out with the background pattern, giving the illusion of an endlessly big canvas.
private func adjustFrame() {
// Determine the minimum size needed for our content area, then see if the contentview is bigger. When either width or height of the contentview area is bigger, use those values.
let neededFrame = minimumLinesRectangle
let availableSize = enclosingScrollView!.contentSize
var newFrame = frame
newFrame.size.width = max(neededFrame.size.width, availableSize.width)
newFrame.size.height = max(neededFrame.size.height, availableSize.height)
frame = newFrame
}
PS: The minimumLinesRectangle is a property that evaluates the minimum size needed to display all of the document data.
Update 2015.10.01: I still have problems where the scrollview sometimes jumps back to the upper-left corner of the documentView. I finally solved that by creating the scrollview programatically. Seems there is a difference between interface builder and programatically created scrollview hierarchies. Go figure. You can read about the programatic example here.
Happy coding...
Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH
We don't get the world we wish for... we get the world we pay for.
can you plz make a video of it
ReplyDeleteSorry, I don't do video's. (We all have to draw a line somewhere ;-))
ReplyDelete