├── 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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
5 | 
6 | 
7 |
8 | 
9 | 
10 | 
--------------------------------------------------------------------------------