Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Chapter 6.5 of the Cosmos tutorial
// Copyright © 2016 C4
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import UIKit
//The amount of space between constellations (e.g. big/small stars)
let gapBetweenSigns : CGFloat = 10.0
class Stars : CanvasController, UIScrollViewDelegate {
//Specifies how quickly each layer moves, relative to top layer, order = [bottom, ..., top]
let speeds : [CGFloat] = [0.08,0.0,0.10,0.12,0.15,1.0,0.8,1.0]
//array of infinite scrollview layers
var scrollviews : [InfiniteScrollView]!
//variable used for marking observeValueForKeyPath
var scrollviewOffsetContext = 0
//the scrollview that holds the lines that connect small and big stars
var signLines : SignLines!
//the scrollview that holds the big star images, need variable to be able to observe its scroll position
var bigStars : StarsBig!
//array of targets to which the scrollview should snap
var snapTargets : [CGFloat]!
override func setup() {
//changes background color (specified in main view controller)
canvas.backgroundColor = cosmosbkgd
//adds layers to parallax background
scrollviews = [InfiniteScrollView]()
scrollviews.append(StarsBackground(frame: view.frame, imageName: "0Star", starCount: 20, speed: speeds[0]))
scrollviews.append(createVignette())
scrollviews.append(StarsBackground(frame: view.frame, imageName: "2Star", starCount: 20, speed: speeds[2]))
scrollviews.append(StarsBackground(frame: view.frame, imageName: "3Star", starCount: 20, speed: speeds[3]))
scrollviews.append(StarsBackground(frame: view.frame, imageName: "4Star", starCount: 20, speed: speeds[4]))
//Create the layer with the lines
signLines = SignLines(frame: view.frame)
scrollviews.append(signLines)
let smallStars = StarsSmall(frame: view.frame, speed: speeds[6])
smallStars.contentOffset = CGPointMake(view.frame.size.width * CGFloat(gapBetweenSigns / 2.0), 0)
//Create the layer with the small stars
scrollviews.append(smallStars)
//Create the layer with th big stars
bigStars = StarsBig(frame: view.frame)
//Add an observer for the scroll view's offset
bigStars.addObserver(self, forKeyPath: "contentOffset", options: .New, context: &scrollviewOffsetContext)
//Set the offset so the app appears with no sign
bigStars.contentOffset = smallStars.contentOffset
//Set the delegate of the scrollview (so that the observer method triggers)
bigStars.delegate = self
//Add it to the list of layers
scrollviews.append(bigStars)
//adds all layers to the canvas
for sv in scrollviews {
canvas.add(sv)
}
createSnapTargets()
}
//MARK: Vignette
//No need for a class for the vignette, it's simple to have an ISV with 0 speed
func createVignette() -> InfiniteScrollView {
let sv = InfiniteScrollView(frame: view.frame)
let img = Image("1vignette")!
img.frame = canvas.frame
sv.add(img)
return sv
}
//gets called every time the top scrollview layer is scrolled, runs because we created an observer at the end of setup()
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
//checks the context
if context == &scrollviewOffsetContext {
//casts the object to ISV
let sv = object as! InfiniteScrollView
//grabs the offset
let offset = sv.contentOffset
//for each layer running from bottom, to top
for i in 0..<scrollviews.count-1 {
//grab the layer
let layer = scrollviews[i]
//set the layer's offset based on the top layer's current position
layer.contentOffset = CGPointMake(offset.x * speeds[i], 0.0)
}
}
}
//MARK: Snapping
func createSnapTargets() {
//the app will check against these targets to see if it should snap into place
//each target is essentially the placement of the white dashes for each sign
snapTargets = [CGFloat]()
for i in 0...12 {
snapTargets.append(gapBetweenSigns * CGFloat(i) * view.frame.width)
}
}
func snapIfNeeded(x: CGFloat, _ scrollView: UIScrollView) {
//check all the targets
for target in snapTargets {
//calculate the distance from a position to that of the target
let dist = abs(CGFloat(target) - x)
//if the abs value is less than half the width of the screen
//i.e. if the "dash" is on screen
if dist <= CGFloat(canvas.width/2.0) {
//snap the scrollview into place
scrollView.setContentOffset(CGPointMake(target,0), animated: true)
//wait a bit
wait(0.25) {
//then reveal the current lines
var index = Int(Double(target) / (self.canvas.width * Double(gapBetweenSigns)))
//if the "index" is 13 (i.e. that bit that overlaps) then make sure the index is 0
//0 is the same set of lines as 13
if index == 12 { index = 0 }
self.signLines.currentIndex = index
self.signLines.revealCurrentSignLines()
}
return
}
}
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
//any time the view slows down and stops on its own
snapIfNeeded(scrollView.contentOffset.x, scrollView)
}
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//if the user has ended dragging and the view doesn't move (e.g. touch, drag, stop, release)
if decelerate == false {
snapIfNeeded(scrollView.contentOffset.x, scrollView)
}
}
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
self.signLines.hideCurrentSignLines()
}
//MARK: Go To
func goto(selection: Int) {
//figure out the target location
let target = canvas.width * Double(gapBetweenSigns) * Double(selection)
//animate the bigStars layer to the target
let anim = ViewAnimation(duration: 3.0) { () -> Void in
self.bigStars.contentOffset = CGPoint(x: CGFloat(target),y: 0)
}
anim.curve = .EaseOut
anim.addCompletionObserver { () -> Void in
self.signLines.revealCurrentSignLines()
}
anim.animate()
//update the current index
signLines.currentIndex = selection
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment