Skip to content
Snippets Groups Projects
Commit 47d2bc9d authored by Peter J. Keleher's avatar Peter J. Keleher
Browse files

auto

parent 8e020cef
No related branches found
No related tags found
No related merge requests found
# 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
![](https://sedna.cs.umd.edu/436clips/assign3.mp4)
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.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment