Image Tiling - Scroll View problem, using Xcode 9.3 and Swift 4.1

Has anyone figured out how to get the scroll view to work using Xcode 9.3 and Swift 4.1? The only thing I find online is for iOS, not OS X. I have it set up as described in the book, and the scrolling doesn’t work and I haven’t learned enough yet to figure out a fix.

import Cocoa

@IBDesignable class TiledImageView: NSView {

@IBInspectable var image: NSImage?
var columnCount = 5
var rowCount = 5

override func draw(_ dirtyRect: NSRect) {
	if let image = image {
		for x in 0..<columnCount {
			for y in 0..<rowCount {
				let frame = frameForImageAtLogicalX(logicalX: x, y: y)
				image.draw(in: frame)
			}
		}
	}
}

override var intrinsicContentSize: NSSize {
	let furthestFrame = frameForImageAtLogicalX(logicalX: columnCount-1, y: rowCount-1)
	return NSSize(width: furthestFrame.maxX, height: furthestFrame.maxY)
}

// MARK: - Drawing

func frameForImageAtLogicalX(logicalX: Int, y logicalY: Int) -> CGRect {
	let spacing = 10
	let width = 100
	let height = 100
	let x = (spacing + width) * logicalX
	let y = (spacing + height) * logicalY
	return CGRect(x: x, y: y, width: width, height: height)
}

}

1 Like

Although 1.2 decades all, here is an article worth reading about scroll views in Cocoa: Scroll View Programming Guide for Cocoa.

Tip: For the scrolling to work, the bounds of the document view must be larger than its frame.

Thanks again for your help ibex.

I’ve read the programming guide, it has the following in it to set up a Scroll View in the Interface builder:

Creating a Scroll View in Interface Builder

Creating a scroll view in Interface Builder is straightforward.

Create the view, or views, that will be the document view of the scroll view.
Select that view, or views, that will be the scroll view’s document view.
Choose Layout > Make subviews of > Scroll View. This creates a new NSScrollView instance with the selected view, or views, as its document view.
Open the inspector and configure the visible scrollers, background color, and line and page scroll amounts.

I’m assuming when the current Apple documentation on how to use their software says to “Choose Layout > Make subviews of > Scroll View. This creates a new NSScrollView instance with the selected view, or views, as its document view.”, that they haven’t updated their programming guides (which I’ve seen many times now) to: select Editor → Embed In → Scroll View? Which is how I did it. And I still can’t get the scrolling to work when I run it.

If I understand your tip correctly, the bounds of the TiledImageView must be larger than the the frame of the Scroll View, which I think it is, since it is supposed to show 5 x 5 of the NSComputer images and it is only showing barely two rows and 3.5 columns.

I had previously found some discussion on the apple forums that the latest update to Swift 4.1 and Xcode 9.3 is causing problems, but I can’t find that same discussion now, so I don’t know if that may be causing my problem or not. It seems to me that I’ve set up the way it should be, it just isn’t doing anything.

Thanks again.

I was able to reproduce the problem you are having. I then realised that I could adjust the auto layout constraints. I did that, and the scrolling started to work.

I configured the auto layout constraints of the tiled image view as shown below, with the priorities of both Horizontal Space Constraint and Vertical Space Constraint set to the low value of 250.

1 Like

Many thanks again, ibex10.

I have now learned that I need to learn a lot more about auto layout and constraints so then I can learn when and what needs to be set.

I set the constraints and set them as you suggested, and it works as advertised now. Thank you.

Also, now that I’ve set the constraints, I get this warning…

warning: Ambiguous Layout: Size is ambiguous for “Tiled Image View”.

I’ll try to research and figure it out.

I fixed the warning by adding constraints to the scroll view, which after I did that and ran it, I see I should have done that all along too.

Back to reading about Auto Layout…

For scrolling to work, the main point is size, not auto layout.

//  ViewController.swift

import Cocoa

class ViewController: NSViewController {
  @IBOutlet weak var innerView: InnerView!
  
  override func viewDidLoad() {
    super.viewDidLoad()

    innerView.frame = view.frame.insetBy(dx: -50, dy: -50)
  }
}
//  InnerView.swift

import Cocoa

