Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
448 changes: 448 additions & 0 deletions LambdaTimeline.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "46463777216FDE4B00E7FF73"
BuildableName = "LambdaTimeline.app"
BlueprintName = "LambdaTimeline"
ReferencedContainer = "container:LambdaTimeline.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "46463777216FDE4B00E7FF73"
BuildableName = "LambdaTimeline.app"
BlueprintName = "LambdaTimeline"
ReferencedContainer = "container:LambdaTimeline.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "46463777216FDE4B00E7FF73"
BuildableName = "LambdaTimeline.app"
BlueprintName = "LambdaTimeline"
ReferencedContainer = "container:LambdaTimeline.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
41 changes: 41 additions & 0 deletions LambdaTimeline/Helpers/Extensions/UIImage+Ratio.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// UIImage+Ratio.swift
// LambdaTimeline
//
// Created by Spencer Curtis on 10/14/18.
// Copyright © 2018 Lambda School. All rights reserved.
//

import UIKit

extension UIImage {
var ratio: CGFloat {
return size.height / size.width
}

/// Resize the image to a max dimension from size parameter
func imageByScaling(toSize size: CGSize) -> UIImage? {
guard size.width > 0 && size.height > 0 else { return nil }

let originalAspectRatio = self.size.width/self.size.height
var correctedSize = size

if correctedSize.width > correctedSize.width*originalAspectRatio {
correctedSize.width = correctedSize.width*originalAspectRatio
} else {
correctedSize.height = correctedSize.height/originalAspectRatio
}

return UIGraphicsImageRenderer(size: correctedSize, format: imageRendererFormat).image { context in
draw(in: CGRect(origin: .zero, size: correctedSize))
}
}

/// Renders the image if the pixel data was rotated due to orientation of camera
var flattened: UIImage {
if imageOrientation == .up { return self }
return UIGraphicsImageRenderer(size: size, format: imageRendererFormat).image { context in
draw(at: .zero)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// UIViewController+InformationalAlert.swift
// LambdaTimeline
//
// Created by Spencer Curtis on 10/12/18.
// Copyright © 2018 Lambda School. All rights reserved.
//

import UIKit

extension UIViewController {

func presentInformationalAlertController(title: String?, message: String?, dismissActionCompletion: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "Dismiss", style: .cancel, handler: dismissActionCompletion)

alertController.addAction(dismissAction)

present(alertController, animated: true, completion: completion)
}
}
126 changes: 126 additions & 0 deletions LambdaTimeline/Helpers/ShiftableViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// Created by Spencer Curtis.
// Copyright © 2017-2018 Spencer Curtis. All rights reserved.
//

/*
All you need to do is set your subclass of ShiftableViewController as the delegate for all
UITextFields and UITextViews that you want to be shifted up so the keyboard doesn't obscure it.
*/

import UIKit

class ShiftableViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate, UIGestureRecognizerDelegate {

var currentYShiftForKeyboard: CGFloat = 0

var textFieldBeingEdited: UITextField?
var textViewBeingEdited: UITextView?

var keyboardDismissTapGestureRecognizer: UITapGestureRecognizer!

override func viewDidLoad() {
super.viewDidLoad()

setupKeyboardDismissTapGestureRecognizer()

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func stopEditingTextInput() {
if let textField = self.textFieldBeingEdited {

textField.resignFirstResponder()

self.textFieldBeingEdited = nil
self.textViewBeingEdited = nil
} else if let textView = self.textViewBeingEdited {

textView.resignFirstResponder()

self.textFieldBeingEdited = nil
self.textViewBeingEdited = nil
}

guard keyboardDismissTapGestureRecognizer.isEnabled else { return }

keyboardDismissTapGestureRecognizer.isEnabled = false
}

func textFieldDidBeginEditing(_ textField: UITextField) {
textFieldBeingEdited = textField
}

func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
textViewBeingEdited = textView
return true
}

@objc func keyboardWillShow(notification: Notification) {

keyboardDismissTapGestureRecognizer.isEnabled = true

var keyboardSize: CGRect = .zero

if let keyboardRect = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
keyboardRect.height != 0 {
keyboardSize = keyboardRect
} else if let keyboardRect = notification.userInfo?["UIKeyboardBoundsUserInfoKey"] as? CGRect {
keyboardSize = keyboardRect
}

if let textField = textFieldBeingEdited {
if self.view.frame.origin.y == 0 {

let yShift = yShiftWhenKeyboardAppearsFor(textInput: textField, keyboardSize: keyboardSize, nextY: keyboardSize.height)
self.currentYShiftForKeyboard = yShift
self.view.frame.origin.y -= yShift
}
} else if let textView = textViewBeingEdited {
if self.view.frame.origin.y == 0 {

let yShift = yShiftWhenKeyboardAppearsFor(textInput: textView, keyboardSize: keyboardSize, nextY: keyboardSize.height)
self.currentYShiftForKeyboard = yShift
self.view.frame.origin.y -= yShift
}
}
}

@objc func yShiftWhenKeyboardAppearsFor(textInput: UIView, keyboardSize: CGRect, nextY: CGFloat) -> CGFloat {

let textFieldOrigin = self.view.convert(textInput.frame, from: textInput.superview!).origin.y
let textFieldBottomY = textFieldOrigin + textInput.frame.size.height

// This is the y point that the textField's bottom can be at before it gets covered by the keyboard
let maximumY = self.view.frame.height - (keyboardSize.height + view.safeAreaInsets.bottom)

if textFieldBottomY > maximumY {
// This makes the view shift the right amount to have the text field being edited just above they keyboard if it would have been covered by the keyboard.
return textFieldBottomY - maximumY
} else {
// It would go off the screen if moved, and it won't be obscured by the keyboard.
return 0
}
}

@objc func keyboardWillHide(notification: Notification) {

if self.view.frame.origin.y != 0 {

self.view.frame.origin.y += currentYShiftForKeyboard
}

stopEditingTextInput()
}

@objc func setupKeyboardDismissTapGestureRecognizer() {

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(stopEditingTextInput))
tapGestureRecognizer.numberOfTapsRequired = 1

view.addGestureRecognizer(tapGestureRecognizer)

keyboardDismissTapGestureRecognizer = tapGestureRecognizer
}
}
42 changes: 42 additions & 0 deletions LambdaTimeline/Model Controllers/PostController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// PostController.swift
// LambdaTimeline
//
// Created by Spencer Curtis on 10/11/18.
// Copyright © 2018 Lambda School. All rights reserved.
//

import UIKit

class PostController {

var posts: [Post] = []

var currentUser: String? {
UserDefaults.standard.string(forKey: "username")
}

func createImagePost(with title: String, image: UIImage, ratio: CGFloat?) {

guard let currentUser = currentUser else { return }

let post = Post(title: title, mediaType: .image(image), ratio: ratio, author: currentUser)

posts.append(post)
}

func addComment(with text: String, to post: inout Post) {

guard let currentUser = currentUser else { return }

let comment = Comment(text: text, author: currentUser)
post.comments.append(comment)
}

func addComment(with audio: URL, to post: inout Post) {
guard let currentUser = currentUser else { return }

let comment = Comment(author: currentUser, audioURL: audio)
post.comments.append(comment)
}
}
37 changes: 37 additions & 0 deletions LambdaTimeline/Models/Comment.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Comment.swift
// LambdaTimeline
//
// Created by Spencer Curtis on 10/11/18.
// Copyright © 2018 Lambda School. All rights reserved.
//

import Foundation

class Comment: Hashable {

static private let textKey = "text"
static private let authorKey = "author"
static private let timestampKey = "timestamp"

let text: String?
let author: String
let timestamp: Date
let audioURL: URL?

init(text: String? = nil, author: String, timestamp: Date = Date(), audioURL: URL? = nil) {
self.text = text
self.author = author
self.timestamp = timestamp
self.audioURL = audioURL
}

func hash(into hasher: inout Hasher) {
hasher.combine(timestamp.hashValue ^ author.hashValue)
}

static func ==(lhs: Comment, rhs: Comment) -> Bool {
return lhs.author == rhs.author &&
lhs.timestamp == rhs.timestamp
}
}
40 changes: 40 additions & 0 deletions LambdaTimeline/Models/Post.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// Post.swift
// LambdaTimeline
//
// Created by Spencer Curtis on 10/11/18.
// Copyright © 2018 Lambda School. All rights reserved.
//

import UIKit

enum MediaType {
case image(UIImage)
}

class Post: Equatable {

let mediaType: MediaType
let author: String
let timestamp: Date
var comments: [Comment]
var ratio: CGFloat?
var id: String?

var title: String? {
return comments.first?.text
}

init(title: String, mediaType: MediaType, ratio: CGFloat?, author: String, timestamp: Date = Date()) {
self.mediaType = mediaType
self.ratio = ratio
self.author = author
self.comments = [Comment(text: title, author: author)]
self.timestamp = timestamp
self.id = UUID().uuidString
}

static func ==(lhs: Post, rhs: Post) -> Bool {
return lhs.id == rhs.id
}
}
Loading