Skip to content

Instantly share code, notes, and snippets.

@DonMag
Created December 5, 2019 17:37
Show Gist options
  • Save DonMag/1bab48ea53b26419d59de635e7692e4b to your computer and use it in GitHub Desktop.
Save DonMag/1bab48ea53b26419d59de635e7692e4b to your computer and use it in GitHub Desktop.
//
// HomeViewController.swift
// Wishlist
//
// Created by Christian Konnerth on 22.08.19.
// Copyright © 2019 CKBusiness. All rights reserved.
//
import UIKit
struct CustomData {
var title: String
var image: UIImage
}
// MARK: Main Wishlist cell
class MainWishlistCell: UICollectionViewCell {
let btn: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let wishlistImage: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.image = UIImage(named: "iconRoundedImage")
v.layer.shadowOpacity = 1
v.layer.shadowOffset = CGSize(width: 1.5, height: 1.5)
v.layer.shadowRadius = 3
v.layer.shadowColor = UIColor.darkGray.cgColor
return v
}()
let wishlistLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.text = "Main Wishlist"
v.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
v.textColor = .darkGray
v.textAlignment = .center
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
}
func commonInit() -> Void {
contentView.addSubview(wishlistImage)
contentView.addSubview(wishlistLabel)
contentView.addSubview(btn)
// constrain view to all 4 sides
NSLayoutConstraint.activate([
wishlistImage.topAnchor.constraint(equalTo: contentView.topAnchor),
wishlistImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
wishlistImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
wishlistImage.heightAnchor.constraint(equalToConstant:150),
wishlistLabel.topAnchor.constraint(equalTo: wishlistImage.bottomAnchor,constant: 1),
wishlistLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
wishlistLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
wishlistLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
btn.topAnchor.constraint(equalTo: contentView.topAnchor),
btn.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
btn.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
btn.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
btn.addTarget(self, action: #selector(mainWishlistTapped(_:)), for: .touchUpInside)
}
var wishlistTapCallback: (() -> ())?
@objc func mainWishlistTapped(_ sender: Any) {
// tell the collection view controller we got a button tap
wishlistTapCallback?()
}
}
// MARK: Simple Whishlist Cell
class ContentCell: UICollectionViewCell {
let buttonView: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.layer.shadowOpacity = 1
v.layer.shadowOffset = CGSize(width: 1.5, height: 1.5)
v.layer.shadowRadius = 3
v.layer.shadowColor = UIColor.darkGray.cgColor
return v
}()
// let testImage: UIImageView = {
// let v = UIImageView()
// v.translatesAutoresizingMaskIntoConstraints = false
// v.layer.shadowOpacity = 1
// v.layer.shadowOffset = CGSize(width: 1.5, height: 1.5)
// v.layer.shadowRadius = 3
// v.layer.shadowColor = UIColor.darkGray.cgColor
// return v
// }()
let theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
return v
}()
let testLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.text = "Test Label"
v.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
v.textColor = .darkGray
v.textAlignment = .center
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// contentView.addSubview(theLabel)
contentView.layer.cornerRadius = 3.0;
contentView.addSubview(testLabel)
contentView.addSubview(buttonView)
// constrain label to all 4 sides
NSLayoutConstraint.activate([
// theLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
// theLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
// theLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
// theLabel.heightAnchor.constraint(equalToConstant:150),
buttonView.topAnchor.constraint(equalTo: contentView.topAnchor),
buttonView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
buttonView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
buttonView.heightAnchor.constraint(equalToConstant:150),
testLabel.topAnchor.constraint(equalTo: buttonView.bottomAnchor,constant: 1),
testLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
testLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
testLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
buttonView.addTarget(self, action: #selector(customWishlistTapped(_:)), for: .touchUpInside)
}
var customWishlistTapCallback: (() -> ())?
@objc func customWishlistTapped(_ sender: Any) {
// tell the collection view controller we got a button tap
customWishlistTapCallback?()
}
}
// MARK: Add WishList Cell
class AddItemCell: UICollectionViewCell {
let btn: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitleColor(.darkGray, for: .normal)
v.titleLabel?.font = UIFont.systemFont(ofSize: 40.0)
return v
}()
let label: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.text = "neue Wishlist erstellen"
v.numberOfLines = 0
v.font = UIFont(name: "AvenirNext", size: 20)
v.textColor = .darkGray
v.textAlignment = .center
return v
}()
let plusLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.text = "+"
v.font = UIFont(name: "AvenirNext-Bold", size: 30)
v.textColor = .white
v.textAlignment = .center
return v
}()
// this will be used as a "callback closure" in collection view controller
var tapCallback: (() -> ())?
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
contentView.layer.cornerRadius = 3.0;
contentView.addSubview(btn)
contentView.addSubview(label)
contentView.addSubview(plusLabel)
// constrain button to all 4 sides
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: contentView.topAnchor),
btn.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
btn.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
btn.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: -50),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
plusLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
plusLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 20),
plusLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
plusLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
}
@objc func didTap(_ sender: Any) {
// tell the collection view controller we got a button tap
tapCallback?()
}
}
// DonMag3 - protocol / delegate pattern
// allows wish table view (and cell) to update wish list data
protocol DeleteWishDelegate {
func deleteWish(_ idx: Int)
}
// MARK: ViewController
// DonMag3 - conform to DeleteWishDelegate protocol
class ExampleViewController: UIViewController, UICollectionViewDataSource, DeleteWishDelegate {
@IBOutlet weak var backGroundImage: UIImageView!
@IBOutlet weak var welcomeTextLeftConstraint: NSLayoutConstraint!
@IBOutlet weak var welcomeTextLabel: UILabel!
@IBOutlet weak var listNameTextfield: UITextField!
@IBOutlet weak var blurrImage: UIImageView!
@IBOutlet weak var newListView: UIView!
@IBOutlet weak var newListTextfield: UITextField!
@IBOutlet weak var createListButton: UIButton!
@IBOutlet weak var editButton: UIButton!
@IBOutlet weak var imagePreview: UIImageView!
@IBOutlet weak var containerView: UIView!
// MARK: CustomWishlistView
// MARK: WishListView
let wishlistView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .darkGray
v.layer.cornerRadius = 30
return v
}()
lazy var theTableView: WhishlistTableViewController = {
let v = WhishlistTableViewController()
v.view.layer.masksToBounds = true
v.view.layer.borderColor = UIColor.white.cgColor
v.view.backgroundColor = .clear
v.view.layer.borderWidth = 7.0
v.view.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let dismissWishlistViewButton: UIButton = {
let v = UIButton()
v.setImage(UIImage(named: "dropdown"), for: .normal)
v.translatesAutoresizingMaskIntoConstraints = false
v.addTarget(self, action: #selector(hideView), for: .touchUpInside)
return v
}()
let menueButton: UIButton = {
let v = UIButton()
v.setImage(UIImage(named: "menueButton"), for: .normal)
v.translatesAutoresizingMaskIntoConstraints = false
v.addTarget(self, action: #selector(menueButtonTapped), for: .touchUpInside)
return v
}()
let wishlistLabel: UILabel = {
let v = UILabel()
v.text = "Main Wishlist"
v.font = UIFont(name: "AvenirNext-Bold", size: 30)
v.textColor = .white
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let wishCounterLabel: UILabel = {
let v = UILabel()
v.text = "5 unerfüllte Wünsche"
v.font = UIFont(name: "AvenirNext", size: 12)
v.textColor = .white
v.font = v.font.withSize(12)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let wishlistImage: UIImageView = {
let v = UIImageView()
v.image = UIImage(named: "iconRoundedImage")
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
// MARK: PopUpView
let popUpView: PopUpView = {
let v = PopUpView()
v.layer.cornerRadius = 10
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let wishButton: UIButton = {
let v = UIButton()
v.setBackgroundImage(UIImage(named: "wishButton"), for: .normal)
v.translatesAutoresizingMaskIntoConstraints = false
v.addTarget(self, action: #selector(wishButtonTapped), for: .touchUpInside)
v.clipsToBounds = true
// v.contentVerticalAlignment = .fill
// v.contentHorizontalAlignment = .fill
return v
}()
let visualEffectView: UIVisualEffectView = {
let blurrEffect = UIBlurEffect(style: .light)
let v = UIVisualEffectView(effect: blurrEffect)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
// MARK: CollectionView
let theCollectionView: UICollectionView = {
let v = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor(red: 225/255.0, green: 215/255.0, blue: 200/255.0, alpha: 1)
v.contentInsetAdjustmentBehavior = .always
v.layer.cornerRadius = 30
return v
}()
let columnLayout = FlowLayout(
itemSize: CGSize(width: 150, height: 150),
minimumInteritemSpacing: 20,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 20, left: 20, bottom: 10, right: 20)
)
// track collection view frame change
var colViewWidth: CGFloat = 0.0
// collectionView data, Image + Label
var wishListTitlesArray: [String] = [String]()
var wishListImagesArray: [UIImage] = [UIImage]()
var image: UIImage?
func styleTextField(_ textfield:UITextField) {
}
private let imageView = UIImageView()
private var imageTimer: Timer?
private let images: [UIImage] = [
UIImage(named: "avocadoImage")!,
UIImage(named: "beerImage")!,
UIImage(named: "bikeImage")!,
UIImage(named: "christmasImage")!,
UIImage(named: "dressImage")!,
UIImage(named: "giftImage")!,
UIImage(named: "goalImage")!,
UIImage(named: "rollerImage")!,
UIImage(named: "shirtImage")!,
UIImage(named: "shoeImage")!,
UIImage(named: "travelImage")!,
]
// DonMag3 - array of wish lists
// this will eventually be managed by some type of data handler class
var userWishListData: [[Wish]] = [[Wish]]()
// DonMag3 - track the current selected wish list
var currentWishListIDX: Int = 0
// MARK: viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
imagePreview.image = UIImage(named: "iconRoundedImage")
image = UIImage(named: "iconRoundedImage")
// set up popUpView
self.createListButton.layer.cornerRadius = 2
self.listNameTextfield.tintColor = .lightGray
self.listNameTextfield.addLine(position: .LINE_POSITION_BOTTOM, color: .lightGray, width: 1.5)
self.blurrImage.transform = CGAffineTransform(translationX: 0, y: 1000)
self.newListView.transform = CGAffineTransform(translationX: 0, y: 1000)
// set CollectionView to bottom
self.theCollectionView.transform = CGAffineTransform(translationX: 0, y: 500)
// animate collectionView
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
self.theCollectionView.transform = CGAffineTransform(translationX: 0, y: 0)
})
// animate welcomeLabel
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: .curveEaseIn, animations: {
self.welcomeTextLabel.transform = CGAffineTransform(translationX: 278, y: 0)
})
// hide wishlistView
self.wishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
// add slideDown-gesture to WishlistView
let slideDown = UISwipeGestureRecognizer(target: self, action: #selector(dismissView(gesture:)))
slideDown.direction = .down
view.addGestureRecognizer(slideDown)
// adding notification from `ContainerViewController` so `addButtonTapped` is accessable here
NotificationCenter.default.addObserver(self, selector: #selector(self.addWishButtonTapped(notification:)), name: Notification.Name("addWishButtonTapped"), object: nil)
// MARK: Views + Constraints
view.addSubview(theCollectionView)
view.addSubview(wishlistView)
wishlistView.addSubview(dismissWishlistViewButton)
wishlistView.addSubview(menueButton)
wishlistView.addSubview(wishlistLabel)
wishlistView.addSubview(wishlistImage)
wishlistView.addSubview(wishCounterLabel)
wishlistView.addSubview(theTableView.tableView)
addChild(theTableView)
NSLayoutConstraint.activate([
// constrain collectionView
theCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 180.0),
theCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
theCollectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
theCollectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
// constrain wishlistView
wishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120.0),
wishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
wishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
wishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
// constrain tableView
theTableView.view.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: 180.0),
theTableView.view.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: 0),
theTableView.view.leadingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
theTableView.view.trailingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
// constrain dropDownButton
dismissWishlistViewButton.topAnchor.constraint(equalTo: wishlistView.topAnchor),
dismissWishlistViewButton.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: -650),
dismissWishlistViewButton.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor),
dismissWishlistViewButton.trailingAnchor.constraint(equalTo: wishlistView.trailingAnchor, constant: -260),
// constrain menueButton
menueButton.topAnchor.constraint(equalTo: wishlistView.topAnchor),
menueButton.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: -650),
menueButton.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor, constant: 260),
menueButton.trailingAnchor.constraint(equalTo: wishlistView.trailingAnchor),
// constrain wishlistImage
wishlistImage.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: -570),
wishlistImage.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor, constant: 30),
wishlistImage.widthAnchor.constraint(equalToConstant: 80),
wishlistImage.heightAnchor.constraint(equalToConstant: 80),
//constrain wishlistlabel
wishlistLabel.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: -600),
wishlistLabel.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor, constant: 115),
// constrain wishCounterLabel
wishCounterLabel.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: -585),
wishCounterLabel.leadingAnchor.constraint(equalTo: wishlistView.leadingAnchor, constant: 115),
])
// register the two cell classes for reuse
theCollectionView.register(ContentCell.self, forCellWithReuseIdentifier: "ContentCell")
theCollectionView.register(AddItemCell.self, forCellWithReuseIdentifier: "AddItemCell")
theCollectionView.register(MainWishlistCell.self, forCellWithReuseIdentifier: "MainWishlistCell")
// set collection view dataSource
theCollectionView.dataSource = self
// use custom flow layout
theCollectionView.collectionViewLayout = columnLayout
// add observer to starte/stop timer for imagePreview-rotation
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackgroundHandler), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForegroundHandler), name: UIApplication.willEnterForegroundNotification, object: nil)
self.view.sendSubviewToBack(wishlistView)
self.view.sendSubviewToBack(theCollectionView)
self.view.sendSubviewToBack(backGroundImage)
// DonMag3 - set DeleteWishDelegate protocol for the table
theTableView.deleteWishDelegate = self
// DonMag 3 - hide collection view while data is retirieved from server
theCollectionView.isHidden = true
// DonMag 3 - get the data from the server
retrieveUserDataFromDB()
}
// DonMag3 - simulate retrieving user wishlists from database
// when you're further along in development, you may be retrieving
// this data when you log-in the user
func retrieveUserDataFromDB() -> Void {
// show a spinner Activity Indicator
let spinnerView = UIActivityIndicatorView(style: .whiteLarge)
spinnerView.backgroundColor = UIColor(white: 1.0, alpha: 0.5)
spinnerView.translatesAutoresizingMaskIntoConstraints = false
spinnerView.layer.cornerRadius = 20.0
view.addSubview(spinnerView)
NSLayoutConstraint.activate([
spinnerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
spinnerView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
spinnerView.widthAnchor.constraint(equalToConstant: 120),
spinnerView.heightAnchor.constraint(equalToConstant: 120),
])
spinnerView.startAnimating()
// simulate 1.0 seconds to retrieve data
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
// local mutable "WishList" var
var wList: [Wish] = [Wish]()
// simlate user data from server
var retrievedData: [[String : Any]] = [
[
"listName" : "Main Wishlist",
"listImageIndex" : -1,
"wishes" : ["Ein Auto", "Ein Fahrrad"]
],
[
"listName" : "My List",
"listImageIndex" : 1,
"wishes" : ["Ein Bier", "Kuemmerling", "Viele Freunde"]
]
]
// un-comment next line to simulate First-Time user (no saved data yet)
//retrievedData = [["" : ""]]
var hasUserData = false
if let _ = retrievedData.first?["listName"] {
hasUserData = true
}
if hasUserData {
retrievedData.forEach { userData in
// make sure the data is valid
guard let listName = userData["listName"] as? String,
let listImageIDX = userData["listImageIndex"] as? Int,
let wishes = userData["wishes"] as? [String]
else { fatalError("Bad Data - implement better error handling") }
self.wishListTitlesArray.append(listName)
if listImageIDX == -1 {
self.wishListImagesArray.append(UIImage(named: "iconRoundedImage")!)
} else {
self.wishListImagesArray.append(self.images[1])
}
wList = [Wish]()
wishes.forEach {
wList.append(Wish(withWishName: $0, checked: false))
}
self.userWishListData.append(wList)
}
} else {
// no data from server, so this is a first time user
// data will ALWAYS start with "Main Wishlist"
self.wishListTitlesArray.append("Main Wishlist")
self.wishListImagesArray.append(UIImage(named: "iconRoundedImage")!)
// create an empty wishlist, in case this is a new user
wList = [Wish]()
self.userWishListData.append(wList)
}
// un-hide the collection view
self.theCollectionView.isHidden = false
// remove the activity view
spinnerView.removeFromSuperview()
// reload the collection view
self.theCollectionView.reloadData()
}
}
// MARK: ImageRotation-Functions
@objc private func appDidEnterBackgroundHandler() {
if imageTimer != nil {
imageTimer!.invalidate()
imageTimer = nil
}
}
@objc private func appWillEnterForegroundHandler() {
startImageTimer()
}
private func startImageTimer() {
// instantiate timer
imageTimer = Timer(fire: Date(), interval: 2.5, repeats: true) { (timer) in
UIView.transition(with: self.imagePreview,
duration: 0.5,
options: .transitionCrossDissolve,
animations: {
let imageStore = self.images.randomElement()
self.imagePreview.image = imageStore
self.image = imageStore
},
completion: nil)
}
// add to run loop
RunLoop.main.add(imageTimer!, forMode: .common)
}
// MARK: CollectionView
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
// change "sender: Any" to "sender: Any?" so we can call this
// from "Liste erstellen" button tap
@IBAction func closeButtonTappedNewList(_ sender: Any?) {
self.appDidEnterBackgroundHandler()
self.newListTextfield.resignFirstResponder()
self.listNameTextfield.text = ""
// let newListView disappear
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
self.blurrImage.transform = CGAffineTransform(translationX: 0, y: 1000)
self.newListView.transform = CGAffineTransform(translationX: 0, y: 1000)
self.view.layoutIfNeeded()
})
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// only want to call this when collection view frame changes
// to set the item size
if theCollectionView.frame.width != colViewWidth {
let w = theCollectionView.frame.width / 2 - 30
columnLayout.itemSize = CGSize(width: w, height: w + 50)
colViewWidth = theCollectionView.frame.width
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// return 1 more than our data array (the extra one will be the "add item" cell)
return wishListTitlesArray.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// DonMag3 - "Main Wishlist" is now at item Zero in the
// wish lists array, so no need to treat it differently than
// any other list
// if indexPath.item is less than data count, return a "Content" cell
if indexPath.item < wishListTitlesArray.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath) as! ContentCell
cell.testLabel.text = wishListTitlesArray[indexPath.item]
cell.buttonView.setImage(wishListImagesArray[indexPath.item], for: .normal)
cell.customWishlistTapCallback = {
// let wishlistView appear
// DonMag3 - track selected index
self.currentWishListIDX = indexPath.item
// update label in wishList view
self.wishlistLabel.text = self.wishListTitlesArray[indexPath.item]
// update image in wishList view
self.wishlistImage.image = self.wishListImagesArray[indexPath.item]
// update the data for in wishList table view
self.theTableView.wishList = self.userWishListData[indexPath.item]
// reload wishList table
self.theTableView.tableView.reloadData()
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
self.wishlistView.transform = CGAffineTransform(translationX: 0, y: 0)
})
// let welcomeText disappear
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
self.welcomeTextLabel.transform = CGAffineTransform(translationX: 0, y: 0)
})
}
return cell
}
// past the end of the data count, so return an "Add Item" cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddItemCell", for: indexPath) as! AddItemCell
// set the closure
cell.tapCallback = {
self.listNameTextfield.becomeFirstResponder()
// let newListView appear
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
self.blurrImage.alpha = 0.96
self.blurrImage.transform = CGAffineTransform(translationX: 0, y: 0)
self.newListView.transform = CGAffineTransform(translationX: 0, y: 0)
self.view.layoutIfNeeded()
})
self.appWillEnterForegroundHandler()
}
return cell
}
// MARK: CreateNewListView
//hide keyboard, wenn user außerhalb toucht
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
// lazy var theCustomWishlistView: CustomWishlistView = {
// let v = CustomWishlistView()
// v.translatesAutoresizingMaskIntoConstraints = false
// v.backgroundColor = .darkGray
// v.layer.cornerRadius = 30
// return v
// }()
func createCustomWishlistView() -> CustomWishlistView {
let v = CustomWishlistView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .darkGray
v.layer.cornerRadius = 30
return v
}
@IBAction func createListButtonTapped(_ sender: Any) {
// "Liste erstellen" button was tapped
self.appDidEnterBackgroundHandler()
if let txt = listNameTextfield.text {
self.newListTextfield.resignFirstResponder()
// append user-entered text to the data array
self.wishListTitlesArray.append(txt)
self.wishListImagesArray.append(self.image!)
// DonMag3 - append new empty wish array
self.userWishListData.append([Wish]())
let theCustomWishlistView = createCustomWishlistView()
self.view.addSubview(theCustomWishlistView)
// constrain CustomWishlistView
theCustomWishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120.0).isActive = true
theCustomWishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
theCustomWishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0).isActive = true
theCustomWishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0).isActive = true
theCustomWishlistView.wishlistImage.image = self.image
theCustomWishlistView.wishlistLabel.text = txt
theCustomWishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
self.view.bringSubviewToFront(containerView)
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: {
(result) in
// scroll to make newly added row visible (if needed)
let i = self.theCollectionView.numberOfItems(inSection: 0) - 1
let idx = IndexPath(item: i, section: 0)
self.theCollectionView.scrollToItem(at: idx, at: .bottom, animated: true)
// close (hide) the "New List" view
self.closeButtonTappedNewList(nil)
})
}
}
@IBAction func editButtonTapped(_ sender: Any) {
let imageCollectionView = self.storyboard?.instantiateViewController(withIdentifier: "ImageCollectionVC") as! ImageCollectionViewController
imageCollectionView.delegate = self
if (self.navigationController == nil){
print("fuck")
}
self.navigationController?.present(imageCollectionView, animated: true)
}
// MARK: WishlistView
// swipe down to dismiss
@objc func dismissView(gesture: UISwipeGestureRecognizer) {
hideView()
}
@objc func hideView(){
//animate welcomeLabel
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
// show welcomeText
self.welcomeTextLabel.transform = CGAffineTransform(translationX: 278, y: 0)
// hide wishlistView
self.wishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
})
UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseOut, animations: {
// hide wishlistView
self.wishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
})
}
@objc func menueButtonTapped(){
print("menueButton tapped")
}
// MARK: wishPopUpView
@objc func addWishButtonTapped(notification : Notification){
popUpView.popUpTextField.text = ""
view.addSubview(visualEffectView)
view.addSubview(popUpView)
view.addSubview(wishButton)
// constrain blurrEffectView
visualEffectView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
visualEffectView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
visualEffectView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// constrain popUpView
popUpView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
popUpView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
popUpView.heightAnchor.constraint(equalToConstant: 200).isActive = true
popUpView.widthAnchor.constraint(equalToConstant: view.frame.width - 85).isActive = true
// constrain wishButton
wishButton.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
wishButton.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: 65).isActive = true
wishButton.heightAnchor.constraint(equalToConstant: 72).isActive = true
wishButton.widthAnchor.constraint(equalToConstant: 72).isActive = true
self.view.bringSubviewToFront(visualEffectView)
self.view.bringSubviewToFront(popUpView)
self.view.bringSubviewToFront(wishButton)
popUpView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
popUpView.alpha = 0
wishButton.alpha = 0
visualEffectView.alpha = 0
UIView.animate(withDuration: 0.3) {
self.visualEffectView.alpha = 1
self.wishButton.alpha = 1
self.popUpView.alpha = 1
self.popUpView.transform = CGAffineTransform.identity
}
}
@objc func wishButtonTapped(){
dismissPopUpView()
insertWish()
}
func dismissPopUpView(){
UIView.animate(withDuration: 0.3, animations: {
self.popUpView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.wishButton.alpha = 0
self.popUpView.alpha = 0
self.visualEffectView.alpha = 0
}) { (_) in
self.popUpView.removeFromSuperview()
self.wishButton.removeFromSuperview()
self.visualEffectView.removeFromSuperview()
}
}
func insertWish(){
// DonMag3 - append the new wish to the user's currently selected wishlist
userWishListData[currentWishListIDX].append(Wish(withWishName: popUpView.whishName!, checked: false))
// set the updated data as the data for the table view
theTableView.wishList = userWishListData[currentWishListIDX]
theTableView.tableView.reloadData()
}
func deleteWish(_ idx: Int){
// DonMag3 - remove the wish from the user's currently selected wishlist
var wishes: [Wish] = userWishListData[currentWishListIDX]
wishes.remove(at: idx)
userWishListData[currentWishListIDX] = wishes
// set the updated data as the data for the table view
theTableView.wishList = userWishListData[currentWishListIDX]
theTableView.tableView.reloadData()
}
}
extension ExampleViewController: ClassBDelegate {
func childVCDidComplete( with image: UIImage?) {
self.image = image!
self.imagePreview.image = image!
self.appDidEnterBackgroundHandler()
}
}
// MARK: Custom Flowlayout
// custom FlowLayout class to left-align collection view cells
// found here: https://stackoverflow.com/a/49717759/6257435
class FlowLayout: UICollectionViewFlowLayout {
required init(itemSize: CGSize, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
super.init()
self.itemSize = itemSize
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
sectionInsetReference = .fromSafeArea
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
guard scrollDirection == .vertical else { return layoutAttributes }
// Filter attributes to compute only cell attributes
let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
// Group cell attributes by row (cells with same vertical center) and loop on those groups
for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
// Set the initial left inset
var leftInset = sectionInset.left
// Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
for attribute in attributes {
attribute.frame.origin.x = leftInset
leftInset = attribute.frame.maxX + minimumInteritemSpacing
}
}
return layoutAttributes
}
}
// MARK: CenterFlowLayout
class CenterAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
override var itemSize: CGSize {
get { return CGSize(width: 150, height: 150) }
set {}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let superAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
// Copy each item to prevent "UICollectionViewFlowLayout has cached frame mismatch" warning
guard let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
// Constants
let leftPadding: CGFloat = 8
let interItemSpacing = minimumInteritemSpacing
// Tracking values
var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item
var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item
var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row
var currentRow: Int = 0 // Tracks the current row
attributes.forEach { layoutAttribute in
// Each layoutAttribute represents its own item
if layoutAttribute.frame.origin.y >= maxY {
// This layoutAttribute represents the left-most item in the row
leftMargin = leftPadding
// Register its origin.x in rowSizes for use later
if rowSizes.count == 0 {
// Add to first row
rowSizes = [[leftMargin, 0]]
} else {
// Append a new row
rowSizes.append([leftMargin, 0])
currentRow += 1
}
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + interItemSpacing
maxY = max(layoutAttribute.frame.maxY, maxY)
// Add right-most x value for last item in the row
rowSizes[currentRow][1] = leftMargin - interItemSpacing
}
// At this point, all cells are left aligned
// Reset tracking values and add extra left padding to center align entire row
leftMargin = leftPadding
maxY = -1.0
currentRow = 0
attributes.forEach { layoutAttribute in
// Each layoutAttribute is its own item
if layoutAttribute.frame.origin.y >= maxY {
// This layoutAttribute represents the left-most item in the row
leftMargin = leftPadding
// Need to bump it up by an appended margin
let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x
let appendedMargin = (collectionView!.frame.width - leftPadding - rowWidth - leftPadding) / 2
leftMargin += appendedMargin
currentRow += 1
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + interItemSpacing
maxY = max(layoutAttribute.frame.maxY, maxY)
}
return attributes
}
}
//
// WhishCell.swift
// Wishlist
//
// Created by Christian Konnerth on 22.11.19.
// Copyright © 2019 CKBusiness. All rights reserved.
//
import UIKit
class WhishCell: UITableViewCell {
// DonMag3 - change "callback" to "deleteWishCallback" so we know what its purpose
var deleteWishCallback : (() -> ())?
let label: UILabel = {
let v = UILabel()
v.font = UIFont(name: "AvenirNext", size: 23)
v.textColor = .white
v.font = v.font.withSize(23)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let checkButton: UIButton = {
let v = UIButton()
v.backgroundColor = .darkGray
// v.layer.borderColor = UIColor.red.cgColor
// v.layer.borderWidth = 2.0
v.translatesAutoresizingMaskIntoConstraints = false
v.setBackgroundImage(UIImage(named: "boxUnchecked"), for: .normal)
return v
}()
public static let reuseID = "WhishCell"
required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .clear
// add checkButton
self.contentView.addSubview(checkButton)
self.checkButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
self.checkButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
self.checkButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
self.checkButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.checkButton.addTarget(self, action: #selector(checkButtonTapped), for: .touchUpInside)
// add label
self.contentView.addSubview(label)
self.label.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 70).isActive = true
self.label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
@objc func checkButtonTapped(){
self.checkButton.setBackgroundImage(UIImage(named: "boxChecked"), for: .normal)
self.checkButton.alpha = 0
self.checkButton.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
UIView.animate(withDuration: 0.3, animations: {
self.checkButton.alpha = 1
self.checkButton.transform = CGAffineTransform.identity
}) { (_) in
// DonMag3 - tell the callback to delete this wish
self.deleteWishCallback?()
}
}
}
// extension for "check" function
extension UITableViewCell {
var tableView: UITableView? {
return (next as? UITableView) ?? (parentViewController as? UITableViewController)?.tableView
}
var indexPath: IndexPath? {
return tableView?.indexPath(for: self)
}
}
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
//
// WhishlistTableViewController.swift
// Wishlist
//
// Created by Christian Konnerth on 22.11.19.
// Copyright © 2019 CKBusiness. All rights reserved.
//
import UIKit
class WhishlistTableViewController: UITableViewController {
public var wishList = [Wish]()
// DonMag3 - protocol / delegate pattern
public var deleteWishDelegate: DeleteWishDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// disable prefill tableview with cells
let v = UIView()
v.backgroundColor = .clear
tableView.tableFooterView = v
// disable didSelectAt
self.tableView.allowsSelection = false
self.tableView.register(WhishCell.self, forCellReuseIdentifier: WhishCell.reuseID)
// DonMag3 - wishList will come from user data, so don't append a "test" wish
//self.wishList.append(Wish(withWishName: "Test", checked: false))
// add top inset for tavleview
self.tableView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wishList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath) as! WhishCell
let currentWish = self.wishList[indexPath.row]
cell.label.text = currentWish.wishName
cell.backgroundColor = .clear
cell.checkButton.setBackgroundImage(UIImage(named: "boxUnchecked"), for: .normal)
// DonMag3 - tapping the checkbox in the wish cell will call back here
// and we tell the delegate to delete the wish
cell.deleteWishCallback = {
self.deleteWishDelegate?.deleteWish(indexPath.row)
}
return cell
}
}
class Wish: NSObject {
public var wishName : String?
public var checkedStatus : Bool?
init(withWishName name: String, checked: Bool) {
super.init()
wishName = name
checkedStatus = checked
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment