Chapters

Hide chapters

macOS Apprentice

First Edition · macOS 13 · Swift 5.7 · Xcode 14.2

Section II: Building With SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

Section III: Building With AppKit

Section 3: 6 chapters
Show chapters Hide chapters

15. Creating the Edit Interface
Written by Sarah Reichelt

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous chapter, you created a contextual menu for the movies table. One of the menu items is for editing a movie, but at the moment, it doesn’t do anything.

Over the next two chapters, you’ll add and configure a new window for doing these edits, with edit fields for the data like the movie title and year, plus a table for listing and editing the movie principals.

When you finish, the edit window will look like this:

Final edit window.
Final edit window.

Adding a New Window

Launch Xcode and open your MovieTables project or get the starter project from the downloads for this chapter.

The first task is to add a new window controller and view controller in the storyboard. Open Main.storyboard and then open the Library using the + button in the toolbar or by pressing Shift-Command-L.

Search for win and drag a Window Controller into any blank area in the storyboard. This adds a Window Controller with a window and a connected View Controller with a view.

Select the window and press Command-Option-5 to open the Attributes inspector. Set its Title to Edit. The window and view are too small, so switch to the next inspector along — the Size inspector — and set the Width to 600 and Height to 400. Press Tab or Return to process your new size, then check Minimum Content Size and make sure the same numbers appear there.

How do you decide what sizes to set? Looking at the design, the table is the widest component. It needs three columns, each of which will be between 150 and 200 pixels wide. Rounding that to a neat number gives a width of 600. For the height, you want to use a fraction of the width. Two-thirds of the width gives 400, which looks good. You may need to change this later as the design develops, but this provides a decent starting point.

Next, select the View Controller ▸ View and set its width and height to 600 and 400 to match.

This View Controller needs its own class. Select TableData.swift in the Project navigator and press Command-N to create a new file. This time, choose macOS ▸ Source ▸ Cocoa Class. This gives a file containing a subclass of a known AppKit class.

Click Next and set the Class to EditViewController. Choose NSViewController from the Subclass of: popup. Uncheck Also create XIB file for user interface. You’ll use Main.storyboard for the design, so you don’t need a separate design file. Make sure Language is set to Swift and press Next, then Create:

Adding a view controller.
Adding a view controller.

This gives your project a new NSViewController class and now you can set it as the class for the newly added View Controller.

In Main.storyboard, select the new View Controller. Use the outline view to be sure you select the controller and not its view. Press Command-Option-4 to open the Identity inspector and use the Class popup to change it to EditViewController:

Assigning the view controller class.
Assigning the view controller class.

When you do this, the names in the outline view change too, making it easier to identify this view controller from now on.

Segueing to the New Window

The navigation link between two view controllers is a segue (pronounced SEG-way). To create a segue, position the objects in the storyboard so you can see the original View Controller and the new Edit window’s Window Controller. Click in the bar at the top of the View Controller so you can see its buttons. Control-drag from the View Controller icon into the new Edit Window Controller, not the Edit View Controller:

Creating a segue.
Psauderm i titio.

Identifying the segue.
Opeckitlokw yyi jebio.

performSegue(withIdentifier: "showEditWindow", sender: movie)
Segueing to the Edit window.
Latoioys ya lpa Ohuc tedyek.

Preparing Data for the Edit Window

When you initiate a segue, it triggers a method called prepare(for:sender:). You can override this method to customize what happens during the transition. At this point, the segue knows where it’s heading, so this is a great place to set some properties on the destination.

// 1
var movie: Movie?

// 2
var originalMovieData: Data? {
  // 3
  didSet {
    // 4
    if let originalMovieData {
      // 5
      movie = try? JSONDecoder().decode(Movie.self, from: originalMovieData)
      // 6
      print("In EditViewController")
      print(movie)
    }
  }
}

Passing the Data

Next, open ViewController.swift and add this new method underneath editMovie(_:):

