From 47d2bc9d639289fa6a5cc573246305a57c1028e1 Mon Sep 17 00:00:00 2001 From: "Peter J. Keleher" <keleher@cs.umd.edu> Date: Tue, 10 Mar 2020 14:05:27 -0400 Subject: [PATCH] auto --- assign3.md | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 assign3.md diff --git a/assign3.md b/assign3.md new file mode 100644 index 0000000..668295a --- /dev/null +++ b/assign3.md @@ -0,0 +1,262 @@ +# Assignment 3: Fancy Triples +**Due: Mar 29, 2020, 11:59:59 pm, v1** + + +## Goals +Learn to use: +- property animation +- custom views +- drawing +- gestures +- tab bar view controllers +- user defaults +- dynamic animations + + + +This is the project where we end up with a real game we can give out +to friends and family. We are not significantly changing core +functionality, but we are adding many of the extras that make apps usable. + +## Before you begin + +This project is based on the last one, but copying Xcode projects is a +dicy, multiple-step project, easy to mess up. + +Instead, I suggest you create a new project `assign3` in your repository, no need for either unit or UI tests. Create +a new `model.swift`, and then copy and paste +whichever portions of your model and `ViewController` code you want to keep (probably a lot +of it). + +**Note:** create your new project **assign2** directly in your repository, so the resulting directory +structure should look like this: +``` + repository + / \ + / | \ + / | \ + / | \ + assign1 assign2 assign3 + / \ / \ / \ + assign2/ assign2.x... assign3/ assign3.xcodeproj/ + / \ / \ + model.swift ... +``` + +Run this version to ensure everything is working correctly, quit out of Xcode, `git add +assign3`, and then push to your repository. Note that if you create more files, you need +to manually add them as well. Easiest to just do a `git add *` in any affected +subdirectories. + +We realize that `git` is sometimes a challenging, non-intuitive tool, especially as we are +not using Xcode's GUI interface, but understanding git will serve you well in almost any +facet of the tech world going forward. + +## Task 1: Change your storyboard. +Throw away your statically defined "tile" Buttons. You are going to +programmatically create +"tiles" on the fly to match your game model. + +The storyboard will have the following elements: +- a `view` to use as your square board (add a constraint to ensure that it stays square) +- a stack of controls, including: + - left, right, up, down buttons + - newgame + - segmented random/determ control + - score label + +Put the view and the control stack into another stack (the top-level stack), add constraints as I +did in class to: +- pull the top-level stack's leading and trailing edges to 10 points off safe + area, center it vertically +- ensure that the square board is as large as possible (pin leading and + trailing edges to it's superview + +### Adapt to Orientation changes +Introduce two variations to allow your storyboard to adapt smoothly to +landscape mode and back again: +- Introduce a *horizontal* variant to the top-level stack for *compact height* +- Put the storyboard in horizontal mode, click on "Vary for Traits", and get + rid of some constraints that no longer apply in horizontal mode (don't need + to pin leading and trailing edges of top-level stack to edges, etc.), and + add new ones (pin top and bottom of top-level stack to edges, etc.) +- Click done varying, go back to vertical mode, and click "Done Varying". + +You cannot run at this point because your `IBOutlet`s refer to non-existent views. + + +## Task 2: Change the model + +Your model is conceptually unchanged. However, the view controller is +now going to animate tile movement, and therefore needs to recognize +specific tiles in order to identify tile movements. + +We are therefore going to change our board's base type from `Int` to a +new struct `Tile`. +`Tile`s will have a unique *id*, and therefore be identifiable even +as their value (the number) changes. Define `Tile` as follows: +``` + struct Tile { + var val : Int + var id : Int + var row: Int // recommended + var col: Int // recommended + } +``` + +Your model's `board` will change from `[[Int]]` to `[[Tile?]]`. Note the +optional base type; each cell of your board will consist of either a `Tile` +or a `nil`. + +You might want to include `row` and `col` properties to make drawing on the +board a bit easier. Be sure to update these properties before returning to the +view controller after a collapse, spawn, or newgame. + +**Hint:** If a row has the following tiles and we are collapsing left: +``` + (val: 1, id: 13), (val:2, id:14), nil, nil +``` +you probably want to have tile 13 disappear, while tile 14 moves to the +left. This makes animation more straightforward. + + +## Implement TileButtons programmatically +Your `updateViewForGame` (substitute whatever name you use) needs to ensure +that there are buttons on top of the *board* (`viewOutlet` you've attached to +the square view you put on the storyboard) for each current tile. This +involves: +- create buttons for each newly-seen tile and adding as a *subview* to the + `board` at the correct location +- moving the `frame` for each tile that we've already seen +- removing buttons for any tiles that we had seen before but are no longer in + the model + +Before you get started, you should create a new subclass `ButtonTile`: +``` +class ButtonTile: UIButton { + var tile = Tile(val: 0, id: 0, row: 0, col: 0) + + convenience init(t: Tile) { + self.init() + tile = t + } +``` +where you have defined `Tile` in your new model. You can use a `ButtonTile` +just like a `UIButton`, but it has the `Tile` information readily at hand +(crucial for implementing animation via a custom view later). + +Your code will be able to recognize tiles from previous steps in the +application because each tile will have a unique `ID`. + +At the end of this step you should have a fully functional game again. It +should adapt well to the orientation changes *except* that existing TileButtons +might have the wrong size and position until a subsequent swipe. + +**Hint:** You should create a method `func buttonFrame(row: Int, col: Int, +view: UIView) -> CGRect` +that will compute the frame for a `ButtonTile`, based on `view` and the fact +that your game board will always have four rows of four possible positions. + +**Hint:** adding a button to `board` can be done w/ the following code: +``` + let button = ButtonTile(t: tileMap[id]!) + board.addSubview(button) +``` +Later on it can be removed via `tileButton.removeFromSuperView()` + +**Save early and often by pushing to your repository.** + +## Task 3: Custom View +At this point we want to build a custom view for our board, and have +`ButtonTile` operate independently of the view controller. + +- Subclass `UIView` with a custom `BoardView` by create a new Cocoa Touch + file. +- Move `buttonFrame` your new `BoardView` class. +- `override func draw(_ rect: CGRect)` to draw the board lines any way you + wish, though you must use `UIBezierPath`. +- `override func layoutSubviews()` to get lay out your buttons in the correct + location. + +`draw` is called by iOS whenever a previously unseen portion of the board gets +seen. Use this just to draw the board's background lines. + +`layoutSubviews` is the majority of this task. It is called whenever iOS think +there might have been a change in layout (at app startup, orientation changes, +and other seemingly random times). + +Your job is to make put each button in it's proper place, set the titles, +colors, etc. Views should not access view controllers' data directly. Instead make this +routine entirely standalone, deriving all information by querying its own +`subviews` property. + +`subviews` is an array of all child view, which in this case is only those +buttons that the view controller has put on the board. But how to figure out +where to situate the buttons, and how to figure out color and text of each? + +Luckily, these "buttons" are actually `ButtonTile`s. Cast each `UIView` to a +`ButtonTile` and you now have access to the complete `Tile` associated with +each button. + +The view controller's `updateViewFromGame()` method is now quite short. It has +to: +- add `ButtonTile`s to the board if the model has a previously-unseen `Tile`. +- remove `ButtonTile`s from the board if a previously-unseen `Tile` no longer + exists. +- ensure that all remaining `ButtonTile`s have accurate row/col information. +- update the score +- ensure that the board's layout / draw methods are called by calling `board.setNeedsDisplay()` + +## Task 4: Animation +Implement tile animation entirely in your `BoardView` class: +- animate new tiles but starting them w/ opacity of 0 and animating to opacity + 1.0 +- animate movement of existing tiles to new locations +- use opacity again to animate disappearence of previously-seen tiles + +**Hint:** Animating tile disappearence in `BoardView` will not work if the +view controller removes the `ButtonTile` from the board. + +## Task 5: Gestures +Add gestures to the board. The gesture recognizer use your +game's view controller as a target (first parameter to the +`UISwipeGestureRecognizer` initializer), but you can attach this either to +view controller's view, or to the game board. Either works. + +## Task 6: TabBarController +This task is simple: draw out a `TabBarController` in the storyboard, attach +the game controller (your original `ViewController`), and ensure that the `TabBarController` is the initial +controller by moving the arrow. + +Everything else should continue working the same. + +## Task 7: Implement the *HighController* Scene +You should: +- subclass the `UIViewController` to a new `HighController` class. +- use interface builder to add a `UITableView`. +- implement routines for the view controller to call providing a new high + score +- use `UserDefaults` to store and retrieve high scores +- the the tableview to show at least ten of the top scores with rank / score / + date-and-date, as shown in the video. +- your game should jump to the high-scores screen at the end of a game when + calling `isDone()` returns true, but not whe + + +## Task 8: Implement the *About* Scene +It doesn't have to be like mine, and it doesn't have use gestures, but it +should be fancy. Use `UIBezierPath` to draw something a diagram, make a little +mini-game using dynamic gestures, be creative! + + + +## Grading + Note that the above tasks are for sequencing your work. They do not match up exactly with this grading rubric: +- 30: change storyboard and buttons to be dynamic, adapting to orientation/device changes +- 30: animation, both movement and spawns +- 10: gestures +- 10: tabbarcontroller (just with labels saying "High Score" and "by AwesomeWhoever") +- 10: High scores using a tableview and `UserDefaults` +- 10: Fancy About scene using dynamic animation. + + -- GitLab