Chapter 7 in Swift (inc. previous challenges)


#1

Please let me know of any improvements that could be made!

AppDelegate.swift

//
//  AppDelegate.swift
//  Hypnonerd
//
//  Created by adam on 6/16/14.
//  Copyright (c) 2014 Adam Schoonmaker. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
                            
    var window: UIWindow?

    // At the beginning of this implementation, HypnosisViewController is asked
    // for its view. Since it has just been created, its "view" is nil, so it is
    // send the "loadView" message that creates te "view" member
    // The HypnosisViewController is being used to present the HypnosisView,
    // instead of adding the view object itself to the window
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        // Override point for customization after application launch.
        
        // Allows local Alert notifications to be used by this app
        let notificationSettings = UIUserNotificationSettings(forTypes:
            UIUserNotificationType.Alert, categories: nil)
        UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
        
        // Uses the convenience initializer to create an instance with
        // a dummy frame of all 0.0's. Doesn't matter since setting it as 
        // the root view controller will resize it.
        let hvc = HypnosisViewController()
        
        // Gets the object that represents the app bundle
        // The app buncdle is a directory on the filesystem that contains the 
        // apps executable and its resources (like NIB files) that the executable
        // will use
        //let appBundle = NSBundle.mainBundle()
        
        // Look in the appBundle for the file ReminderViewController.xib
        //let rvc = ReminderViewController(nibName: "ReminderViewController",
        //    bundle: appBundle)
        
        // Uses a convenience initializer that passes "nil" to nibName and bundle
        // When passed "nil", it looks in the main bundle for "ReminderViewController.xib"
        let rvc = ReminderViewController()
        
        // Same as for "rvc"
        let qvc = QuizViewController()
        
        // UITabBarController keeps an array of view controllers, and maintains
        // a tab bar at the bottom of the screen w/ a tab for each view controller
        // in the array. Tapping on a tab presents the view of the view controller
        // associated with that tab
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = [hvc, rvc, qvc]
        
        // Adds the view controller's view hierarchy to the window
        // Setting a view controller as the rootViewController adds that view
        // controller's "view" as a subview of the window, and automatically
        // resizes the view to be the same size as the window
        //window!.rootViewController = hvc
        //window!.rootViewController = rvc
        window!.rootViewController = tabBarController
        
        window!.backgroundColor = UIColor.whiteColor()
        window!.makeKeyAndVisible()
        return true
    }
//...
}

HypnosisViewController.swift

[code]//
// HypnosisViewController.swift
// Hypnonerd
//
// Created by adam on 6/16/14.
// Copyright © 2014 Adam Schoonmaker. All rights reserved.
//

import UIKit