// 1
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
  // 2
  guard segue.identifier == "showEditWindow" else {
    return
  }

  // 3
  guard
    let movie = sender as? Movie,
    let movieData = try? JSONEncoder().encode(movie)
  else {
    return
  }

  // 4
  print("Leaving ViewController")
  print(movie)

  // 5
  if
    let windowController = segue.destinationController as? NSWindowController,
    let editViewController = windowController
      .contentViewController as? EditViewController {
    // 6
    editViewController.originalMovieData = movieData
  }
}
Movie for editing
Pacoo suz ocupejd

The Editing Interface

The edit window has three main sections. The top section is for the single chunks of data: title, year etc. The middle is a table for the list of principals. And finally the bottom has the Cancel and Save Changes buttons.

Aligning a button.
Utamqajf e maxnul.

Cancel button attributes
Holzin fewmil izwxuluhip

Reset to suggested constraints
Dipeg va ruclopyeb yayyjpoupht

Adding horizontal spacing
Ubkaqk dilonezbuy tbixulg

Changing the constraint constant.
Fyevfexm sha pihcggoalc penndefy.

Placing the Table View

Find Table View in the Library and drag it so it aligns with the left alignment guide and clicks into the standard spacing above the Cancel button:

Adding a table view.
Egvorx e zahna foel.

Setting table constraints.
Kujjawx mepye caqwryueywt.

Editing table height.
Uyojirk loxna youylm.

Principals label
Hhivjigawz mufag

Setting up the Text Edit Fields

Checking the screenshot at the start of this chapter, the top section is the most complicated.

Positioning the top edit field.
Fupipaolegy jde geg ixem goiys.

Aligning the text fields.
Uruzdolq qqo besk juuybx.

Testing the Auto Layout.
Vogkupr zhe Uoqe Siqaen.

Constraining the width.
Lugjxmaofarq fna loxxy.

Labeling the Edit Fields

The Principals label is set up the way you want, so select it and use Command-D or Option-Drag, whichever you prefer, to duplicate it. Drag the copy up to beside the top edit field. These field labels align to the left of their matching edit fields, so use the Attributes inspector to set the alignment to Right.

Aligning baselines.
Uhurhuyj wiquqawuj.

Complete layout
Jiwpgujo dipoiv

Coding the Buttons

With Main.storyboard still open, Option-click EditViewController.swift in the Project navigator to open it in a secondary editor.

view.window?.close()
// 1
view.window?.makeFirstResponder(nil)

// 2
// tell parent view controller to save

// 3
view.window?.close()

Saving the Edits

In order for EditViewController to call a method in ViewController, it needs access to that view controller.

weak var parentVC: ViewController?
editViewController.parentVC = self
// 1
func saveEdits(for movie: Movie) {
  // 2
  let index = movies.firstIndex {
    $0.id == movie.id
  }

  if let index {
    // 3
    movies[index] = movie
  } else {
    // 4
    movies.append(movie)
  }

  // 5
  searchMovies()
  if selectedMovie?.id == movie.id {
    showSelectedMovie(movie)
  }

  // 6
  dataStore.saveData(movies: movies)
}
if
  let parentVC,
  let movie {
  parentVC.saveEdits(for: movie)
}

Making Editing Easier

Before getting into the actual editing, there’s a feature you can add to the main table to make editing faster and easier. How about letting the user double-click a line to open the Edit window?


Connecting the doubleAction.
Pocmihriwq bhi kaujnaUxhoeq.


Double-click editing.
Pouppu-lyirw omihamp.

Key Points

  • To show a secondary window, add a Window Controller and a View Controller to the storyboard.
  • Use a segue to transition to the new window.
  • Attach data to the segue to send it to the secondary window.

Where to Go From Here

The next step is to show the data in the fields and table. You’ll also want to configure the fields for editing and set up the table for sorting and editing. That’s all in the next chapter.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now