├── 1.gif ├── 2.gif ├── 3.gif ├── 4.gif ├── 5.gif ├── 6.gif ├── Assignment 1 ├── Calculator │ ├── Calculator.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── Calculator.xcscheme │ │ │ └── xcschememanagement.plist │ ├── Calculator │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── CalculatorBrain.swift │ │ ├── Constants.swift │ │ ├── Info.plist │ │ └── ViewController.swift │ └── CalculatorTests │ │ ├── CalculatorTests.swift │ │ └── Info.plist ├── README.md └── calculator.gif ├── Assignment 2 ├── Calculator │ ├── Calculator.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── Calculator.xcscheme │ │ │ └── xcschememanagement.plist │ ├── Calculator │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── CalculatorBrain.swift │ │ ├── Constants.swift │ │ ├── Info.plist │ │ └── ViewController.swift │ └── CalculatorTests │ │ ├── CalculatorTests.swift │ │ └── Info.plist ├── README.md └── calculator.gif ├── Assignment 3 ├── Calculator │ ├── Calculator.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── Calculator.xcscheme │ │ │ └── xcschememanagement.plist │ ├── Calculator │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AxesDrawer.swift │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── CalculatorBrain.swift │ │ ├── CalculatorViewController.swift │ │ ├── Constants.swift │ │ ├── GraphView.swift │ │ ├── GraphViewController.swift │ │ └── Info.plist │ └── CalculatorTests │ │ ├── CalculatorTests.swift │ │ └── Info.plist ├── README.md └── calculator.gif ├── Assignment 4 ├── README.md ├── Smashtag Mentions.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── linou.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── Smashtag │ ├── Smashtag.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── Smashtag.xcscheme │ │ │ └── xcschememanagement.plist │ └── Smashtag │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── ImageTableViewCell.swift │ │ ├── ImageViewController.swift │ │ ├── Info.plist │ │ ├── MentionsTableViewController.swift │ │ ├── RecentQueries.swift │ │ ├── RecentQueriesTableViewController.swift │ │ ├── TweetTableViewCell.swift │ │ └── TweetTableViewController.swift ├── Twitter │ ├── Twitter.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── Paul.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ ├── Paul.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── Twitter.xcscheme │ │ │ │ └── xcschememanagement.plist │ │ │ ├── cs193p.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── Twitter.xcscheme │ │ │ │ └── xcschememanagement.plist │ │ │ └── linou.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── Twitter.xcscheme │ │ │ └── xcschememanagement.plist │ └── Twitter │ │ ├── Info.plist │ │ ├── MediaItem.swift │ │ ├── Request.swift │ │ ├── Tweet.swift │ │ ├── Twitter.h │ │ └── User.swift └── smashtag.gif ├── Assignment 5 ├── README.md ├── Smashtag Mention Popularity.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── linou.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── Smashtag │ ├── Smashtag.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── Smashtag.xcscheme │ │ │ └── xcschememanagement.plist │ └── Smashtag │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── CoreDataTableViewController.swift │ │ ├── CountedMentionsTVC.swift │ │ ├── ImageTableViewCell.swift │ │ ├── ImageViewController.swift │ │ ├── Info.plist │ │ ├── Mention+CoreDataProperties.swift │ │ ├── Mention.swift │ │ ├── MentionsTableViewController.swift │ │ ├── Model.xcdatamodeld │ │ └── Model.xcdatamodel │ │ │ └── contents │ │ ├── Query+CoreDataProperties.swift │ │ ├── Query.swift │ │ ├── RecentQueries.swift │ │ ├── RecentQueriesTableViewController.swift │ │ ├── Tweet+CoreDataProperties.swift │ │ ├── Tweet.swift │ │ ├── TweetTableViewCell.swift │ │ └── TweetTableViewController.swift ├── Twitter │ ├── Twitter.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── Paul.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ ├── Paul.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── Twitter.xcscheme │ │ │ │ └── xcschememanagement.plist │ │ │ ├── cs193p.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── Twitter.xcscheme │ │ │ │ └── xcschememanagement.plist │ │ │ └── linou.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── Twitter.xcscheme │ │ │ └── xcschememanagement.plist │ └── Twitter │ │ ├── Info.plist │ │ ├── MediaItem.swift │ │ ├── Request.swift │ │ ├── Tweet.swift │ │ ├── Twitter.h │ │ └── User.swift └── smashtag.gif ├── Assignment 6 ├── Breakout │ ├── Breakout.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── linou.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── linou.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── Breakout.xcscheme │ │ │ └── xcschememanagement.plist │ └── Breakout │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── breakout.imageset │ │ │ ├── Contents.json │ │ │ └── first.pdf │ │ └── settings.imageset │ │ │ ├── Contents.json │ │ │ └── second.pdf │ │ ├── BallView.swift │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── BreakoutBehavior.swift │ │ ├── BreakoutBehaviorDelegate.swift │ │ ├── BreakoutVC.swift │ │ ├── BrickView.swift │ │ ├── Info.plist │ │ ├── Settings.swift │ │ └── SettingsTVC.swift ├── README.md └── breakout.gif └── README.md /1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/1.gif -------------------------------------------------------------------------------- /2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/2.gif -------------------------------------------------------------------------------- /3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/3.gif -------------------------------------------------------------------------------- /4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/4.gif -------------------------------------------------------------------------------- /5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/5.gif -------------------------------------------------------------------------------- /6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/6.gif -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 1/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Calculator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Calculator.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78B068FE1D3949A500849AED 16 | 17 | primary 18 | 19 | 20 | 78B1390C1D36C8D200EF52BC 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/13/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/CalculatorBrain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorBrain.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/15/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CalculatorBrain { 12 | 13 | private var isPartialResult: Bool { 14 | get { 15 | return pending != nil 16 | } 17 | } 18 | 19 | private var resultAccumulator = 0.0 20 | 21 | var description: String { 22 | get { 23 | if pending == nil { 24 | return descriptionAccumulator 25 | } else { 26 | return pending!.descriptionFunction(pending!.descriptionOperand, 27 | pending!.descriptionOperand != descriptionAccumulator ? descriptionAccumulator : "") 28 | } 29 | } 30 | } 31 | 32 | private var descriptionAccumulator = "0" { 33 | didSet { 34 | if pending == nil { 35 | currentPrecedence = Precedence.Max 36 | } 37 | } 38 | } 39 | 40 | private var currentPrecedence = Precedence.Max 41 | 42 | func clear() { 43 | pending = nil 44 | resultAccumulator = 0.0 45 | descriptionAccumulator = "0" 46 | } 47 | 48 | func setOperand(operand: Double) { 49 | resultAccumulator = operand 50 | descriptionAccumulator = String(format:"%g", operand) 51 | } 52 | 53 | private enum Precedence: Int { 54 | case Min = 0, Max 55 | } 56 | 57 | private var operations: Dictionary = [ 58 | "π" : Operation.Constant(M_PI), 59 | "e" : Operation.Constant(M_E), 60 | "±" : Operation.UnaryOperation({ -$0 }, { "-(\($0))"}), 61 | "√" : Operation.UnaryOperation(sqrt, { "√(\($0))"}), 62 | "cos" : Operation.UnaryOperation(cos, { "cos(\($0))"}), 63 | "x⁻¹" : Operation.UnaryOperation({ 1 / $0 }, { "(\($0))⁻1"}), 64 | "x²" : Operation.UnaryOperation({ $0 * $0 }, { "(\($0))²"}), 65 | "×" : Operation.BinaryOperation({ $0 * $1 }, { "\($0) × \($1)"}, Precedence.Max), 66 | "÷" : Operation.BinaryOperation({ $0 / $1 }, { "\($0) ÷ \($1)"}, Precedence.Max), 67 | "+" : Operation.BinaryOperation({ $0 + $1 }, { "\($0) + \($1)"}, Precedence.Min), 68 | "−" : Operation.BinaryOperation({ $0 - $1 }, { "\($0) - \($1)"}, Precedence.Min), 69 | "rand" : Operation.NullaryOperation( { Double(arc4random()) }, "arc4random()"), 70 | "=" : Operation.Equals 71 | ] 72 | 73 | private enum Operation { 74 | case Constant(Double) 75 | case UnaryOperation((Double) -> Double, (String) -> String) 76 | case BinaryOperation((Double, Double) -> Double, (String, String) -> String, Precedence) 77 | case NullaryOperation(() -> Double, String) 78 | case Equals 79 | } 80 | 81 | func performOperation(symbol: String) { 82 | if let operation = operations[symbol] { 83 | switch operation { 84 | case .Constant(let value): 85 | resultAccumulator = value 86 | descriptionAccumulator = symbol 87 | case .NullaryOperation(let function, let descriptionValue): 88 | resultAccumulator = function() 89 | descriptionAccumulator = descriptionValue 90 | case .UnaryOperation(let resultFunction, let descriptionFunction): 91 | resultAccumulator = resultFunction(resultAccumulator) 92 | descriptionAccumulator = descriptionFunction(descriptionAccumulator) 93 | case .BinaryOperation(let resultFunction, let descriptionFunction, let precedence): 94 | executePendingBinaryOperation() 95 | if currentPrecedence.rawValue < precedence.rawValue { 96 | descriptionAccumulator = "(\(descriptionAccumulator))" 97 | } 98 | currentPrecedence = precedence 99 | pending = PendingBinaryOperationInfo(binaryFunction: resultFunction, firstOperand: resultAccumulator, 100 | descriptionFunction: descriptionFunction, descriptionOperand: descriptionAccumulator) 101 | case .Equals: 102 | executePendingBinaryOperation() 103 | } 104 | } 105 | } 106 | 107 | private func executePendingBinaryOperation() { 108 | if pending != nil { 109 | resultAccumulator = pending!.binaryFunction(pending!.firstOperand, resultAccumulator) 110 | descriptionAccumulator = pending!.descriptionFunction(pending!.descriptionOperand, descriptionAccumulator) 111 | pending = nil 112 | } 113 | } 114 | 115 | private var pending: PendingBinaryOperationInfo? 116 | 117 | private struct PendingBinaryOperationInfo { 118 | var binaryFunction: (Double, Double) -> Double 119 | var firstOperand: Double 120 | var descriptionFunction: (String, String) -> String 121 | var descriptionOperand: String 122 | } 123 | 124 | var result: Double { 125 | get { 126 | return resultAccumulator 127 | } 128 | } 129 | 130 | func getDescription() -> String { 131 | let whitespace = (description.hasSuffix(" ") ? "" : " ") 132 | return isPartialResult ? (description + whitespace + "...") : (description + whitespace + "=") 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/17/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | static let numberOfDigitsAfterDecimalPoint = 6 13 | } 14 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/Calculator/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/13/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController 12 | { 13 | @IBOutlet private weak var display: UILabel! 14 | @IBOutlet weak var descriptionLabel: UILabel! 15 | 16 | private var userIsInTheMiddleOfTyping = false 17 | 18 | @IBAction private func touchDigit(sender: UIButton) { 19 | let digit = sender.currentTitle! 20 | if userIsInTheMiddleOfTyping { 21 | let textCurrentlyInDisplay = display.text! 22 | if digit != "." || textCurrentlyInDisplay.rangeOfString(".") == nil { 23 | display.text = textCurrentlyInDisplay + digit 24 | } 25 | } else { 26 | if digit == "." { 27 | display.text = "0." 28 | } else { 29 | display.text = digit 30 | } 31 | userIsInTheMiddleOfTyping = true 32 | } 33 | } 34 | 35 | @IBAction func clear(sender: UIButton) { 36 | brain.clear() 37 | displayValue = 0 38 | userIsInTheMiddleOfTyping = false 39 | } 40 | 41 | @IBAction func backspace(sender: UIButton) { 42 | guard userIsInTheMiddleOfTyping == true else { 43 | return 44 | } 45 | 46 | guard var number = display.text else { 47 | return 48 | } 49 | 50 | number.removeAtIndex(number.endIndex.predecessor()) 51 | if number.isEmpty { 52 | number = "0" 53 | userIsInTheMiddleOfTyping = false 54 | } 55 | display.text = number 56 | } 57 | 58 | private var displayValue: Double? { 59 | get { 60 | if let text = display.text, value = NSNumberFormatter().numberFromString(text)?.doubleValue { 61 | return value 62 | } 63 | return nil 64 | } 65 | set { 66 | if let value = newValue { 67 | let formatter = NSNumberFormatter() 68 | formatter.numberStyle = .DecimalStyle 69 | formatter.maximumFractionDigits = Constants.numberOfDigitsAfterDecimalPoint 70 | display.text = formatter.stringFromNumber(value) 71 | descriptionLabel.text = brain.getDescription() 72 | } else { 73 | display.text = "0" 74 | descriptionLabel.text = " " 75 | userIsInTheMiddleOfTyping = false 76 | } 77 | 78 | } 79 | } 80 | 81 | private var brain = CalculatorBrain() 82 | 83 | @IBAction private func performOperation(sender: UIButton) { 84 | if userIsInTheMiddleOfTyping { 85 | brain.setOperand(displayValue!) 86 | userIsInTheMiddleOfTyping = false 87 | } 88 | 89 | if let mathematicalSymbol = sender.currentTitle { 90 | brain.performOperation(mathematicalSymbol) 91 | } 92 | 93 | displayValue = brain.result 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/CalculatorTests/CalculatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorTests.swift 3 | // CalculatorTests 4 | // 5 | // Created by Kanstantsin Linou on 7/15/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | class CalculatorTests: XCTestCase { 11 | 12 | override func setUp() { 13 | super.setUp() 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDown() { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | super.tearDown() 20 | } 21 | 22 | func testExample() { 23 | 24 | // a. touching 7 + would show “7 + ...” (with 7 still in the display) 25 | let brain = CalculatorBrain() 26 | brain.setOperand(7) 27 | brain.performOperation("+") 28 | XCTAssertEqual(brain.getDescription(), "7 + ...") 29 | XCTAssertEqual(brain.result, 7.0) 30 | 31 | // b. 7 + 9 would show “7 + ...” (9 in the display) 32 | // brain.setOperand(9) // entered but not pushed to model 33 | XCTAssertEqual(brain.getDescription(), "7 + ...") 34 | XCTAssertEqual(brain.result, 7.0) 35 | 36 | // c. 7 + 9 = would show “7 + 9 =” (16 in the display) 37 | brain.setOperand(9) 38 | brain.performOperation("=") 39 | XCTAssertEqual(brain.getDescription(), "7 + 9 =") 40 | XCTAssertEqual(brain.result, 16.0) 41 | 42 | // d. 7 + 9 = √ would show “√(7 + 9) =” (4 in the display) 43 | brain.performOperation("√") 44 | XCTAssertEqual(brain.getDescription(), "√(7 + 9) =") 45 | XCTAssertEqual(brain.result, 4.0) 46 | 47 | // e. 7 + 9 √ would show “7 + √(9) ...” (3 in the display) 48 | brain.setOperand(7) 49 | brain.performOperation("+") 50 | brain.setOperand(9) 51 | brain.performOperation("√") 52 | XCTAssertEqual(brain.getDescription(), "7 + √(9) ...") 53 | XCTAssertEqual(brain.result, 3.0) 54 | 55 | // f. 7 + 9 √ = would show “7 + √(9) =“ (10 in the display) 56 | brain.performOperation("=") 57 | XCTAssertEqual(brain.getDescription(), "7 + √(9) =") 58 | XCTAssertEqual(brain.result, 10.0) 59 | 60 | // g. 7 + 9 = + 6 + 3 = would show “7 + 9 + 6 + 3 =” (25 in the display) 61 | brain.setOperand(7) 62 | brain.performOperation("+") 63 | brain.setOperand(9) 64 | brain.performOperation("=") 65 | brain.performOperation("+") 66 | brain.setOperand(6) 67 | brain.performOperation("+") 68 | brain.setOperand(3) 69 | brain.performOperation("=") 70 | XCTAssertEqual(brain.getDescription(), "7 + 9 + 6 + 3 =") 71 | XCTAssertEqual(brain.result, 25.0) 72 | 73 | // h. 7 + 9 = √ 6 + 3 = would show “6 + 3 =” (9 in the display) 74 | brain.setOperand(7) 75 | brain.performOperation("+") 76 | brain.setOperand(9) 77 | brain.performOperation("=") 78 | brain.performOperation("√") 79 | brain.setOperand(6) 80 | brain.performOperation("+") 81 | brain.setOperand(3) 82 | brain.performOperation("=") 83 | XCTAssertEqual(brain.getDescription(), "6 + 3 =") 84 | XCTAssertEqual(brain.result, 9.0) 85 | 86 | // i. 5 + 6 = 7 3 would show “5 + 6 =” (73 in the display) 87 | brain.setOperand(5) 88 | brain.performOperation("+") 89 | brain.setOperand(6) 90 | brain.performOperation("=") 91 | //brain.setOperand(73) // entered but not pushed to model 92 | XCTAssertEqual(brain.getDescription(), "5 + 6 =") 93 | XCTAssertEqual(brain.result, 11.0) 94 | 95 | // j. 7 + = would show “7 + 7 =” (14 in the display) 96 | brain.setOperand(7) 97 | brain.performOperation("+") 98 | brain.performOperation("=") 99 | XCTAssertEqual(brain.getDescription(), "7 + 7 =") 100 | XCTAssertEqual(brain.result, 14.0) 101 | 102 | // k. 4 × π = would show “4 × π =“ (12.5663706143592 in the display) 103 | brain.setOperand(4) 104 | brain.performOperation("×") 105 | brain.performOperation("π") 106 | brain.performOperation("=") 107 | XCTAssertEqual(brain.getDescription(), "4 × π =") 108 | XCTAssertTrue(abs(brain.result - 12.5663706143592) < 0.001) 109 | 110 | // m. 4 + 5 × 3 = could also show “(4 + 5) × 3 =” if you prefer (27 in the display) 111 | brain.setOperand(4) 112 | brain.performOperation("+") 113 | brain.setOperand(5) 114 | brain.performOperation("×") 115 | brain.setOperand(3) 116 | brain.performOperation("=") 117 | XCTAssertEqual(brain.getDescription(), "(4 + 5) × 3 =") 118 | XCTAssertEqual(brain.result, 27.0) 119 | 120 | } 121 | func testPerformanceExample() { 122 | // This is an example of a performance test case. 123 | self.measureBlock { 124 | // Put the code you want to measure the time of here. 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /Assignment 1/Calculator/CalculatorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assignment 1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1: Calculator 2 | 3 | The goal of this assignment is to recreate the demonstration given in lecture and then make some small enhancements. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%201/calculator.gif) 6 | -------------------------------------------------------------------------------- /Assignment 1/calculator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 1/calculator.gif -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 2/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Calculator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Calculator.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78B068FE1D3949A500849AED 16 | 17 | primary 18 | 19 | 20 | 78B1390C1D36C8D200EF52BC 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/13/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/17/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | struct Math { 13 | static let numberOfDigitsAfterDecimalPoint = 6 14 | static let variableName = "M" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/Calculator/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/13/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController 12 | { 13 | @IBOutlet private weak var display: UILabel! 14 | @IBOutlet weak var descriptionLabel: UILabel! 15 | 16 | private var userIsInTheMiddleOfTyping = false 17 | 18 | private func updateUI() { 19 | descriptionLabel.text = (brain.description.isEmpty ? " " : brain.getDescription()) 20 | displayValue = brain.result 21 | } 22 | 23 | @IBAction private func touchDigit(sender: UIButton) { 24 | let digit = sender.currentTitle! 25 | if userIsInTheMiddleOfTyping { 26 | let textCurrentlyInDisplay = display.text! 27 | if digit != "." || textCurrentlyInDisplay.rangeOfString(".") == nil { 28 | display.text = textCurrentlyInDisplay + digit 29 | } 30 | } else { 31 | if digit == "." { 32 | display.text = "0." 33 | } else { 34 | display.text = digit 35 | } 36 | userIsInTheMiddleOfTyping = true 37 | } 38 | } 39 | 40 | @IBAction func clear(sender: UIButton) { 41 | brain.clear() 42 | displayValue = 0 43 | userIsInTheMiddleOfTyping = false 44 | } 45 | 46 | var savedProgram: CalculatorBrain.PropertyList? 47 | @IBAction func save() { 48 | savedProgram = brain.program 49 | } 50 | 51 | @IBAction func restore() { 52 | if savedProgram != nil { 53 | brain.program = savedProgram! 54 | displayValue = brain.result 55 | } 56 | } 57 | 58 | @IBAction func backspace(sender: UIButton) { 59 | guard userIsInTheMiddleOfTyping == true else { 60 | brain.undo() 61 | updateUI() 62 | return 63 | } 64 | 65 | guard var number = display.text else { 66 | return 67 | } 68 | 69 | number.removeAtIndex(number.endIndex.predecessor()) 70 | if number.isEmpty { 71 | number = "0" 72 | userIsInTheMiddleOfTyping = false 73 | } 74 | display.text = number 75 | } 76 | 77 | private var displayValue: Double? { 78 | get { 79 | if let text = display.text, value = NSNumberFormatter().numberFromString(text)?.doubleValue { 80 | return value 81 | } 82 | return nil 83 | } 84 | set { 85 | if let value = newValue { 86 | let formatter = NSNumberFormatter() 87 | formatter.numberStyle = .DecimalStyle 88 | formatter.maximumFractionDigits = Constants.Math.numberOfDigitsAfterDecimalPoint 89 | display.text = formatter.stringFromNumber(value) 90 | descriptionLabel.text = brain.getDescription() 91 | } else { 92 | display.text = "0" 93 | descriptionLabel.text = " " 94 | userIsInTheMiddleOfTyping = false 95 | } 96 | 97 | } 98 | } 99 | 100 | private var brain = CalculatorBrain() 101 | 102 | @IBAction private func performOperation(sender: UIButton) { 103 | if userIsInTheMiddleOfTyping { 104 | brain.setOperand(displayValue!) 105 | userIsInTheMiddleOfTyping = false 106 | } 107 | if let mathematicalSymbol = sender.currentTitle { 108 | brain.performOperation(mathematicalSymbol) 109 | } 110 | updateUI() 111 | } 112 | 113 | @IBAction func setVariable() { 114 | brain.variableValues[Constants.Math.variableName] = displayValue 115 | if userIsInTheMiddleOfTyping { 116 | userIsInTheMiddleOfTyping = false 117 | } else { 118 | brain.undo() 119 | } 120 | // Trick with a computed property 121 | brain.program = brain.program 122 | updateUI() 123 | } 124 | 125 | 126 | @IBAction func getVariable() { 127 | brain.setOperand(Constants.Math.variableName) 128 | userIsInTheMiddleOfTyping = false 129 | updateUI() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/CalculatorTests/CalculatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorTests.swift 3 | // CalculatorTests 4 | // 5 | // Created by Kanstantsin Linou on 7/15/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | class CalculatorTests: XCTestCase { 11 | 12 | override func setUp() { 13 | super.setUp() 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDown() { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | super.tearDown() 20 | } 21 | 22 | func testAssignment1() { 23 | 24 | // a. touching 7 + would show “7 + ...” (with 7 still in the display) 25 | let brain = CalculatorBrain() 26 | brain.setOperand(7) 27 | brain.performOperation("+") 28 | XCTAssertEqual(brain.getDescription(), "7 + ...") 29 | XCTAssertEqual(brain.result, 7.0) 30 | 31 | // b. 7 + 9 would show “7 + ...” (9 in the display) 32 | // brain.setOperand(9) // entered but not pushed to model 33 | XCTAssertEqual(brain.getDescription(), "7 + ...") 34 | XCTAssertEqual(brain.result, 7.0) 35 | 36 | // c. 7 + 9 = would show “7 + 9 =” (16 in the display) 37 | brain.setOperand(9) 38 | brain.performOperation("=") 39 | XCTAssertEqual(brain.getDescription(), "7 + 9 =") 40 | XCTAssertEqual(brain.result, 16.0) 41 | 42 | // d. 7 + 9 = √ would show “√(7 + 9) =” (4 in the display) 43 | brain.performOperation("√") 44 | XCTAssertEqual(brain.getDescription(), "√(7 + 9) =") 45 | XCTAssertEqual(brain.result, 4.0) 46 | 47 | // e. 7 + 9 √ would show “7 + √(9) ...” (3 in the display) 48 | brain.setOperand(7) 49 | brain.performOperation("+") 50 | brain.setOperand(9) 51 | brain.performOperation("√") 52 | XCTAssertEqual(brain.getDescription(), "7 + √(9) ...") 53 | XCTAssertEqual(brain.result, 3.0) 54 | 55 | // f. 7 + 9 √ = would show “7 + √(9) =“ (10 in the display) 56 | brain.performOperation("=") 57 | XCTAssertEqual(brain.getDescription(), "7 + √(9) =") 58 | XCTAssertEqual(brain.result, 10.0) 59 | 60 | // g. 7 + 9 = + 6 + 3 = would show “7 + 9 + 6 + 3 =” (25 in the display) 61 | brain.setOperand(7) 62 | brain.performOperation("+") 63 | brain.setOperand(9) 64 | brain.performOperation("=") 65 | brain.performOperation("+") 66 | brain.setOperand(6) 67 | brain.performOperation("+") 68 | brain.setOperand(3) 69 | brain.performOperation("=") 70 | XCTAssertEqual(brain.getDescription(), "7 + 9 + 6 + 3 =") 71 | XCTAssertEqual(brain.result, 25.0) 72 | 73 | // h. 7 + 9 = √ 6 + 3 = would show “6 + 3 =” (9 in the display) 74 | brain.setOperand(7) 75 | brain.performOperation("+") 76 | brain.setOperand(9) 77 | brain.performOperation("=") 78 | brain.performOperation("√") 79 | brain.setOperand(6) 80 | brain.performOperation("+") 81 | brain.setOperand(3) 82 | brain.performOperation("=") 83 | XCTAssertEqual(brain.getDescription(), "6 + 3 =") 84 | XCTAssertEqual(brain.result, 9.0) 85 | 86 | // i. 5 + 6 = 7 3 would show “5 + 6 =” (73 in the display) 87 | brain.setOperand(5) 88 | brain.performOperation("+") 89 | brain.setOperand(6) 90 | brain.performOperation("=") 91 | //brain.setOperand(73) // entered but not pushed to model 92 | XCTAssertEqual(brain.getDescription(), "5 + 6 =") 93 | XCTAssertEqual(brain.result, 11.0) 94 | 95 | // j. 7 + = would show “7 + 7 =” (14 in the display) 96 | brain.setOperand(7) 97 | brain.performOperation("+") 98 | brain.performOperation("=") 99 | XCTAssertEqual(brain.getDescription(), "7 + 7 =") 100 | XCTAssertEqual(brain.result, 14.0) 101 | 102 | // k. 4 × π = would show “4 × π =“ (12.5663706143592 in the display) 103 | brain.setOperand(4) 104 | brain.performOperation("×") 105 | brain.performOperation("π") 106 | brain.performOperation("=") 107 | XCTAssertEqual(brain.getDescription(), "4 × π =") 108 | XCTAssertTrue(abs(brain.result - 12.5663706143592) < 0.001) 109 | 110 | // m. 4 + 5 × 3 = could also show “(4 + 5) × 3 =” if you prefer (27 in the display) 111 | brain.setOperand(4) 112 | brain.performOperation("+") 113 | brain.setOperand(5) 114 | brain.performOperation("×") 115 | brain.setOperand(3) 116 | brain.performOperation("=") 117 | XCTAssertEqual(brain.getDescription(), "(4 + 5) × 3 =") 118 | XCTAssertEqual(brain.result, 27.0) 119 | 120 | } 121 | 122 | func testAssignment2() { 123 | 124 | // a. 9 + M = √ ⇒ description is √(9 + M), display is 3 because M is not set (and so is 0) 125 | let brain = CalculatorBrain() 126 | brain.setOperand(9) 127 | brain.performOperation("+") 128 | brain.setOperand("M") 129 | brain.performOperation("=") 130 | brain.performOperation("√") 131 | XCTAssertEqual(brain.getDescription(), "√(9 + M) =") 132 | XCTAssertEqual(brain.result, 3) 133 | } 134 | 135 | func testPerformanceExample() { 136 | // This is an example of a performance test case. 137 | self.measureBlock { 138 | // Put the code you want to measure the time of here. 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /Assignment 2/Calculator/CalculatorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assignment 2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2: Calculator Brain 2 | 3 | The goal of this assignment is to push its capabilities a bit further by allowing a “variable” as an input to the Calculator and support Undo. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%202/calculator.gif) 6 | -------------------------------------------------------------------------------- /Assignment 2/calculator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 2/calculator.gif -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 3/Calculator/Calculator.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Calculator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Calculator.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78B068FE1D3949A500849AED 16 | 17 | primary 18 | 19 | 20 | 78B1390C1D36C8D200EF52BC 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/13/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/17/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | struct Math { 13 | static let numberOfDigitsAfterDecimalPoint = 6 14 | static let variableName = "M" 15 | } 16 | 17 | struct Drawing { 18 | static let pointsPerUnit = 40.0 19 | } 20 | 21 | struct Error { 22 | static let data = "Calculator: DataSource wasn't found" 23 | static let partialResult = "Calculator: Trying to draw a partial result" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/GraphView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphView.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/18/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol GraphViewDataSource { 12 | func getBounds() -> CGRect 13 | func getYCoordinate(x: CGFloat) -> CGFloat? 14 | } 15 | 16 | @IBDesignable 17 | class GraphView: UIView{ 18 | 19 | @IBInspectable 20 | var origin: CGPoint! { didSet { setNeedsDisplay() } } 21 | 22 | @IBInspectable 23 | var scale = CGFloat(Constants.Drawing.pointsPerUnit) { didSet { setNeedsDisplay() } } 24 | 25 | @IBInspectable 26 | var color = UIColor.blackColor() { didSet { setNeedsDisplay() } } 27 | 28 | @IBInspectable 29 | var lineWidth: CGFloat = 1.0 { didSet { setNeedsDisplay() } } 30 | 31 | var dataSource: GraphViewDataSource? 32 | private let drawer = AxesDrawer(color: UIColor.blueColor()) 33 | 34 | // Only override drawRect: if you perform custom drawing. 35 | // An empty implementation adversely affects performance during animation. 36 | override func drawRect(rect: CGRect) { 37 | 38 | // Set default origin to center 39 | origin = origin ?? CGPoint(x: bounds.midX, y: bounds.midY) 40 | 41 | color.set() 42 | pathForFunction().stroke() 43 | 44 | drawer.drawAxesInRect(dataSource?.getBounds() ?? bounds, origin: origin, pointsPerUnit: scale) 45 | } 46 | 47 | private func pathForFunction() -> UIBezierPath { 48 | let path = UIBezierPath() 49 | 50 | guard let data = dataSource else { 51 | NSLog(Constants.Error.data) 52 | return path 53 | } 54 | 55 | var pathIsEmpty = true 56 | var point = CGPoint() 57 | 58 | // Iterate over every pixel (not point) across the width of your view and 59 | // draw a line to (or just “move to” if the last datapoint was not valid) 60 | // the next datapoint you get (if it is valid). 61 | let width = Int(bounds.size.width * scale) 62 | for pixel in 0...width { 63 | point.x = CGFloat(pixel) / scale 64 | 65 | if let y = data.getYCoordinate((point.x - origin.x) / scale) { 66 | 67 | // Do something sensible when graphing discontinuous functions 68 | // only try to draw lines to or from points whose y value .isNormal or .isZero) 69 | if !y.isNormal && !y.isZero { 70 | // Move the path to the next point 71 | pathIsEmpty = true 72 | continue 73 | } 74 | 75 | // As the origin (of the view coordinate system) is upper left and units are points, not pixels 76 | point.y = origin.y - y * scale 77 | 78 | if pathIsEmpty { 79 | // Set the path’s current point before we call this addLineToPoint: method 80 | // If the path is empty, addLineToPoint: does nothing. 81 | path.moveToPoint(point) 82 | pathIsEmpty = false 83 | } else { 84 | path.addLineToPoint(point) 85 | } 86 | } 87 | } 88 | 89 | path.lineWidth = lineWidth 90 | return path 91 | } 92 | 93 | // Move the origin of the graph to the point of the double tap 94 | func doubleTap(recognizer: UITapGestureRecognizer) { 95 | if recognizer.state == .Ended { 96 | origin = recognizer.locationInView(self) 97 | } 98 | } 99 | 100 | func zoom(recognizer: UIPinchGestureRecognizer) { 101 | switch recognizer.state { 102 | case .Changed, .Ended: 103 | scale *= recognizer.scale 104 | recognizer.scale = 1.0 105 | default: break 106 | } 107 | } 108 | 109 | func move(recognizer: UIPanGestureRecognizer) { 110 | switch recognizer.state { 111 | case .Changed: fallthrough 112 | case .Ended: 113 | let translation = recognizer.translationInView(self) 114 | // Update anything that depends on the pan gesture using translation.x and translation.y 115 | origin.x += translation.x 116 | origin.y += translation.y 117 | // Cumulative since start of recognition, get 'incremental' translation 118 | recognizer.setTranslation(CGPointZero, inView: self) 119 | default: break 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/GraphViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphViewController.swift 3 | // Calculator 4 | // 5 | // Created by Kanstantsin Linou on 7/18/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GraphViewController: UIViewController, GraphViewDataSource { 12 | @IBOutlet weak var graphView: GraphView! { 13 | didSet { 14 | graphView.dataSource = self 15 | 16 | // Since we don't need to update the model after recognizing gestures, target is the outlet itself (not view controller) 17 | 18 | // a. Pinching (zooms the entire graph, including the axes, in or out on the graph) 19 | graphView.addGestureRecognizer(UIPinchGestureRecognizer(target: graphView, action: #selector(graphView.zoom(_:)))) 20 | 21 | 22 | // b. Panning (moves the entire graph, including the axes, to follow the touch around) 23 | graphView.addGestureRecognizer(UIPanGestureRecognizer(target: graphView, action: #selector(graphView.move(_:)))) 24 | 25 | // c. Double-tapping (moves the origin of the graph to the point of the double tap) 26 | let recognizer = UITapGestureRecognizer(target: graphView, action: #selector(graphView.doubleTap(_:))) 27 | recognizer.numberOfTapsRequired = 2 // single tap, double tap, etc. 28 | graphView.addGestureRecognizer(recognizer) 29 | } 30 | } 31 | 32 | func getBounds() -> CGRect { 33 | return navigationController?.view.bounds ?? view.bounds 34 | } 35 | 36 | func getYCoordinate(x: CGFloat) -> CGFloat? { 37 | if let function = function { 38 | return CGFloat(function(x)) 39 | } 40 | return nil 41 | } 42 | 43 | var function: (CGFloat -> Double)? 44 | } 45 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/Calculator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/CalculatorTests/CalculatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorTests.swift 3 | // CalculatorTests 4 | // 5 | // Created by Kanstantsin Linou on 7/15/16. 6 | // Copyright © 2016 Kanstantsin Linou. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | class CalculatorTests: XCTestCase { 11 | 12 | override func setUp() { 13 | super.setUp() 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDown() { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | super.tearDown() 20 | } 21 | 22 | func testAssignment1() { 23 | 24 | // a. touching 7 + would show “7 + ...” (with 7 still in the display) 25 | let brain = CalculatorBrain() 26 | brain.setOperand(7) 27 | brain.performOperation("+") 28 | XCTAssertEqual(brain.getDescription(), "7 + ...") 29 | XCTAssertEqual(brain.result, 7.0) 30 | 31 | // b. 7 + 9 would show “7 + ...” (9 in the display) 32 | // brain.setOperand(9) // entered but not pushed to model 33 | XCTAssertEqual(brain.getDescription(), "7 + ...") 34 | XCTAssertEqual(brain.result, 7.0) 35 | 36 | // c. 7 + 9 = would show “7 + 9 =” (16 in the display) 37 | brain.setOperand(9) 38 | brain.performOperation("=") 39 | XCTAssertEqual(brain.getDescription(), "7 + 9 =") 40 | XCTAssertEqual(brain.result, 16.0) 41 | 42 | // d. 7 + 9 = √ would show “√(7 + 9) =” (4 in the display) 43 | brain.performOperation("√") 44 | XCTAssertEqual(brain.getDescription(), "√(7 + 9) =") 45 | XCTAssertEqual(brain.result, 4.0) 46 | 47 | // e. 7 + 9 √ would show “7 + √(9) ...” (3 in the display) 48 | brain.setOperand(7) 49 | brain.performOperation("+") 50 | brain.setOperand(9) 51 | brain.performOperation("√") 52 | XCTAssertEqual(brain.getDescription(), "7 + √(9) ...") 53 | XCTAssertEqual(brain.result, 3.0) 54 | 55 | // f. 7 + 9 √ = would show “7 + √(9) =“ (10 in the display) 56 | brain.performOperation("=") 57 | XCTAssertEqual(brain.getDescription(), "7 + √(9) =") 58 | XCTAssertEqual(brain.result, 10.0) 59 | 60 | // g. 7 + 9 = + 6 + 3 = would show “7 + 9 + 6 + 3 =” (25 in the display) 61 | brain.setOperand(7) 62 | brain.performOperation("+") 63 | brain.setOperand(9) 64 | brain.performOperation("=") 65 | brain.performOperation("+") 66 | brain.setOperand(6) 67 | brain.performOperation("+") 68 | brain.setOperand(3) 69 | brain.performOperation("=") 70 | XCTAssertEqual(brain.getDescription(), "7 + 9 + 6 + 3 =") 71 | XCTAssertEqual(brain.result, 25.0) 72 | 73 | // h. 7 + 9 = √ 6 + 3 = would show “6 + 3 =” (9 in the display) 74 | brain.setOperand(7) 75 | brain.performOperation("+") 76 | brain.setOperand(9) 77 | brain.performOperation("=") 78 | brain.performOperation("√") 79 | brain.setOperand(6) 80 | brain.performOperation("+") 81 | brain.setOperand(3) 82 | brain.performOperation("=") 83 | XCTAssertEqual(brain.getDescription(), "6 + 3 =") 84 | XCTAssertEqual(brain.result, 9.0) 85 | 86 | // i. 5 + 6 = 7 3 would show “5 + 6 =” (73 in the display) 87 | brain.setOperand(5) 88 | brain.performOperation("+") 89 | brain.setOperand(6) 90 | brain.performOperation("=") 91 | //brain.setOperand(73) // entered but not pushed to model 92 | XCTAssertEqual(brain.getDescription(), "5 + 6 =") 93 | XCTAssertEqual(brain.result, 11.0) 94 | 95 | // j. 7 + = would show “7 + 7 =” (14 in the display) 96 | brain.setOperand(7) 97 | brain.performOperation("+") 98 | brain.performOperation("=") 99 | XCTAssertEqual(brain.getDescription(), "7 + 7 =") 100 | XCTAssertEqual(brain.result, 14.0) 101 | 102 | // k. 4 × π = would show “4 × π =“ (12.5663706143592 in the display) 103 | brain.setOperand(4) 104 | brain.performOperation("×") 105 | brain.performOperation("π") 106 | brain.performOperation("=") 107 | XCTAssertEqual(brain.getDescription(), "4 × π =") 108 | XCTAssertTrue(abs(brain.result - 12.5663706143592) < 0.001) 109 | 110 | // m. 4 + 5 × 3 = could also show “(4 + 5) × 3 =” if you prefer (27 in the display) 111 | brain.setOperand(4) 112 | brain.performOperation("+") 113 | brain.setOperand(5) 114 | brain.performOperation("×") 115 | brain.setOperand(3) 116 | brain.performOperation("=") 117 | XCTAssertEqual(brain.getDescription(), "(4 + 5) × 3 =") 118 | XCTAssertEqual(brain.result, 27.0) 119 | 120 | } 121 | 122 | func testAssignment2() { 123 | 124 | // a. 9 + M = √ ⇒ description is √(9 + M), display is 3 because M is not set (and so is 0) 125 | let brain = CalculatorBrain() 126 | brain.setOperand(9) 127 | brain.performOperation("+") 128 | brain.setOperand("M") 129 | brain.performOperation("=") 130 | brain.performOperation("√") 131 | XCTAssertEqual(brain.getDescription(), "√(9 + M) =") 132 | XCTAssertEqual(brain.result, 3) 133 | } 134 | 135 | func testPerformanceExample() { 136 | // This is an example of a performance test case. 137 | self.measureBlock { 138 | // Put the code you want to measure the time of here. 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /Assignment 3/Calculator/CalculatorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assignment 3/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 3: Graphing Calculator 2 | 3 | The goal of this assignment is to create a graph of the “program” the user has entered which can be zoomed in on and panned around. This app will now work not only on iPhones, but on iPads as well. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%203/calculator.gif) 6 | -------------------------------------------------------------------------------- /Assignment 3/calculator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 3/calculator.gif -------------------------------------------------------------------------------- /Assignment 4/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 4: Smashtag Mentions 2 | 3 | The goal of this assignment is to enhance the Smashtag application that we built in class to give ready-access to hashtags, urls, images and users mentioned in a tweet. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%204/smashtag.gif) 6 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag Mentions.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag Mentions.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 4/Smashtag Mentions.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 4/Smashtag Mentions.xcworkspace/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 4/Smashtag/Smashtag.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Smashtag.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Smashtag.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 7836D3401D3FAAE800072210 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/ImageTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTableViewCell.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var tweetImage: UIImageView! 14 | @IBOutlet weak var spinner: UIActivityIndicatorView! 15 | 16 | var imageUrl: NSURL? { didSet { updateUI() } } 17 | 18 | private func updateUI() { 19 | if let url = imageUrl { 20 | spinner?.startAnimating() 21 | 22 | // NSData(contentsOfURL:) blocks the thread it is called from when invoked with a network url. 23 | // Thus we cannot call it from the main thread. 24 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 25 | let loadedImageData = NSData(contentsOfURL: url) 26 | dispatch_async(dispatch_get_main_queue()) { 27 | if url == self.imageUrl { 28 | if let imageData = loadedImageData { 29 | self.tweetImage?.image = UIImage(data: imageData) 30 | } 31 | self.spinner?.stopAnimating() 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewController.swift 3 | // Cassini 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright © 2016 Stanford University. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageViewController: UIViewController, UIScrollViewDelegate 12 | { 13 | var imageURL: NSURL? { 14 | didSet { 15 | image = nil 16 | if view.window != nil { 17 | fetchImage() 18 | } 19 | } 20 | } 21 | 22 | private func fetchImage() { 23 | if let url = imageURL { 24 | spinner?.startAnimating() 25 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 26 | let loadedImageData = NSData(contentsOfURL: url) 27 | dispatch_async(dispatch_get_main_queue()) { 28 | if url == self.imageURL { 29 | if let imageData = loadedImageData { 30 | self.image = UIImage(data: imageData) 31 | } else { 32 | self.spinner?.stopAnimating() 33 | } 34 | } else { NSLog("\(Error.downloadImage) + \(url)") } 35 | } 36 | } 37 | } 38 | } 39 | 40 | @IBOutlet weak var spinner: UIActivityIndicatorView! 41 | 42 | @IBOutlet weak var scrollView: UIScrollView! { 43 | didSet { 44 | scrollView.contentSize = imageView.frame.size 45 | scrollView.delegate = self 46 | scrollView.minimumZoomScale = 0.03 47 | scrollView.maximumZoomScale = 1.0 48 | } 49 | } 50 | 51 | func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { 52 | return imageView 53 | } 54 | 55 | private var imageView = UIImageView() 56 | 57 | private var scrollViewDidScrollOrZoom = false 58 | 59 | private func autoScale() { 60 | if scrollViewDidScrollOrZoom { 61 | return 62 | } 63 | if let sv = scrollView { 64 | if image != nil { 65 | sv.zoomScale = max(sv.bounds.size.height / image!.size.height, 66 | sv.bounds.size.width / image!.size.width) 67 | sv.contentOffset = CGPoint(x: (imageView.frame.size.width - sv.frame.size.width) / 2, 68 | y: (imageView.frame.size.height - sv.frame.size.height) / 2) 69 | scrollViewDidScrollOrZoom = false 70 | } 71 | } 72 | } 73 | 74 | private var image: UIImage? { 75 | get { return imageView.image } 76 | set { 77 | imageView.image = newValue 78 | imageView.sizeToFit() 79 | scrollView?.contentSize = imageView.frame.size 80 | spinner?.stopAnimating() 81 | scrollViewDidScrollOrZoom = false 82 | autoScale() 83 | } 84 | } 85 | 86 | private struct Error { 87 | static let downloadImage = "Smashtag: couldn't get the data from" 88 | } 89 | 90 | // MARK: View Controller Lifecycle 91 | 92 | override func viewWillAppear(animated: Bool) { 93 | super.viewWillAppear(animated) 94 | if image == nil { 95 | fetchImage() 96 | } 97 | } 98 | 99 | override func viewDidLoad() { 100 | super.viewDidLoad() 101 | scrollView.addSubview(imageView) 102 | } 103 | 104 | override func viewDidLayoutSubviews() { 105 | super.viewDidLayoutSubviews() 106 | autoScale() 107 | } 108 | 109 | func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) { 110 | scrollViewDidScrollOrZoom = true 111 | } 112 | 113 | func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { 114 | scrollViewDidScrollOrZoom = true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | NSAppTransportSecurity 40 | 41 | NSAllowsArbitraryLoads 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/RecentQueries.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecentQueries.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct RecentQueries 12 | { 13 | private static let defaults = NSUserDefaults.standardUserDefaults() 14 | private struct Constants { 15 | static let key = "RecentQueries" 16 | static let limit = 100 17 | } 18 | 19 | static var queries: [String] { 20 | return (defaults.objectForKey(Constants.key) as? [String]) ?? [] 21 | } 22 | 23 | static func add(term: String) { 24 | var newArray = queries.filter({ term.caseInsensitiveCompare($0) != .OrderedSame }) 25 | newArray.insert(term, atIndex: 0) 26 | while newArray.count > Constants.limit { 27 | newArray.removeLast() 28 | } 29 | defaults.setObject(newArray, forKey: Constants.key) 30 | } 31 | 32 | static func removeAtIndex(index: Int) { 33 | var currentQueries = (defaults.objectForKey(Constants.key) as? [String]) ?? [] 34 | currentQueries.removeAtIndex(index) 35 | defaults.setObject(currentQueries, forKey: Constants.key) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/RecentQueriesTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecentQueriesTableViewController.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import UIKit 12 | 13 | class RecentsQueriesTableViewController: UITableViewController { 14 | 15 | // MARK: Model 16 | 17 | var recentQueries: [String] { 18 | return RecentQueries.queries 19 | } 20 | 21 | // MARK: View 22 | 23 | override func viewWillAppear(animated: Bool) { 24 | super.viewWillAppear(false) 25 | tableView.reloadData() 26 | } 27 | 28 | private struct Storyboard { 29 | private static let RecentCell = "Recent Cell" 30 | private static let TweetsSegue = "show recent tweets" 31 | } 32 | 33 | // MARK: - UITableViewDataSource 34 | 35 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 36 | return 1 37 | } 38 | 39 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 40 | return recentQueries.count 41 | } 42 | 43 | override func tableView(tableView: UITableView, 44 | cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 45 | let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.RecentCell, 46 | forIndexPath: indexPath) as UITableViewCell 47 | cell.textLabel?.text = recentQueries[indexPath.row] 48 | return cell 49 | } 50 | 51 | override func tableView(tableView: UITableView, 52 | commitEditingStyle editingStyle: UITableViewCellEditingStyle, 53 | forRowAtIndexPath indexPath: NSIndexPath) { 54 | if editingStyle == .Delete { 55 | RecentQueries.removeAtIndex(indexPath.row) 56 | tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 57 | } 58 | } 59 | 60 | 61 | // MARK: - Navigation 62 | 63 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 64 | 65 | if let identifier = segue.identifier where identifier == Storyboard.TweetsSegue, 66 | let cell = sender as? UITableViewCell, 67 | let ttvc = segue.destinationViewController as? TweetTableViewController { 68 | ttvc.searchText = cell.textLabel?.text 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/TweetTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TweetTableViewCell.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Twitter 11 | 12 | class TweetTableViewCell: UITableViewCell 13 | { 14 | // MARK: Outlets 15 | 16 | @IBOutlet weak var tweetScreenNameLabel: UILabel! 17 | @IBOutlet weak var tweetTextLabel: UILabel! 18 | @IBOutlet weak var tweetProfileImageView: UIImageView! 19 | @IBOutlet weak var tweetCreatedLabel: UILabel! 20 | 21 | // MARK: Model 22 | 23 | var tweet: Twitter.Tweet? { 24 | didSet { 25 | updateUI() 26 | } 27 | } 28 | 29 | private func updateUI() 30 | { 31 | // reset any existing tweet information 32 | tweetTextLabel?.attributedText = nil 33 | tweetScreenNameLabel?.text = nil 34 | tweetProfileImageView?.image = nil 35 | tweetCreatedLabel?.text = nil 36 | 37 | // load new information from our tweet (if any) 38 | if let tweet = self.tweet 39 | { 40 | tweetTextLabel?.attributedText = getColorfulTextLabel(tweet) 41 | tweetScreenNameLabel?.text = "\(tweet.user)" 42 | setProfileImageView(tweet) 43 | tweetCreatedLabel?.text = getCreatedLabel(tweet) 44 | } 45 | } 46 | 47 | private struct Color { 48 | static let user = UIColor.purpleColor() 49 | static let hashtag = UIColor.darkGrayColor() 50 | static let url = UIColor.blueColor() 51 | } 52 | 53 | private func getColorfulTextLabel(tweet: Tweet) -> NSMutableAttributedString { 54 | var text = tweet.text 55 | for _ in tweet.media { 56 | text += " 📷" 57 | } 58 | 59 | // Enhance Smashtag from lecture to highlight (in a different color for each) hashtags, 60 | // urls and user screen names mentioned in the text of each Tweet 61 | let attributedText = NSMutableAttributedString(string: text) 62 | attributedText.setMensionsColor(tweet.hashtags, color: Color.hashtag) 63 | attributedText.setMensionsColor(tweet.urls, color: Color.url) 64 | attributedText.setMensionsColor(tweet.userMentions, color: Color.user) 65 | 66 | return attributedText 67 | } 68 | 69 | private func setProfileImageView(tweet: Tweet) { 70 | guard let profileImageURL = tweet.user.profileImageURL else { 71 | return 72 | } 73 | 74 | // NSData(contentsOfURL:) blocks the thread it is called from when invoked with a network url. 75 | // Thus we cannot call it from the main thread. 76 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 77 | let loadedImageData = NSData(contentsOfURL: profileImageURL) 78 | dispatch_async(dispatch_get_main_queue()) { 79 | if profileImageURL == tweet.user.profileImageURL { 80 | if let imageData = loadedImageData { 81 | self.tweetProfileImageView?.image = UIImage(data: imageData) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | private func getCreatedLabel(tweet: Tweet) -> String { 89 | let formatter = NSDateFormatter() 90 | if NSDate().timeIntervalSinceDate(tweet.created) > 24*60*60 { 91 | formatter.dateStyle = NSDateFormatterStyle.ShortStyle 92 | } else { 93 | formatter.timeStyle = NSDateFormatterStyle.ShortStyle 94 | } 95 | return formatter.stringFromDate(tweet.created) 96 | } 97 | } 98 | 99 | private extension NSMutableAttributedString { 100 | func setMensionsColor(mensions: [Mention], color: UIColor) { 101 | for mension in mensions { 102 | addAttribute(NSForegroundColorAttributeName, value: color, range: mension.nsrange) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Assignment 4/Smashtag/Smashtag/TweetTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TweetTableViewController.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Twitter 11 | 12 | class TweetTableViewController: UITableViewController, UITextFieldDelegate 13 | { 14 | // MARK: Model 15 | 16 | var tweets = [Array]() { 17 | didSet { 18 | tableView.reloadData() 19 | } 20 | } 21 | 22 | var searchText: String? { 23 | didSet { 24 | guard let text = searchText where !text.isEmpty else { 25 | return 26 | } 27 | tweets.removeAll() 28 | lastTwitterRequest = nil 29 | searchForTweets() 30 | title = searchText 31 | RecentQueries.add(text) 32 | } 33 | } 34 | 35 | // MARK: Fetching Tweets 36 | 37 | private var twitterRequest: Twitter.Request? { 38 | if lastTwitterRequest == nil { 39 | if let query = searchText where !query.isEmpty { 40 | return Twitter.Request(search: query + " -filter:retweets", count: Constants.numberOfTweets) 41 | } 42 | } 43 | return lastTwitterRequest?.requestForNewer 44 | } 45 | 46 | private var lastTwitterRequest: Twitter.Request? 47 | 48 | private func searchForTweets() 49 | { 50 | if let request = twitterRequest { 51 | lastTwitterRequest = request 52 | request.fetchTweets { [weak weakSelf = self] newTweets in 53 | dispatch_async(dispatch_get_main_queue()) { 54 | if request == weakSelf?.lastTwitterRequest { 55 | if !newTweets.isEmpty { 56 | weakSelf?.tweets.insert(newTweets, atIndex: 0) 57 | } 58 | } 59 | weakSelf?.refreshControl?.endRefreshing() 60 | } 61 | } 62 | } else { 63 | self.refreshControl?.endRefreshing() 64 | } 65 | } 66 | 67 | @IBAction func refresh(sender: UIRefreshControl) { 68 | searchForTweets() 69 | } 70 | 71 | // MARK: UITableViewDataSource 72 | 73 | override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 74 | return nil 75 | } 76 | 77 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 78 | return tweets.count 79 | } 80 | 81 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 82 | return tweets[section].count 83 | } 84 | 85 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 86 | let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.TweetCellIdentifier, forIndexPath: indexPath) 87 | 88 | let tweet = tweets[indexPath.section][indexPath.row] 89 | if let tweetCell = cell as? TweetTableViewCell { 90 | tweetCell.tweet = tweet 91 | } 92 | return cell 93 | } 94 | 95 | // MARK: Constants 96 | 97 | private struct Storyboard { 98 | static let TweetCellIdentifier = "Tweet" 99 | static let MentionsSegueIdentifier = "show mentions" 100 | } 101 | 102 | private struct Constants { 103 | static let numberOfTweets = 100 104 | } 105 | 106 | // MARK: Outlets 107 | 108 | @IBOutlet weak var searchTextField: UITextField! { 109 | didSet { 110 | searchTextField.delegate = self 111 | searchTextField.text = searchText 112 | } 113 | } 114 | 115 | // MARK: UITextFieldDelegate 116 | 117 | func textFieldShouldReturn(textField: UITextField) -> Bool { 118 | textField.resignFirstResponder() 119 | searchText = textField.text 120 | return true 121 | } 122 | 123 | // MARK: View Controller Lifecycle 124 | 125 | override func viewDidLoad() { 126 | super.viewDidLoad() 127 | tableView.estimatedRowHeight = tableView.rowHeight 128 | tableView.rowHeight = UITableViewAutomaticDimension 129 | } 130 | 131 | // MARK: - Navigation 132 | 133 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 134 | if let identifier = segue.identifier { 135 | if identifier == Storyboard.MentionsSegueIdentifier { 136 | if let mtvc = segue.destinationViewController as? MentionsTableViewController { 137 | if let cell = sender as? TweetTableViewCell { 138 | mtvc.tweet = cell.tweet 139 | } 140 | } 141 | } 142 | } 143 | } 144 | 145 | private func needUpdate(tweet: Tweet) -> Bool { 146 | return !tweet.media.isEmpty || !tweet.hashtags.isEmpty || !tweet.userMentions.isEmpty || !tweet.urls.isEmpty 147 | } 148 | 149 | override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool { 150 | if identifier == Storyboard.MentionsSegueIdentifier { 151 | if let cell = sender as? TweetTableViewCell { 152 | return needUpdate(cell.tweet!) 153 | } 154 | } 155 | return false 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/project.xcworkspace/xcuserdata/Paul.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 4/Twitter/Twitter.xcodeproj/project.xcworkspace/xcuserdata/Paul.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/Paul.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/Paul.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/cs193p.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/cs193p.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter/MediaItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MediaItem.swift 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright (c) 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // holds the network url and aspectRatio of an image attached to a Tweet 12 | // created automatically when a Tweet object is created 13 | 14 | public class MediaItem: NSObject 15 | { 16 | public let url: NSURL 17 | public let aspectRatio: Double 18 | 19 | public override var description: String { return "\(url.absoluteString) (aspect ratio = \(aspectRatio))" } 20 | 21 | // MARK: - Internal Implementation 22 | 23 | init?(data: NSDictionary?) { 24 | guard 25 | let height = data?.valueForKeyPath(TwitterKey.Height) as? Double where height > 0, 26 | let width = data?.valueForKeyPath(TwitterKey.Width) as? Double where width > 0, 27 | let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? String, 28 | let url = NSURL(string: urlString) 29 | else { 30 | return nil 31 | } 32 | self.url = url 33 | self.aspectRatio = width/height 34 | } 35 | 36 | struct TwitterKey { 37 | static let MediaURL = "media_url_https" 38 | static let Width = "sizes.small.w" 39 | static let Height = "sizes.small.h" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter/Twitter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Twitter.h 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright © 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Twitter. 12 | FOUNDATION_EXPORT double TwitterVersionNumber; 13 | 14 | //! Project version string for Twitter. 15 | FOUNDATION_EXPORT const unsigned char TwitterVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | -------------------------------------------------------------------------------- /Assignment 4/Twitter/Twitter/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright (c) 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // container to hold data about a Twitter user 12 | 13 | public class User: NSObject 14 | { 15 | public let screenName: String 16 | public let name: String 17 | public let id: String 18 | public let verified: Bool 19 | public let profileImageURL: NSURL? 20 | 21 | public override var description: String { return "@\(screenName) (\(name))\(verified ? " ✅" : "")" } 22 | 23 | // MARK: - Internal Implementation 24 | 25 | init?(data: NSDictionary?) { 26 | guard 27 | let screenName = data?.valueForKeyPath(TwitterKey.ScreenName) as? String, 28 | let name = data?.valueForKeyPath(TwitterKey.Name) as? String, 29 | let id = data?.valueForKeyPath(TwitterKey.ID) as? String 30 | else { 31 | return nil 32 | } 33 | 34 | self.screenName = screenName 35 | self.name = name 36 | self.id = id 37 | 38 | self.verified = data?.valueForKeyPath(TwitterKey.Verified)?.boolValue ?? false 39 | let urlString = data?.valueForKeyPath(TwitterKey.ProfileImageURL) as? String ?? "" 40 | self.profileImageURL = (urlString.characters.count > 0) ? NSURL(string: urlString) : nil 41 | } 42 | 43 | var asPropertyList: AnyObject { 44 | return [ 45 | TwitterKey.Name:name, 46 | TwitterKey.ScreenName:screenName, 47 | TwitterKey.ID:id, 48 | TwitterKey.Verified:verified ? "YES" : "NO", 49 | TwitterKey.ProfileImageURL:profileImageURL?.absoluteString ?? "" 50 | ] 51 | } 52 | 53 | struct TwitterKey { 54 | static let Name = "name" 55 | static let ScreenName = "screen_name" 56 | static let ID = "id_str" 57 | static let Verified = "verified" 58 | static let ProfileImageURL = "profile_image_url" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Assignment 4/smashtag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 4/smashtag.gif -------------------------------------------------------------------------------- /Assignment 5/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 5: Smashtag Mention Popularity 2 | 3 | The goal of this assignment is to enhance the Smashtag application even further to do some analysis on all of the mentions in a search result using Core Data. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%205/smashtag.gif) 6 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag Mention Popularity.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag Mention Popularity.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 5/Smashtag Mention Popularity.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 5/Smashtag Mention Popularity.xcworkspace/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 5/Smashtag/Smashtag.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Smashtag.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Smashtag.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 7836D3401D3FAAE800072210 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/CoreDataTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataTableViewController.swift 3 | // 4 | // Created by CS193p Instructor. 5 | // Copyright © 2015-16 Stanford University. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | import CoreData 10 | 11 | class CoreDataTableViewController: UITableViewController, NSFetchedResultsControllerDelegate 12 | { 13 | var fetchedResultsController: NSFetchedResultsController? { 14 | didSet { 15 | do { 16 | if let frc = fetchedResultsController { 17 | frc.delegate = self 18 | try frc.performFetch() 19 | } 20 | tableView.reloadData() 21 | } catch let error { 22 | print("NSFetchedResultsController.performFetch() failed: \(error)") 23 | } 24 | } 25 | } 26 | 27 | // MARK: UITableViewDataSource 28 | 29 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 30 | return fetchedResultsController?.sections?.count ?? 1 31 | } 32 | 33 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | if let sections = fetchedResultsController?.sections where sections.count > 0 { 35 | return sections[section].numberOfObjects 36 | } else { 37 | return 0 38 | } 39 | } 40 | 41 | override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 42 | if let sections = fetchedResultsController?.sections where sections.count > 0 { 43 | return sections[section].name 44 | } else { 45 | return nil 46 | } 47 | } 48 | 49 | override func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { 50 | return fetchedResultsController?.sectionIndexTitles 51 | } 52 | 53 | override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int { 54 | return fetchedResultsController?.sectionForSectionIndexTitle(title, atIndex: index) ?? 0 55 | } 56 | 57 | // MARK: NSFetchedResultsControllerDelegate 58 | 59 | func controllerWillChangeContent(controller: NSFetchedResultsController) { 60 | tableView.beginUpdates() 61 | } 62 | 63 | func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 64 | switch type { 65 | case .Insert: tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) 66 | case .Delete: tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) 67 | default: break 68 | } 69 | } 70 | 71 | func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 72 | switch type { 73 | case .Insert: 74 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) 75 | case .Delete: 76 | tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) 77 | case .Update: 78 | tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) 79 | case .Move: 80 | tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) 81 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) 82 | } 83 | } 84 | 85 | func controllerDidChangeContent(controller: NSFetchedResultsController) { 86 | tableView.endUpdates() 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/CountedMentionsTVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CountedMentionsTVC.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class CountedMentionsTVC: CoreDataTableViewController { 13 | var mention: String? { didSet { updateUI() } } 14 | var managedObjectContext: NSManagedObjectContext? { didSet { updateUI() } } 15 | 16 | private func updateUI() { 17 | if let context = managedObjectContext where mention?.characters.count > 0 { 18 | let request = NSFetchRequest(entityName: "Mention") 19 | request.predicate = NSPredicate(format: "count > %@ AND query =[cd] %@", NSNumber(integer: 1), mention!) 20 | 21 | let sortDescriptor1 = NSSortDescriptor( 22 | key: "count", 23 | ascending: false 24 | ) 25 | let sortDescriptor2 = NSSortDescriptor( 26 | key: "text", 27 | ascending: true, 28 | selector: #selector(NSString.localizedStandardCompare(_:)) 29 | ) 30 | 31 | request.sortDescriptors = [sortDescriptor1, sortDescriptor2] 32 | fetchedResultsController = NSFetchedResultsController( 33 | fetchRequest: request, 34 | managedObjectContext: context, 35 | sectionNameKeyPath: nil, 36 | cacheName: nil) 37 | } else { 38 | fetchedResultsController = nil 39 | } 40 | } 41 | 42 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 43 | let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.CountedMentionCellIdentifier, forIndexPath: indexPath) 44 | 45 | if let mention = fetchedResultsController?.objectAtIndexPath(indexPath) as? Mention { 46 | var keyword: String? 47 | var numberOfTweets: Int? 48 | mention.managedObjectContext?.performBlockAndWait { 49 | keyword = mention.text 50 | numberOfTweets = mention.count?.integerValue 51 | } 52 | cell.textLabel?.text = keyword 53 | cell.detailTextLabel?.text = "tweets.count: " + String(numberOfTweets!) 54 | } 55 | 56 | return cell 57 | } 58 | 59 | // MARK: Constants 60 | 61 | private struct Storyboard { 62 | static let CountedMentionCellIdentifier = "CountedMention Cell" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/ImageTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTableViewCell.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var tweetImage: UIImageView! 14 | @IBOutlet weak var spinner: UIActivityIndicatorView! 15 | 16 | var imageUrl: NSURL? { didSet { updateUI() } } 17 | 18 | private func updateUI() { 19 | if let url = imageUrl { 20 | spinner?.startAnimating() 21 | 22 | // NSData(contentsOfURL:) blocks the thread it is called from when invoked with a network url. 23 | // Thus we cannot call it from the main thread. 24 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 25 | let loadedImageData = NSData(contentsOfURL: url) 26 | dispatch_async(dispatch_get_main_queue()) { 27 | if url == self.imageUrl { 28 | if let imageData = loadedImageData { 29 | self.tweetImage?.image = UIImage(data: imageData) 30 | } 31 | self.spinner?.stopAnimating() 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewController.swift 3 | // Cassini 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright © 2016 Stanford University. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageViewController: UIViewController, UIScrollViewDelegate 12 | { 13 | var imageURL: NSURL? { 14 | didSet { 15 | image = nil 16 | if view.window != nil { 17 | fetchImage() 18 | } 19 | } 20 | } 21 | 22 | private func fetchImage() { 23 | if let url = imageURL { 24 | spinner?.startAnimating() 25 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 26 | let loadedImageData = NSData(contentsOfURL: url) 27 | dispatch_async(dispatch_get_main_queue()) { 28 | if url == self.imageURL { 29 | if let imageData = loadedImageData { 30 | self.image = UIImage(data: imageData) 31 | } else { 32 | self.spinner?.stopAnimating() 33 | } 34 | } else { NSLog("\(Error.downloadImage) + \(url)") } 35 | } 36 | } 37 | } 38 | } 39 | 40 | @IBOutlet weak var spinner: UIActivityIndicatorView! 41 | 42 | @IBOutlet weak var scrollView: UIScrollView! { 43 | didSet { 44 | scrollView.contentSize = imageView.frame.size 45 | scrollView.delegate = self 46 | scrollView.minimumZoomScale = 0.03 47 | scrollView.maximumZoomScale = 1.0 48 | } 49 | } 50 | 51 | func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { 52 | return imageView 53 | } 54 | 55 | private var imageView = UIImageView() 56 | 57 | private var scrollViewDidScrollOrZoom = false 58 | 59 | private func autoScale() { 60 | if scrollViewDidScrollOrZoom { 61 | return 62 | } 63 | if let sv = scrollView { 64 | if image != nil { 65 | sv.zoomScale = max(sv.bounds.size.height / image!.size.height, 66 | sv.bounds.size.width / image!.size.width) 67 | sv.contentOffset = CGPoint(x: (imageView.frame.size.width - sv.frame.size.width) / 2, 68 | y: (imageView.frame.size.height - sv.frame.size.height) / 2) 69 | scrollViewDidScrollOrZoom = false 70 | } 71 | } 72 | } 73 | 74 | private var image: UIImage? { 75 | get { return imageView.image } 76 | set { 77 | imageView.image = newValue 78 | imageView.sizeToFit() 79 | scrollView?.contentSize = imageView.frame.size 80 | spinner?.stopAnimating() 81 | scrollViewDidScrollOrZoom = false 82 | autoScale() 83 | } 84 | } 85 | 86 | private struct Error { 87 | static let downloadImage = "Smashtag: couldn't get the data from" 88 | } 89 | 90 | // MARK: View Controller Lifecycle 91 | 92 | override func viewWillAppear(animated: Bool) { 93 | super.viewWillAppear(animated) 94 | if image == nil { 95 | fetchImage() 96 | } 97 | } 98 | 99 | override func viewDidLoad() { 100 | super.viewDidLoad() 101 | scrollView.addSubview(imageView) 102 | } 103 | 104 | override func viewDidLayoutSubviews() { 105 | super.viewDidLayoutSubviews() 106 | autoScale() 107 | } 108 | 109 | func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) { 110 | scrollViewDidScrollOrZoom = true 111 | } 112 | 113 | func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { 114 | scrollViewDidScrollOrZoom = true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | NSAppTransportSecurity 40 | 41 | NSAllowsArbitraryLoads 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Mention+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mention+CoreDataProperties.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Mention { 16 | 17 | @NSManaged var text: String? 18 | @NSManaged var count: NSNumber? 19 | @NSManaged var query: String? 20 | @NSManaged var tweets: NSSet? 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Mention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mention.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | import Twitter 12 | 13 | 14 | class Mention: NSManagedObject { 15 | 16 | class func mentionWithMentionInfo(keyword: String, mentionInfo: Twitter.Mention, inManagedObjectContext context: NSManagedObjectContext) -> Mention? 17 | { 18 | let request = NSFetchRequest(entityName: "Mention") 19 | request.predicate = NSPredicate(format: "text =[cd] %@ AND query = %@", mentionInfo.keyword, keyword) 20 | 21 | if let mention = (try? context.executeFetchRequest(request))?.first as? Mention { 22 | mention.count = NSNumber(int: mention.count!.intValue + 1) 23 | return mention 24 | } else if let mention = NSEntityDescription.insertNewObjectForEntityForName("Mention", inManagedObjectContext: context) as? Mention { 25 | mention.text = mentionInfo.keyword.lowercaseString 26 | mention.count = 1 27 | mention.query = keyword 28 | return mention 29 | } 30 | return nil 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Model.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Query+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Query+CoreDataProperties.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Query { 16 | 17 | @NSManaged var keyword: String? 18 | @NSManaged var tweets: NSSet? 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Query.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Query.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | import Twitter 12 | 13 | class Query: NSManagedObject { 14 | 15 | class func queryWithQueryInfo(keyword: String, tweetsInfo: [Twitter.Tweet], inManagedObjectContext context: NSManagedObjectContext) -> Query? 16 | { 17 | let request = NSFetchRequest(entityName: "Query") 18 | request.predicate = NSPredicate(format: "keyword = %@", keyword) 19 | 20 | if let query = (try? context.executeFetchRequest(request))?.first as? Query { 21 | return addTweets(query, keyword: keyword, tweetsInfo: tweetsInfo, inManagedObjectContext: context) 22 | } else if let query = NSEntityDescription.insertNewObjectForEntityForName("Query", inManagedObjectContext: context) as? Query { 23 | query.keyword = keyword 24 | return addTweets(query, keyword: keyword, tweetsInfo: tweetsInfo, inManagedObjectContext: context) 25 | } 26 | return nil 27 | } 28 | 29 | class func addTweets(query: Query, keyword: String, tweetsInfo: [Twitter.Tweet], inManagedObjectContext context: NSManagedObjectContext) -> Query { 30 | for tweetInfo in tweetsInfo { 31 | if let tweet = Tweet.tweetWithTwitterInfo(keyword, twitterInfo: tweetInfo, inManagedObjectContext: context) { 32 | let tweets = query.mutableSetValueForKey("tweets") 33 | tweets.addObject(tweet) 34 | } 35 | } 36 | return query 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/RecentQueries.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecentQueries.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct RecentQueries 12 | { 13 | private static let defaults = NSUserDefaults.standardUserDefaults() 14 | private struct Constants { 15 | static let key = "RecentQueries" 16 | static let limit = 100 17 | } 18 | 19 | static var queries: [String] { 20 | return (defaults.objectForKey(Constants.key) as? [String]) ?? [] 21 | } 22 | 23 | static func add(term: String) { 24 | var newArray = queries.filter({ term.caseInsensitiveCompare($0) != .OrderedSame }) 25 | newArray.insert(term, atIndex: 0) 26 | while newArray.count > Constants.limit { 27 | newArray.removeLast() 28 | } 29 | defaults.setObject(newArray, forKey: Constants.key) 30 | } 31 | 32 | static func removeAtIndex(index: Int) { 33 | var currentQueries = (defaults.objectForKey(Constants.key) as? [String]) ?? [] 34 | currentQueries.removeAtIndex(index) 35 | defaults.setObject(currentQueries, forKey: Constants.key) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/RecentQueriesTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecentQueriesTableViewController.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class RecentsQueriesTableViewController: UITableViewController { 13 | 14 | // MARK: Model 15 | 16 | var recentQueries: [String] { 17 | return RecentQueries.queries 18 | } 19 | 20 | var managedObjectContext: NSManagedObjectContext? = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext 21 | 22 | // MARK: View 23 | 24 | override func viewWillAppear(animated: Bool) { 25 | super.viewWillAppear(false) 26 | tableView.reloadData() 27 | } 28 | 29 | private struct Storyboard { 30 | static let RecentCell = "Recent Cell" 31 | static let TweetsSegue = "show recent tweets" 32 | static let CountedMentionsSegue = "show counted mentions" 33 | } 34 | 35 | // MARK: - UITableViewDataSource 36 | 37 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 38 | return 1 39 | } 40 | 41 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 42 | return recentQueries.count 43 | } 44 | 45 | override func tableView(tableView: UITableView, 46 | cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 47 | let cell = tableView.dequeueReusableCellWithIdentifier(Storyboard.RecentCell, 48 | forIndexPath: indexPath) as UITableViewCell 49 | cell.textLabel?.text = recentQueries[indexPath.row] 50 | return cell 51 | } 52 | 53 | override func tableView(tableView: UITableView, 54 | commitEditingStyle editingStyle: UITableViewCellEditingStyle, 55 | forRowAtIndexPath indexPath: NSIndexPath) { 56 | if editingStyle == .Delete { 57 | RecentQueries.removeAtIndex(indexPath.row) 58 | tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 59 | } 60 | } 61 | 62 | 63 | // MARK: - Navigation 64 | 65 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 66 | 67 | if let identifier = segue.identifier where identifier == Storyboard.TweetsSegue, 68 | let cell = sender as? UITableViewCell, 69 | let ttvc = segue.destinationViewController as? TweetTableViewController { 70 | ttvc.searchText = cell.textLabel?.text 71 | } else if let identifier = segue.identifier where identifier == Storyboard.CountedMentionsSegue, 72 | let cmtvc = segue.destinationViewController as? CountedMentionsTVC, 73 | let sender = sender as? NSIndexPath { 74 | cmtvc.mention = recentQueries[sender.row] 75 | cmtvc.managedObjectContext = managedObjectContext 76 | } 77 | } 78 | 79 | override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) { 80 | performSegueWithIdentifier(Storyboard.CountedMentionsSegue, sender: indexPath) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Tweet+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tweet+CoreDataProperties.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | // Choose "Create NSManagedObject Subclass…" from the Core Data editor menu 9 | // to delete and recreate this implementation file for your updated model. 10 | // 11 | 12 | import Foundation 13 | import CoreData 14 | 15 | extension Tweet { 16 | 17 | @NSManaged var text: String? 18 | @NSManaged var unique: String? 19 | @NSManaged var posted: NSDate? 20 | @NSManaged var queries: NSSet? 21 | @NSManaged var mentions: NSSet? 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/Tweet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tweet.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/23/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | import Twitter 12 | 13 | 14 | class Tweet: NSManagedObject { 15 | 16 | class func tweetWithTwitterInfo(keyword: String, twitterInfo: Twitter.Tweet, inManagedObjectContext context: NSManagedObjectContext) -> Tweet? 17 | { 18 | let request = NSFetchRequest(entityName: "Tweet") 19 | request.predicate = NSPredicate(format: "unique = %@", twitterInfo.id) 20 | 21 | if let tweet = (try? context.executeFetchRequest(request))?.first as? Tweet { 22 | return addMentions(keyword, tweet: tweet, twitterInfo: twitterInfo, inManagedObjectContext: context) 23 | } else if let tweet = NSEntityDescription.insertNewObjectForEntityForName("Tweet", inManagedObjectContext: context) as? Tweet { 24 | tweet.text = twitterInfo.text 25 | tweet.unique = twitterInfo.id 26 | tweet.posted = twitterInfo.created 27 | return addMentions(keyword, tweet: tweet, twitterInfo: twitterInfo, inManagedObjectContext: context) 28 | } 29 | return nil 30 | } 31 | 32 | class func addMentions(keyword: String, tweet: Tweet, twitterInfo: Twitter.Tweet, inManagedObjectContext context: NSManagedObjectContext) -> Tweet { 33 | let tweetMentions = ["hashtags": twitterInfo.hashtags, 34 | "userMentions": twitterInfo.userMentions] 35 | for (_, mentionsInfo) in tweetMentions { 36 | for mentionInfo in mentionsInfo { 37 | if let mention = Mention.mentionWithMentionInfo(keyword, mentionInfo: mentionInfo, inManagedObjectContext: context) { 38 | let mentions = tweet.mutableSetValueForKey("mentions") 39 | mentions.addObject(mention) 40 | } 41 | } 42 | } 43 | return tweet 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Assignment 5/Smashtag/Smashtag/TweetTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TweetTableViewCell.swift 3 | // Smashtag 4 | // 5 | // Created by Kanstantsin Linou on 7/20/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Twitter 11 | 12 | class TweetTableViewCell: UITableViewCell 13 | { 14 | // MARK: Outlets 15 | 16 | @IBOutlet weak var tweetScreenNameLabel: UILabel! 17 | @IBOutlet weak var tweetTextLabel: UILabel! 18 | @IBOutlet weak var tweetProfileImageView: UIImageView! 19 | @IBOutlet weak var tweetCreatedLabel: UILabel! 20 | 21 | // MARK: Model 22 | 23 | var tweet: Twitter.Tweet? { 24 | didSet { 25 | updateUI() 26 | } 27 | } 28 | 29 | private func updateUI() 30 | { 31 | // reset any existing tweet information 32 | tweetTextLabel?.attributedText = nil 33 | tweetScreenNameLabel?.text = nil 34 | tweetProfileImageView?.image = nil 35 | tweetCreatedLabel?.text = nil 36 | 37 | // load new information from our tweet (if any) 38 | if let tweet = self.tweet 39 | { 40 | tweetTextLabel?.attributedText = getColorfulTextLabel(tweet) 41 | tweetScreenNameLabel?.text = "\(tweet.user)" 42 | setProfileImageView(tweet) 43 | tweetCreatedLabel?.text = getCreatedLabel(tweet) 44 | } 45 | } 46 | 47 | private struct Color { 48 | static let user = UIColor.purpleColor() 49 | static let hashtag = UIColor.darkGrayColor() 50 | static let url = UIColor.blueColor() 51 | } 52 | 53 | private func getColorfulTextLabel(tweet: Twitter.Tweet) -> NSMutableAttributedString { 54 | var text = tweet.text 55 | for _ in tweet.media { 56 | text += " 📷" 57 | } 58 | 59 | // Enhance Smashtag from lecture to highlight (in a different color for each) hashtags, 60 | // urls and user screen names mentioned in the text of each Tweet 61 | let attributedText = NSMutableAttributedString(string: text) 62 | attributedText.setMensionsColor(tweet.hashtags, color: Color.hashtag) 63 | attributedText.setMensionsColor(tweet.urls, color: Color.url) 64 | attributedText.setMensionsColor(tweet.userMentions, color: Color.user) 65 | 66 | return attributedText 67 | } 68 | 69 | private func setProfileImageView(tweet: Twitter.Tweet) { 70 | guard let profileImageURL = tweet.user.profileImageURL else { 71 | return 72 | } 73 | 74 | // NSData(contentsOfURL:) blocks the thread it is called from when invoked with a network url. 75 | // Thus we cannot call it from the main thread. 76 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 77 | let loadedImageData = NSData(contentsOfURL: profileImageURL) 78 | dispatch_async(dispatch_get_main_queue()) { 79 | if profileImageURL == tweet.user.profileImageURL { 80 | if let imageData = loadedImageData { 81 | self.tweetProfileImageView?.image = UIImage(data: imageData) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | private func getCreatedLabel(tweet: Twitter.Tweet) -> String { 89 | let formatter = NSDateFormatter() 90 | if NSDate().timeIntervalSinceDate(tweet.created) > 24*60*60 { 91 | formatter.dateStyle = NSDateFormatterStyle.ShortStyle 92 | } else { 93 | formatter.timeStyle = NSDateFormatterStyle.ShortStyle 94 | } 95 | return formatter.stringFromDate(tweet.created) 96 | } 97 | } 98 | 99 | private extension NSMutableAttributedString { 100 | func setMensionsColor(mensions: [Twitter.Mention], color: UIColor) { 101 | for mension in mensions { 102 | addAttribute(NSForegroundColorAttributeName, value: color, range: mension.nsrange) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/project.xcworkspace/xcuserdata/Paul.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 5/Twitter/Twitter.xcodeproj/project.xcworkspace/xcuserdata/Paul.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/Paul.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/Paul.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/cs193p.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/cs193p.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Twitter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Twitter.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DAB79D141BD20B7900448104 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter/MediaItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MediaItem.swift 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright (c) 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // holds the network url and aspectRatio of an image attached to a Tweet 12 | // created automatically when a Tweet object is created 13 | 14 | public class MediaItem: NSObject 15 | { 16 | public let url: NSURL 17 | public let aspectRatio: Double 18 | 19 | public override var description: String { return "\(url.absoluteString) (aspect ratio = \(aspectRatio))" } 20 | 21 | // MARK: - Internal Implementation 22 | 23 | init?(data: NSDictionary?) { 24 | guard 25 | let height = data?.valueForKeyPath(TwitterKey.Height) as? Double where height > 0, 26 | let width = data?.valueForKeyPath(TwitterKey.Width) as? Double where width > 0, 27 | let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? String, 28 | let url = NSURL(string: urlString) 29 | else { 30 | return nil 31 | } 32 | self.url = url 33 | self.aspectRatio = width/height 34 | } 35 | 36 | struct TwitterKey { 37 | static let MediaURL = "media_url_https" 38 | static let Width = "sizes.small.w" 39 | static let Height = "sizes.small.h" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter/Twitter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Twitter.h 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright © 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Twitter. 12 | FOUNDATION_EXPORT double TwitterVersionNumber; 13 | 14 | //! Project version string for Twitter. 15 | FOUNDATION_EXPORT const unsigned char TwitterVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | -------------------------------------------------------------------------------- /Assignment 5/Twitter/Twitter/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // Twitter 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright (c) 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // container to hold data about a Twitter user 12 | 13 | public class User: NSObject 14 | { 15 | public let screenName: String 16 | public let name: String 17 | public let id: String 18 | public let verified: Bool 19 | public let profileImageURL: NSURL? 20 | 21 | public override var description: String { return "@\(screenName) (\(name))\(verified ? " ✅" : "")" } 22 | 23 | // MARK: - Internal Implementation 24 | 25 | init?(data: NSDictionary?) { 26 | guard 27 | let screenName = data?.valueForKeyPath(TwitterKey.ScreenName) as? String, 28 | let name = data?.valueForKeyPath(TwitterKey.Name) as? String, 29 | let id = data?.valueForKeyPath(TwitterKey.ID) as? String 30 | else { 31 | return nil 32 | } 33 | 34 | self.screenName = screenName 35 | self.name = name 36 | self.id = id 37 | 38 | self.verified = data?.valueForKeyPath(TwitterKey.Verified)?.boolValue ?? false 39 | let urlString = data?.valueForKeyPath(TwitterKey.ProfileImageURL) as? String ?? "" 40 | self.profileImageURL = (urlString.characters.count > 0) ? NSURL(string: urlString) : nil 41 | } 42 | 43 | var asPropertyList: AnyObject { 44 | return [ 45 | TwitterKey.Name:name, 46 | TwitterKey.ScreenName:screenName, 47 | TwitterKey.ID:id, 48 | TwitterKey.Verified:verified ? "YES" : "NO", 49 | TwitterKey.ProfileImageURL:profileImageURL?.absoluteString ?? "" 50 | ] 51 | } 52 | 53 | struct TwitterKey { 54 | static let Name = "name" 55 | static let ScreenName = "screen_name" 56 | static let ID = "id_str" 57 | static let Verified = "verified" 58 | static let ProfileImageURL = "profile_image_url" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Assignment 5/smashtag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 5/smashtag.gif -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 6/Breakout/Breakout.xcodeproj/project.xcworkspace/xcuserdata/linou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout.xcodeproj/xcuserdata/linou.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/Breakout.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout.xcodeproj/xcuserdata/linou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Breakout.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78D01DA81D44A41300774328 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Breakout 4 | // 5 | // Created by Kanstantsin Linou on 7/24/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/breakout.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/breakout.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 6/Breakout/Breakout/Assets.xcassets/breakout.imageset/first.pdf -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Assets.xcassets/settings.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 6/Breakout/Breakout/Assets.xcassets/settings.imageset/second.pdf -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/BallView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BallView.swift 3 | // Breakout 4 | // 5 | // Created by Kanstantsin Linou on 7/24/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BallView: UIView { 12 | 13 | override var collisionBoundsType: UIDynamicItemCollisionBoundsType { 14 | return UIDynamicItemCollisionBoundsType.Ellipse 15 | } 16 | 17 | override func drawRect(rect: CGRect) { 18 | let path = UIBezierPath(ovalInRect: rect) 19 | UIColor.blueColor().setFill() 20 | path.fill() 21 | } 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | backgroundColor = UIColor.whiteColor() 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | super.init(coder: aDecoder) 30 | backgroundColor = UIColor.whiteColor() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/BreakoutBehaviorDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BreakoutBehaviorDelegate.swift 3 | // Breakout 4 | // 5 | // Created by Kanstantsin Linou on 7/24/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol BreakoutBehaviorDelegate { 12 | func gameOver(playerWon: Bool) 13 | } 14 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/BreakoutVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // StanfordBreakout 4 | // 5 | // Created by Michael Flynn on 11/9/15. 6 | // Copyright © 2015 MRF. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BreakoutVC: UIViewController, UIDynamicAnimatorDelegate, BreakoutBehaviorDelegate { 12 | private struct Constants { 13 | static let NumberOfBricksPerRow = 5 14 | static let BrickGap: CGFloat = 8 15 | static let BrickHeight: CGFloat = 30 16 | static let PaddleHeight: CGFloat = 20 17 | static let PaddleWidth: CGFloat = 80 18 | static let PaddleGap: CGFloat = 30 19 | static let BallSize: CGFloat = 20 20 | } 21 | 22 | private var gameInProgress = false 23 | private var paddleView: UIView? 24 | private let breakoutBehavior = BreakoutBehavior() 25 | @IBOutlet private weak var gameView: UIView! 26 | 27 | private lazy var animator: UIDynamicAnimator = { 28 | let lazilyCreatedDynamicAnimator = UIDynamicAnimator(referenceView: self.gameView) 29 | lazilyCreatedDynamicAnimator.delegate = self 30 | return lazilyCreatedDynamicAnimator 31 | }() 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | breakoutBehavior.breakoutBehaviorDelegate = self 36 | animator.addBehavior(breakoutBehavior) 37 | } 38 | 39 | override func viewWillAppear(animated: Bool) { 40 | super.viewWillAppear(animated) 41 | clear() 42 | } 43 | 44 | override func viewDidAppear(animated: Bool) { 45 | super.viewDidAppear(animated) 46 | reset() 47 | } 48 | 49 | private func clear() { 50 | breakoutBehavior.clear() 51 | gameView.subviews.forEach{ $0.removeFromSuperview() } 52 | } 53 | 54 | private func reset() { 55 | gameInProgress = false 56 | let numberOfBricks = Settings.sharedInstance.numberOfBricks 57 | let brickWidth = (gameView.frame.size.width - CGFloat(CGFloat(Constants.NumberOfBricksPerRow + 1) * Constants.BrickGap)) / CGFloat(Constants.NumberOfBricksPerRow) 58 | 59 | var bricksRowIndex = 0 60 | for numberOfUsedBricks in 0.stride(to: numberOfBricks, by: Constants.NumberOfBricksPerRow) { 61 | for bricksColumnIndex in 0..<5 { 62 | if numberOfUsedBricks + bricksColumnIndex >= numberOfBricks { 63 | break 64 | } 65 | 66 | let brickView = BrickView(frame: CGRectMake(CGFloat(bricksColumnIndex) * (Constants.BrickGap + brickWidth) + Constants.BrickGap, CGFloat(bricksRowIndex) * (Constants.BrickGap + Constants.BrickHeight) + Constants.BrickGap, brickWidth, Constants.BrickHeight)) 67 | brickView.numberOfHits = 1 68 | breakoutBehavior.addBrick(brickView) 69 | } 70 | bricksRowIndex += 1 71 | } 72 | 73 | paddleView = UIView(frame: CGRectMake(0, gameView.frame.size.height - Constants.PaddleHeight - Constants.PaddleGap, Constants.PaddleWidth, Constants.PaddleHeight)) 74 | paddleView!.backgroundColor = UIColor.greenColor() 75 | paddleView!.center.x = gameView.center.x 76 | breakoutBehavior.addPaddle(paddleView!) 77 | } 78 | 79 | private func startGame() { 80 | gameInProgress = true 81 | let ballView = BallView(frame: CGRectMake(0, CGRectGetMinY(self.paddleView!.frame) - Constants.BallSize, Constants.BallSize, Constants.BallSize)) 82 | ballView.center.x = paddleView!.center.x 83 | breakoutBehavior.addBall(ballView) 84 | 85 | } 86 | 87 | @IBAction private func tap(sender: UITapGestureRecognizer) { 88 | if !gameInProgress { 89 | startGame() 90 | } 91 | } 92 | 93 | @IBAction private func pan(sender: UIPanGestureRecognizer) { 94 | if !gameInProgress { 95 | return 96 | } 97 | 98 | if sender.state == .Changed || sender.state == .Ended { 99 | if let paddleView = self.paddleView { 100 | paddleView.center.x += sender.translationInView(gameView).x * 1.5 101 | sender.setTranslation(CGPointZero, inView: gameView) 102 | breakoutBehavior.updatePaddlePosition(paddleView) 103 | } 104 | } 105 | } 106 | 107 | func gameOver(playerWon: Bool) { 108 | guard gameInProgress == true else { 109 | return 110 | } 111 | if (playerWon) { 112 | let alert = UIAlertController(title: "Congratulations!", message: "You've won!", preferredStyle: UIAlertControllerStyle.Alert) 113 | alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil)) 114 | presentViewController(alert, animated: true, completion: nil) 115 | } else { 116 | let alert = UIAlertController(title: "Game Over!", message: "You've lost.", preferredStyle: UIAlertControllerStyle.Alert) 117 | alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil)) 118 | presentViewController(alert, animated: true, completion: nil) 119 | } 120 | 121 | gameInProgress = false 122 | clear() 123 | reset() 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/BrickView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrickView.swift 3 | // Breakout 4 | // 5 | // Created by Kanstantsin Linou on 7/24/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BrickView: UIView { 12 | var numberOfHits = 1 { 13 | didSet { 14 | if (numberOfHits == 1) { 15 | UIView.animateWithDuration(0.4) { self.backgroundColor = UIColor.purpleColor() } 16 | } else { 17 | UIView.animateWithDuration(0.4, animations: { 18 | self.transform = CGAffineTransformMakeScale(0.05, 0.05)}) 19 | { _ in self.removeFromSuperview() } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/Settings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Settings.swift 3 | // StanfordBreakout 4 | // 5 | // Created by Michael Flynn on 11/9/15. 6 | // Copyright © 2015 MRF. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Settings { 12 | 13 | static let sharedInstance = Settings() 14 | 15 | private struct UserConstants { 16 | static let ballSpeed = "BallSpeed" 17 | static let numberOfBricks = "NumberOfBricks" 18 | struct Defaults { 19 | static let ballSpeed = NSNumber(float: 300) 20 | static let numberOfBricks = NSNumber(int: 15) 21 | 22 | } 23 | } 24 | 25 | var ballSpeed: Float { 26 | get { 27 | if let ballSpeed = NSUserDefaults.standardUserDefaults().valueForKey(UserConstants.ballSpeed) as? NSNumber { 28 | return ballSpeed.floatValue 29 | } else { 30 | return UserConstants.Defaults.ballSpeed.floatValue 31 | } 32 | } 33 | 34 | set { 35 | NSUserDefaults.standardUserDefaults().setValue(NSNumber(float: newValue), forKey: UserConstants.ballSpeed) 36 | } 37 | } 38 | 39 | var numberOfBricks: NSInteger { 40 | get { 41 | if let numberOfBricks = NSUserDefaults.standardUserDefaults().valueForKey(UserConstants.numberOfBricks) as? NSNumber { 42 | return numberOfBricks.integerValue 43 | } else { 44 | return UserConstants.Defaults.numberOfBricks.integerValue 45 | } 46 | } 47 | 48 | set { 49 | NSUserDefaults.standardUserDefaults().setValue(NSNumber(integer: newValue), forKey: UserConstants.numberOfBricks) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Assignment 6/Breakout/Breakout/SettingsTVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsTVC.swift 3 | // Breakout 4 | // 5 | // Created by Kanstantsin Linou on 7/24/16. 6 | // Copyright © 2016 self.edu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingsTVC: UITableViewController { 12 | @IBOutlet private weak var ballSpeedSlider: UISlider! 13 | @IBOutlet private weak var numberOfBricksStepper: UIStepper! 14 | @IBOutlet private weak var numberOfBricksLabel: UILabel! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | self.ballSpeedSlider.value = Settings.sharedInstance.ballSpeed 19 | self.numberOfBricksStepper.value = Double(Settings.sharedInstance.numberOfBricks) 20 | self.numberOfBricksLabel.text = String(Settings.sharedInstance.numberOfBricks) 21 | } 22 | 23 | @IBAction private func ballSpeedChanged(sender: UISlider) { 24 | Settings.sharedInstance.ballSpeed = sender.value 25 | } 26 | 27 | @IBAction private func numberOfBricksChanged(sender: UIStepper) { 28 | Settings.sharedInstance.numberOfBricks = Int(sender.value) 29 | numberOfBricksLabel.text = String(Int(sender.value)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Assignment 6/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 6: Animation 2 | 3 | The goal of this assignment is to let anyone play the game Breakout. 4 | 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/Assignment%206/breakout.gif) 6 | -------------------------------------------------------------------------------- /Assignment 6/breakout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linouk23/cs193p-ios9-solutions/ef6fa392a42b7b8e382821faf25a53e157ac7e3c/Assignment 6/breakout.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cs193p-ios9-solutions 2 | My solutions to the assignments (6/6) for Stanford's CS193P: Developing iOS 9 Apps with Swift [Spring 2016] 3 | 4 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/1.gif) 5 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/2.gif) 6 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/3.gif) 7 | 8 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/4.gif) 9 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/5.gif) 10 | ![](https://github.com/linouk23/cs193p-ios9-solutions/blob/master/6.gif) --------------------------------------------------------------------------------