Commit d95006ec authored by domenicw's avatar domenicw
Browse files

Added onboarding flow

parent 6529a790
......@@ -40,6 +40,13 @@
B050E1922151A61F0090CB79 /* SettingsSectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B050E1912151A61F0090CB79 /* SettingsSectionModel.swift */; };
B050E1942151A9750090CB79 /* Bundle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B050E1932151A9750090CB79 /* Bundle+Extension.swift */; };
B050E1972151AAC40090CB79 /* SettingsNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B050E1962151AAC40090CB79 /* SettingsNavigator.swift */; };
B07A89FE2152316C003CC2D8 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A89FD2152316C003CC2D8 /* InfoViewController.swift */; };
B07A8A00215233B5003CC2D8 /* InfoItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A89FF215233B5003CC2D8 /* InfoItemView.swift */; };
B07A8A022152384F003CC2D8 /* InfoItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A8A012152384F003CC2D8 /* InfoItemModel.swift */; };
B07A8A0421523AB8003CC2D8 /* InfoViewControllerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A8A0321523AB8003CC2D8 /* InfoViewControllerModel.swift */; };
B07A8A0621523B96003CC2D8 /* InfoViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A8A0521523B96003CC2D8 /* InfoViewControllerDelegate.swift */; };
B07A8A0921524384003CC2D8 /* OnboardingNavigatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A8A0821524384003CC2D8 /* OnboardingNavigatorDelegate.swift */; };
B07A8A0B21524474003CC2D8 /* LoginModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07A8A0A21524474003CC2D8 /* LoginModel.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -91,6 +98,13 @@
B050E1912151A61F0090CB79 /* SettingsSectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSectionModel.swift; sourceTree = "<group>"; };
B050E1932151A9750090CB79 /* Bundle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extension.swift"; sourceTree = "<group>"; };
B050E1962151AAC40090CB79 /* SettingsNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsNavigator.swift; sourceTree = "<group>"; };
B07A89FD2152316C003CC2D8 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; };
B07A89FF215233B5003CC2D8 /* InfoItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoItemView.swift; sourceTree = "<group>"; };
B07A8A012152384F003CC2D8 /* InfoItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoItemModel.swift; sourceTree = "<group>"; };
B07A8A0321523AB8003CC2D8 /* InfoViewControllerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewControllerModel.swift; sourceTree = "<group>"; };
B07A8A0521523B96003CC2D8 /* InfoViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewControllerDelegate.swift; sourceTree = "<group>"; };
B07A8A0821524384003CC2D8 /* OnboardingNavigatorDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingNavigatorDelegate.swift; sourceTree = "<group>"; };
B07A8A0A21524474003CC2D8 /* LoginModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginModel.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -188,6 +202,7 @@
isa = PBXGroup;
children = (
B050E14F21516BA60090CB79 /* OnboardingNavigator.swift */,
B07A8A0821524384003CC2D8 /* OnboardingNavigatorDelegate.swift */,
);
path = Onboarding;
sourceTree = "<group>";
......@@ -235,6 +250,7 @@
B050E15321516C8B0090CB79 /* Onboarding */ = {
isa = PBXGroup;
children = (
B07A8A0721524023003CC2D8 /* Info */,
);
path = Onboarding;
sourceTree = "<group>";
......@@ -347,6 +363,7 @@
children = (
B050E17C2151910F0090CB79 /* LoginViewController.swift */,
B050E18021519B390090CB79 /* LoginViewControllerDelegate.swift */,
B07A8A0A21524474003CC2D8 /* LoginModel.swift */,
);
path = Login;
sourceTree = "<group>";
......@@ -387,6 +404,18 @@
path = Settings;
sourceTree = "<group>";
};
B07A8A0721524023003CC2D8 /* Info */ = {
isa = PBXGroup;
children = (
B07A89FD2152316C003CC2D8 /* InfoViewController.swift */,
B07A89FF215233B5003CC2D8 /* InfoItemView.swift */,
B07A8A012152384F003CC2D8 /* InfoItemModel.swift */,
B07A8A0321523AB8003CC2D8 /* InfoViewControllerModel.swift */,
B07A8A0521523B96003CC2D8 /* InfoViewControllerDelegate.swift */,
);
path = Info;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
......@@ -490,12 +519,17 @@
B050E17821517EF50090CB79 /* AmivApplicationModel.swift in Sources */,
B050E16E215178BA0090CB79 /* AmivApplictionsNavigator.swift in Sources */,
B050E18C2151A54D0090CB79 /* SettingsCellModel.swift in Sources */,
B07A8A0921524384003CC2D8 /* OnboardingNavigatorDelegate.swift in Sources */,
B07A8A00215233B5003CC2D8 /* InfoItemView.swift in Sources */,
B050E17521517E1B0090CB79 /* AmivApplicationViewControllerDelegate.swift in Sources */,
B050E120215169230090CB79 /* AppDelegate.swift in Sources */,
B07A89FE2152316C003CC2D8 /* InfoViewController.swift in Sources */,
B050E17D2151910F0090CB79 /* LoginViewController.swift in Sources */,
B050E171215179A30090CB79 /* AmivApplicationViewController.swift in Sources */,
B050E18A2151A4A80090CB79 /* SettingsAction.swift in Sources */,
B07A8A0421523AB8003CC2D8 /* InfoViewControllerModel.swift in Sources */,
B050E146215169D00090CB79 /* RootNavigator.swift in Sources */,
B07A8A0621523B96003CC2D8 /* InfoViewControllerDelegate.swift in Sources */,
B050E1872151A3C40090CB79 /* SettingsViewControllerDelegate.swift in Sources */,
B050E15021516BA60090CB79 /* OnboardingNavigator.swift in Sources */,
B050E16B215177820090CB79 /* JobsNavigator.swift in Sources */,
......@@ -514,8 +548,10 @@
B050E168215176D50090CB79 /* AmivApplicationActionDelegate.swift in Sources */,
B050E144215169950090CB79 /* Navigator.swift in Sources */,
B050E17A215180D20090CB79 /* AmivApplicationCell.swift in Sources */,
B07A8A0B21524474003CC2D8 /* LoginModel.swift in Sources */,
B050E15F2151735A0090CB79 /* UIColor+Extension.swift in Sources */,
B050E17F215195B50090CB79 /* UIFont+Extension.swift in Sources */,
B07A8A022152384F003CC2D8 /* InfoItemModel.swift in Sources */,
B050E17321517A050090CB79 /* AmivApplication.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
......@@ -13,6 +13,8 @@ public class OnboardingNavigator: Navigator {
// MARK: - Variables
public var delegate: OnboardingNavigatorDelegate?
public var rootViewController: UIViewController {
return self.navigationController
}
......@@ -22,7 +24,42 @@ public class OnboardingNavigator: Navigator {
// MARK: - Initializers
public init() {
self.navigationController = UINavigationController()
let info = InfoViewController(model: .create())
self.navigationController = UINavigationController(rootViewController: info)
info.delegate = self
}
// MARK: - Navigation
private func presentLogin() {
let login = LoginViewController(model: .createOnboarding())
login.delegate = self
self.navigationController.present(login, animated: true, completion: nil)
}
}
extension OnboardingNavigator: InfoViewControllerDelegate {
public func buttonPressed() {
self.presentLogin()
}
public func smallButtonPressed() {
self.delegate?.onboardingFinished()
}
}
extension OnboardingNavigator: LoginViewControllerDelegate {
public func login(username: String, password: String) {
debugPrint("Logging in with username: \(username) and password: \(password)")
self.delegate?.onboardingFinished()
}
public func smallButtonTapped() {
self.delegate?.onboardingFinished()
}
}
//
// OnboardingNavigatorDelegate.swift
// Amiv
//
// Created by Domenic Wüthrich on 19.09.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public protocol OnboardingNavigatorDelegate {
func onboardingFinished()
}
......@@ -22,7 +22,7 @@ public class AmivRootNavigator: RootNavigator {
public init(window: UIWindow) {
self.window = window
self.goToApp()
self.goToOnboarding()
}
// MARK: - Navigation
......@@ -34,7 +34,18 @@ public class AmivRootNavigator: RootNavigator {
}
private func goToOnboarding() {
let onboarding = OnboardingNavigator()
onboarding.delegate = self
self.window.rootViewController = onboarding.rootViewController
self.currentNavigator = onboarding
}
}
extension AmivRootNavigator: OnboardingNavigatorDelegate {
public func onboardingFinished() {
self.goToApp()
}
}
//
// LoginModel.swift
// Amiv
//
// Created by Domenic Wüthrich on 19.09.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public struct LoginModel {
// MARK: - Variables
public let title: String
public let infoText: String
public let buttonTitle: String
public let smallButtonTitle: String
// MARK: - Initializers
public init(title: String, infoText: String, buttonTitle: String, smallButtonTitle: String) {
self.title = title
self.infoText = infoText
self.buttonTitle = buttonTitle
self.smallButtonTitle = smallButtonTitle
}
}
extension LoginModel {
public static func createOnboarding() -> LoginModel {
let infoText = "Login in using your ETHZ username (email address) and your ETHZ password."
return self.init(title: "Login", infoText: infoText, buttonTitle: "Login", smallButtonTitle: "Skip")
}
public static func createNormal() -> LoginModel {
let infoText = "Login in using your ETHZ username (email address) and your ETHZ password."
return self.init(title: "Login", infoText: infoText, buttonTitle: "Login", smallButtonTitle: "Cancel")
}
}
......@@ -15,7 +15,7 @@ public class LoginViewController: UIViewController {
public var delegate: LoginViewControllerDelegate?
private let infoLabelText: String = "Login in using your ETHZ username (email address) and your ETHZ password."
private let infoLabelText: String
// MARK: - View variables
......@@ -23,9 +23,9 @@ public class LoginViewController: UIViewController {
public private(set) var infoLabel: UILabel!
public private(set) var submitButton: UIButton!
public private(set) var button: UIButton!
public private(set) var cancelButton: UIButton!
public private(set) var smallButton: UIButton!
public private(set) var usernameTextField: UITextField!
......@@ -33,21 +33,22 @@ public class LoginViewController: UIViewController {
// MARK: - Initializers
public init() {
public init(model: LoginModel) {
self.infoLabelText = model.infoText
super.init(nibName: nil, bundle: nil)
// View Creation
self.titleLabel = self.createTitleLabel()
self.titleLabel = self.createTitleLabel(model.title)
self.view.addSubview(self.titleLabel)
self.infoLabel = self.createInfoTextLabel()
self.view.addSubview(self.infoLabel)
self.submitButton = self.createSubmitButton()
self.view.addSubview(self.submitButton)
self.button = self.createButton(model.buttonTitle)
self.view.addSubview(self.button)
self.cancelButton = self.createCancelButton()
self.view.addSubview(self.cancelButton)
self.smallButton = self.createSmallButton(model.smallButtonTitle)
self.view.addSubview(self.smallButton)
self.usernameTextField = self.createUsernameTextField()
self.view.addSubview(self.usernameTextField)
......@@ -60,8 +61,8 @@ public class LoginViewController: UIViewController {
self.applyInfoLabelConstraints()
self.applyUsernameTextFieldConstraints()
self.applyPasswordTextFieldConstraints()
self.applySubmitButtonConstraints()
self.applyCancelButtonConstraints()
self.applyButtonConstraints()
self.applySmallButtonConstraints()
// Tap view to hide keyboard
let recognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideKeyboard))
......@@ -74,9 +75,9 @@ public class LoginViewController: UIViewController {
// MARK: - View Creation
private func createTitleLabel() -> UILabel {
private func createTitleLabel(_ title: String) -> UILabel {
let label = UILabel()
label.text = "Login"
label.text = title
label.textColor = .amivRed
label.numberOfLines = 2
label.textAlignment = .center
......@@ -109,9 +110,9 @@ public class LoginViewController: UIViewController {
NSLayoutConstraint(item: self.infoLabel, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
}
private func createSubmitButton() -> UIButton {
private func createButton(_ title: String) -> UIButton {
let button = UIButton()
button.setTitle("Login", for: .normal)
button.setTitle(title, for: .normal)
button.setTitleColor(.white, for: .normal)
button.setTitleColor(.lightGray, for: .highlighted)
button.backgroundColor = .amivRed
......@@ -122,35 +123,36 @@ public class LoginViewController: UIViewController {
return button
}
private func applySubmitButtonConstraints() {
NSLayoutConstraint(item: self.submitButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.submitButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.submitButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottomMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.submitButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50).isActive = true
private func applyButtonConstraints() {
NSLayoutConstraint(item: self.button, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.button, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.button, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottomMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.button, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50).isActive = true
}
private func createCancelButton() -> UIButton {
private func createSmallButton(_ title: String) -> UIButton {
let button = UIButton()
button.setTitle("Cancel", for: .normal)
button.setTitle(title, for: .normal)
button.setTitleColor(.amivRed, for: .normal)
button.setTitleColor(.lightGray, for: .highlighted)
button.addTarget(self, action: #selector(self.cancel), for: .touchUpInside)
button.addTarget(self, action: #selector(self.smallButtonAction), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}
private func applyCancelButtonConstraints() {
NSLayoutConstraint(item: self.cancelButton, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: self.passwordTextField, attribute: .bottom, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: self.cancelButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.cancelButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.cancelButton, attribute: .bottom, relatedBy: .equal, toItem: self.submitButton, attribute: .top, multiplier: 1, constant: -15).isActive = true
private func applySmallButtonConstraints() {
NSLayoutConstraint(item: self.smallButton, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: self.passwordTextField, attribute: .bottom, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: self.smallButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.smallButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.smallButton, attribute: .bottom, relatedBy: .equal, toItem: self.button, attribute: .top, multiplier: 1, constant: -15).isActive = true
}
private func createUsernameTextField() -> UITextField {
let field = UITextField()
field.tintColor = .amivRed
field.placeholder = "Username"
field.autocapitalizationType = .none
field.returnKeyType = .continue
field.borderStyle = .roundedRect
field.addTarget(self, action: #selector(self.fillPassword), for: .primaryActionTriggered)
......@@ -170,6 +172,7 @@ public class LoginViewController: UIViewController {
field.tintColor = .amivRed
field.placeholder = "Password"
field.returnKeyType = .go
field.autocapitalizationType = .none
field.isSecureTextEntry = true
field.borderStyle = .roundedRect
field.addTarget(self, action: #selector(self.login), for: .primaryActionTriggered)
......@@ -192,6 +195,12 @@ public class LoginViewController: UIViewController {
self.view.backgroundColor = .white
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.isHidden = true
}
// MARK: - View Interaction
@objc private func login() {
......@@ -205,8 +214,8 @@ public class LoginViewController: UIViewController {
}
}
@objc private func cancel() {
self.delegate?.cancelLogin()
@objc private func smallButtonAction() {
self.delegate?.smallButtonTapped()
}
@objc private func fillPassword() {
......
......@@ -12,6 +12,6 @@ public protocol LoginViewControllerDelegate {
func login(username: String, password: String)
func cancelLogin()
func smallButtonTapped()
}
//
// InfoItemModel.swift
// Amiv
//
// Created by Domenic Wüthrich on 19.09.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
import UIKit
public struct InfoItemModel {
// MARK: - Variables
public let title: String
public let text: String
public let image: UIImage?
// MARK: - Initializers
public init(title: String, text: String, image: UIImage?) {
self.title = title
self.text = text
self.image = image
}
}
extension InfoItemModel {
public static func create() -> [InfoItemModel] {
let first = self.init(title: "Events", text: "Register for upcoming events.", image: nil)
let second = self.init(title: "Jobs", text: "Get informations about the latest and greates job offers.", image: nil)
let third = self.init(title: "Apps", text: "Use microapps like 'Barcode ID' and 'AMIV Helper Apps'.", image: nil)
return [first, second, third]
}
}
//
// InfoItemView.swift
// Amiv
//
// Created by Domenic Wüthrich on 19.09.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
import UIKit
public class InfoItemView: UIView {
// MARK: - Variables
// MARK: - View Variables
public private(set) var imageView: UIImageView!
public private(set) var titleLabel: UILabel!
public private(set) var infoLabel: UILabel!
// MARK: - Initializers
public init(frame: CGRect = .null, model: InfoItemModel) {
super.init(frame: frame)
// Create View
self.imageView = self.createImageView(model.image)
self.addSubview(self.imageView)
self.titleLabel = self.createTitleLabel(model.title)
self.addSubview(self.titleLabel)
self.infoLabel = self.createInfoLabel(model.text)
self.addSubview(self.infoLabel)
// Apply Constraints
self.applyImageViewConstraints()
self.applyTitleLabelConstraints()
self.applyInfoLabelConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View Creation
private func createImageView(_ image: UIImage?) -> UIImageView {
let imageView = UIImageView()
imageView.image = image
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}
private func applyImageViewConstraints() {
NSLayoutConstraint(item: self.imageView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .topMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.imageView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.imageView, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: self, attribute: .bottomMargin, multiplier: 1, constant: -10).isActive = true
NSLayoutConstraint(item: self.imageView, attribute: .height, relatedBy: .equal, toItem: self.imageView, attribute: .width, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: self.imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40).isActive = true
}
private func createTitleLabel(_ title: String) -> UILabel {
let label = UILabel()
label.numberOfLines = 1
label.text = title
label.textColor = .amivRed
label.font = UIFont.preferredFont(forTextStyle: .body).bold()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
private func applyTitleLabelConstraints() {
NSLayoutConstraint(item: self.titleLabel, attribute: .top, relatedBy: .equal, toItem: self.imageView, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: self.titleLabel, attribute: .leading, relatedBy: .equal, toItem: self.imageView, attribute: .trailing, multiplier: 1, constant: 20).isActive = true
NSLayoutConstraint(item: self.titleLabel, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
}
private func createInfoLabel(_ text: String) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.text = text
label.font = UIFont.preferredFont(forTextStyle: .caption1)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
private func applyInfoLabelConstraints() {
NSLayoutConstraint(item: self.infoLabel, attribute: .top, relatedBy: .equal, toItem: self.titleLabel, attribute: .bottom, multiplier: 1, constant: 8).isActive = true
NSLayoutConstraint(item: self.infoLabel, attribute: .leading, relatedBy: .equal, toItem: self.titleLabel, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: self.infoLabel, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: self, attribute: .bottomMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: self.infoLabel, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailingMargin, multiplier: 1, constant: -10).isActive = true
}
// MARK: - View Interaction
}
//
// InfoViewController.swift
// Amiv
//
// Created by Domenic Wüthrich on 19.09.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
import UIKit