class HypnosisViewController: UIViewController, UITextFieldDelegate {

override func loadView() {
    // Create a view
    // Uses convenience initializer to default size of mainScreen bounds
    let backgroundView = HypnosisView()
    
    
    // Make and add a UITextField
    let textFieldRect = CGRectMake(40, 70, 240, 30)
    let textField = UITextField(frame: textFieldRect)
    
    // Setting the border style of the text field makes it easier to see
    textField.borderStyle = UITextBorderStyle.RoundedRect
    textField.placeholder = "Hypnotize me!"
    textField.returnKeyType = UIReturnKeyType.Done
    
    textField.delegate = self
    
    backgroundView.addSubview(textField)
    
    
    // Make and add a UISegmentedControl for controlling the circle color
    let segControl = UISegmentedControl(items: ["Red", "Green", "Blue"])
    segControl.frame = CGRectMake(35, 20, 250, 20)
    
    segControl.addTarget(self, action: "controlCircleColor:", forControlEvents: UIControlEvents.ValueChanged)
    
    backgroundView.addSubview(segControl)
    
    // Set it as *the* view of this view controller
    view = backgroundView
}

// part of the UITextFieldDelegate protocol; called when any version
// of the "Return" key is pressed
func textFieldShouldReturn(textField: UITextField!) -> Bool {
    drawHypnoticMessage(textField.text)
    
    // reset the text field after done, make keyboard go away
    textField.text = ""
    textField.resignFirstResponder()
    
    return true
}

func drawHypnoticMessage(message: String) {
    for i in 1..20 {
        let messageLabel = UILabel()
        
        // Configure the label's colors and text
        messageLabel.backgroundColor = UIColor.clearColor()
        messageLabel.textColor = UIColor.blackColor()
        messageLabel.text = message
        
        // This method resizes the label, which will be relative to the text
        // that it is displaying
        messageLabel.sizeToFit()
        
        // Get a random x value that fits w/i the hypnosis view's width
        let width = UInt32(view.bounds.size.width - messageLabel.bounds.size.width)
        let x = Int(arc4random() % width)
        
        // Get a random y value that fits w/i the hypnosis view's height
        let height = UInt32(view.bounds.size.height - messageLabel.bounds.size.height)
        let y = Int(arc4random() % height)
        
        // Update the label's frame
        messageLabel.frame.origin = CGPointMake(CGFloat(x), CGFloat(y))
        
        // Add label to the hierarchy
        view.addSubview(messageLabel)
        
        // Add the "parallax" effect
        // x-axis
        var motionEffect = UIInterpolatingMotionEffect(keyPath: "center.x",
            type: UIInterpolatingMotionEffectType.TiltAlongHorizontalAxis)
        motionEffect.minimumRelativeValue = -25
        motionEffect.maximumRelativeValue = 25
        messageLabel.addMotionEffect(motionEffect)
        // y-axis
        motionEffect = UIInterpolatingMotionEffect(keyPath: "center.y",
            type: UIInterpolatingMotionEffectType.TiltAlongVerticalAxis)
        motionEffect.minimumRelativeValue = -25
        motionEffect.maximumRelativeValue = 25
        messageLabel.addMotionEffect(motionEffect)
    }
}

func controlCircleColor(sender: AnyObject) {
    if let segControlSender = sender as? UISegmentedControl {
        let hypnosisView = view as HypnosisView
        switch segControlSender.selectedSegmentIndex {
        case 0:
            hypnosisView.setCircleColor(circleColor: UIColor.redColor())
        case 1:
            hypnosisView.setCircleColor(circleColor: UIColor.greenColor())
        case 2:
            hypnosisView.setCircleColor(circleColor: UIColor.blueColor())
        default:
            println("Error: SegControl selected index > 2")
        }
    }
}

// Shows if the view is utilizing lazy loading or called prematurely
// Override this if the configuration only needs to be done once during
// the run of the app
override func viewDidLoad() {
    // Always call the super implementation of viewDidLoad
    super.viewDidLoad()
    
    println("HypnosisViewController loaded its view")
}

init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
    // Set the tab bar item's title
    tabBarItem.title = "Hypnotize"
    
    // Create a UIImage from a file
    // This will use Hypno@2x.png on retina display devices
    let i = UIImage(named: "Hypno.png")
    
    // Put that image on the tab bar item
    tabBarItem.image = i
}

convenience init() {
    self.init(nibName: nil, bundle: nil)
}

}
[/code]

HypnosisView.swift

[code]//
// HypnosisView.swift
// Hypnosister
//
// Created by adam on 6/13/14.
// Copyright © 2014 Adam Schoonmaker. All rights reserved.
//

import UIKit