class InnerView: NSView {
  override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)

    let stringAttributes: [NSAttributedStringKey: Any] = [
      .font: NSFont.systemFont(ofSize: 50, weight: .bold),
      .backgroundColor: NSColor.yellow]

    var textString = "Origin" as NSString
    textString.draw(at: NSPoint.zero, withAttributes: stringAttributes)

    textString = "Center"
    textString.draw(at: NSPoint(x: NSMidX(bounds), y: NSMidY(bounds)), withAttributes: stringAttributes)
  }
}

Thanks HKray.

I tried to adapt your suggestion to what I had using the TiledImageView instead of the InnerView class you showed (minus the constraints previously suggested). I did this by deleting the Bordered Scroll View and everything it contained, then replacing the custom view, embedding it again in the scroll view and setting it up to display TiledImageViews. I did it this way, because I read in the documentation that once you place constraints, the implicit constraints go away, so I wanted to try this “clean” so to speak.

The scroll bars would show and limited scrolling was available, however, it would only show and “scroll” the initial images that were displayed, including the images that appear cutoff. They all would move around with the scrolling, but it was just moving the original clipView, not the entire view.

When the previously mentioned constraints were entered, it would work. I don’t know how to make it work without the previously mentioned constraints, but it does work with them.

Beginning with chapter 1 this book is useless, the code is not updated to Xcode 9 and Swift 4. Shame in the authors and the publishers, a brand new book that is useless!

It was published in 2015, which means it was written before then.

I agree it is very frustrating, BUT, it teaches you how to research and search for solutions using the current language and Xcode. So for me, it is useful. I know, every solution to problems I will encounter when programming won’t be in a book. I’ll have to research and seek out the answers. The users of this site have been very helpful when I can’t find something anywhere else…or I’ve made a stupid mistake.

What is way MORE aggravating is the outdated Apple documentation that still has depreciated methods in their programming guides. You’d think the company designing the languages would keep their programming guides up to date!

1 Like

Hi there,
I just read to Chapter 4 and got jammed,and I notice not so many recent posts,guess people all move to more advanced materials,can you pls help me with my problem?

Post it in Chapter 4 and I’ll go look…

Oh lord you replied!!!
I post!I post!
Can I see your code?XOXO

Can I have your email address? :slight_smile:

https://forums.bignerdranch.com/t/chapter-4-i-do-step-by-step-but-so-many-errors-cant-build/14358/2?u=shay123321

I’ll take a look in a while…

Shay,

This forum is for Cocoa Programming for OS X (5th Addition). Your question pertains to iOS. When I clicked on ‘4. Text Input and Delegation’ in your question, it goes to the iOS programming book’s page. Go to that forum, and state your question there and see if someone on there can answer. There are probably still some folks watching the forums there too. I’m learning the OS X first before iOS, so I can’t help you…yet.

Try to taste ViewController (not WindowController) based programming by building a simple iOS app with a label and a button. After you tap the button, the label shows some text like “Hello, World!”.

Programming macOS is just like programming iOS.

Thank you, I will look more into iOS programming although I have no interest in programming for the iPhone. Just disappointed that I bought a new book that turned out to be useless.

ibex10’s post certainly got us all on the right track. But I think the better adjustment is to lower the priority of the bottom and trailing constraints to 250, and leave the top and leading constraints at 1000. This will force the Tiled Image View to collapse from the bottom right, not the top left.

All pin constraints default to 1000, which caused our initial problem. The Scroll View and the Tiled Image View were required to be the exact same size, so Interface Builder turned scrolling off—it wasn’t needed. See below for the revised settings:

Two last suggestions: First, override the isFlipped property, as suggested at the end of Chapter 17:

override var isFlipped: Bool {
    return true
}

This will anchor the images to the top-left corner, instead of the lower-left corner.

Second, add reasonable minimum and maximum sizes for the window in its Size inspector:

[Sorry, I tried to add a screenshot of this, but the forum administrators won’t let new users like me put more than one image in a post.]

Like the other contributors on this post, I cry real tears for people who are just starting out and trying to use this book with the current versions of Swift and Xcode. But the nice folks at Big Nerd Ranch have referred me to a more recent book (since theirs is no longer being updated):