class HypnosisView: UIView {

var circleColor = UIColor.lightGrayColor()

func setCircleColor(circleColor color: UIColor) {
    circleColor = color
    setNeedsDisplay()
}

init(frame: CGRect) {
    super.init(frame: frame)
    // Initialization code
    
    // All HypnosisViews start with a clear background color
    // (a view's backgroundColor is drawn regardless of what drawRect does,
    // so often custom views are set to have a transparent background so that
    // only the results of drawRect show
    backgroundColor = UIColor.clearColor()
}

// Simply creates an instance for being automatically resized
convenience init() {
    self.init(frame: CGRectMake(0.0, 0.0, 0.0, 0.0))
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
    // First thing to do when overriding this: get the bounds
    let bounds = self.bounds
    
    // Figure out the center of the bounds rectangle
    let center = CGPoint(x: (bounds.origin.x + bounds.size.width / 2.0),
        y: (bounds.origin.y + bounds.size.height / 2.0))
    
    // The largest circle will circumscribe the view
    let maxRadius = CGFloat(hypot(Double(bounds.size.width), Double(bounds.size.height)) / 2.0)

    // Draw the circle using UIBezierPath; draws lines and curves to make shapes
    let path = UIBezierPath()
    
    // Could have multiple instances of UIBezierPath, each one being one circle, 
    // or add multiple circles to a single instance. slightly more effcient w/ single instance
    for var currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20 {
        
        // "pick up" the "pencil" and move it to the correct starting spot
        path.moveToPoint(CGPointMake(center.x + currentRadius, center.y))
        
        path.addArcWithCenter(center, radius: currentRadius, startAngle: 0,
            endAngle: CGFloat(M_PI * 2.0), clockwise: true)

        //println("drawing circle... x:\(center.x)   y:\(center.y)   radius:\(currentRadius)")
    }
    
    // Make the line width 10 points
    path.lineWidth = 10
    
    // Sets the color of subsequent stroke operations to the color that the receiver represents
    circleColor.setStroke()
    
    // Draw the line!
    path.stroke()
    
    // Shadows cannot be "unset", so the current context must be saved and
    // restored from after the shawdow is used
    var baseContext = UIGraphicsGetCurrentContext()
    CGContextSaveGState(baseContext)
    
    // Calculate values to use for drawing the logo
    let logoRectWidth = bounds.size.width / 2.0
    let logoRectHeight = bounds.size.height / 2.0
    let logoRectOriginX = logoRectWidth / 2.0
    let logoRectOriginY = logoRectHeight / 2.0
    
    // Draw a triangle path using the logo calculated values
    let triangleTop = CGPointMake(logoRectWidth, logoRectOriginY - 20.0)
    let triangleBottomLeft = CGPointMake(logoRectOriginX - 10.0,
        logoRectOriginY + logoRectHeight + 20.0)
    let triangleBottomRight = CGPointMake(logoRectOriginX + logoRectWidth + 10.0,
        logoRectOriginY + logoRectHeight + 20.0)
    let triangleBottomMiddle = CGPointMake(logoRectWidth,
        logoRectOriginY + logoRectHeight + 20.0)
    
    // Set "pencil" down at the top of the triangle, then draw the edges
    let trianglePath = UIBezierPath()
    trianglePath.moveToPoint(triangleTop)
    trianglePath.addLineToPoint(triangleBottomLeft)
    trianglePath.addLineToPoint(triangleBottomRight)
    trianglePath.addLineToPoint(triangleTop)
    //trianglePath.stroke()
    
    // Use the triangle path to draw a gradient
    // Gradients cover everything in the view, so a clipping path must be 
    // installed on the graphics context that defines what the gradient covers
    trianglePath.addClip()
    
    // Create the gradient
    let locations: CGFloat[] = [0.0, 1.0]
    let components: CGFloat[] = [ 0.0, 1.0, 0.0, 1.0, // Start color: green
                                    1.0, 1.0, 0.0, 1.0] // End color: yellow
    var colorspace = CGColorSpaceCreateDeviceRGB()
    var gradient = CGGradientCreateWithColorComponents(colorspace, components,
        locations, 2)
    
    // Draw the gradient
    CGContextDrawLinearGradient(baseContext, gradient, triangleTop,
        triangleBottomMiddle, 0)
    
    // Deallocate (note: make sure gradient and colorspace are defined with
    // "var" instead of "let", or else this will give an error
    //CGGradientRelease(gradient)
    //CGColorSpaceRelease(colorspace)
    
    // Restore the graphics state to clear the clip path
    CGContextRestoreGState(baseContext)

    baseContext = UIGraphicsGetCurrentContext()
    CGContextSaveGState(baseContext)
    // Now anything drawn will appear with a shadow
    CGContextSetShadow(baseContext, CGSizeMake(4, 7), 3)
    
    // Create UIImage object from logo.png imported file, draw it over circles
    let logoImage = UIImage(named: "logo.png")
    
    logoImage.drawInRect(CGRect(x: logoRectOriginX, y: logoRectOriginY,
        width: logoRectWidth, height: logoRectHeight))
    
    // Now anything drawn won't have a shadow
    CGContextRestoreGState(baseContext)
}


// When a finger touches the view; this is a touch event handler
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
    println("\(self) was touched")
    
    // If the segmented controller is set, deselect all segments
    let rvc = UIApplication.sharedApplication().delegate.window!.rootViewController
    if let tvc = rvc as? UITabBarController {
        if let hvc = tvc.viewControllers[0] as? HypnosisViewController {
            for view : AnyObject in hvc.view.subviews {
                if view is UISegmentedControl {
                    let segControl = view as UISegmentedControl
                    if segControl.selectedSegmentIndex == 0 ||
                       segControl.selectedSegmentIndex == 1 ||
                       segControl.selectedSegmentIndex == 2
                    {
                        println("Reset all segments")
                        segControl.selectedSegmentIndex = UISegmentedControlNoSegment
                    }
                }
            }
        }
    }
    
    // Get 3 random numbers between 0 and 1
    let red = CGFloat(arc4random() % 100) / 100.0
    let green = CGFloat(arc4random() % 100) / 100.0
    let blue = CGFloat(arc4random() % 100) / 100.0
    
    let randomColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    
    setCircleColor(circleColor: randomColor)
}

}
[/code]