diff --git a/.DS_Store b/.DS_Store index ae19b49..a2a8bad 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/Marketplace/.DS_Store b/Marketplace/.DS_Store index 963ab4f..57b34a6 100644 Binary files a/Marketplace/.DS_Store and b/Marketplace/.DS_Store differ diff --git a/Marketplace/.swiftformat b/Marketplace/.swiftformat new file mode 100644 index 0000000..629e780 --- /dev/null +++ b/Marketplace/.swiftformat @@ -0,0 +1,6 @@ +--indent 4 # Отступы в 4 пробела +--linebreaks crlf # Использует Unix-стиль перевода строк (LF вместо CRLF) +--wraparguments before-first # Переносит аргументы на новую строку +--stripunusedargs closure-only # Удаляет неиспользуемые аргументы только у замыканий +--commas inline # Оставляет запятые в той же строке +--ranges spaced # Оставляет пробелы вокруг операторов диапазона (1 ... 10) \ No newline at end of file diff --git a/Marketplace/CoreData/ProductData+CoreDataClass.swift b/Marketplace/CoreData/ProductData+CoreDataClass.swift index 255d52a..5fffe9a 100644 --- a/Marketplace/CoreData/ProductData+CoreDataClass.swift +++ b/Marketplace/CoreData/ProductData+CoreDataClass.swift @@ -1,15 +1,12 @@ -// -// ProductData+CoreDataClass.swift -// Marketplace -// -// Created by Алексей Кобяков on 22.10.2022. -// -// - -import Foundation -import CoreData - - -public class ProductData: NSManagedObject { - -} +// +// ProductData+CoreDataClass.swift +// Marketplace +// +// Created by Алексей Кобяков on 22.10.2022. +// +// + +import CoreData +import Foundation + +public class ProductData: NSManagedObject {} diff --git a/Marketplace/CoreData/ProductData+CoreDataProperties.swift b/Marketplace/CoreData/ProductData+CoreDataProperties.swift index 4f36bfd..b6b20ca 100644 --- a/Marketplace/CoreData/ProductData+CoreDataProperties.swift +++ b/Marketplace/CoreData/ProductData+CoreDataProperties.swift @@ -1,28 +1,23 @@ -// -// ProductData+CoreDataProperties.swift -// Marketplace -// -// Created by Алексей Кобяков on 22.10.2022. -// -// - -import Foundation -import CoreData - - -extension ProductData { - - @nonobjc public class func fetchRequest() -> NSFetchRequest { - return NSFetchRequest(entityName: "ProductData") - } - - @NSManaged public var productImage: Data? - @NSManaged public var productTitle: String? - @NSManaged public var productPrice: String? - @NSManaged public var productDescription: String? - -} - -extension ProductData : Identifiable { - -} +// +// ProductData+CoreDataProperties.swift +// Marketplace +// +// Created by Алексей Кобяков on 22.10.2022. +// +// + +import CoreData +import Foundation + +public extension ProductData { + @nonobjc class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "ProductData") + } + + @NSManaged var productImage: Data? + @NSManaged var productTitle: String? + @NSManaged var productPrice: String? + @NSManaged var productDescription: String? +} + +extension ProductData: Identifiable {} diff --git a/Marketplace/File.swift b/Marketplace/File.swift index b512f7c..cbb763a 100644 --- a/Marketplace/File.swift +++ b/Marketplace/File.swift @@ -1,18 +1,15 @@ -// -// File.swift -// Marketplace -// -// Created by Алексей Кобяков on 17.06.2024. -// - -import Foundation - -final class MyClass: Sendable { - - @Sendable func loadPages() async { - await withTaskGroup(of: Void.self) { group in - - } - } - -} +// +// File.swift +// Marketplace +// +// Created by Алексей Кобяков on 17.06.2024. +// + +import Foundation + +final class MyClass: Sendable { + @Sendable func loadPages() async { + await withTaskGroup(of: Void.self) { _ in + } + } +} diff --git a/Marketplace/Marketplace.xcodeproj/project.pbxproj b/Marketplace/Marketplace.xcodeproj/project.pbxproj index ee86c6b..3b7b6d2 100644 --- a/Marketplace/Marketplace.xcodeproj/project.pbxproj +++ b/Marketplace/Marketplace.xcodeproj/project.pbxproj @@ -142,6 +142,7 @@ 5B02EC5028E4234300124D5B /* Sources */, 5B02EC5128E4234300124D5B /* Frameworks */, 5B02EC5228E4234300124D5B /* Resources */, + 5BA6121C2D942DFD00FF5BD2 /* ShellScript */, ); buildRules = ( ); @@ -198,6 +199,26 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 5BA6121C2D942DFD00FF5BD2 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif which swiftformat > /dev/null; then\n swiftformat .\n echo \"Start scanning...\"\nelse \n echo \"WARNING: SwiftLing not installed!\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 5B02EC5028E4234300124D5B /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/Marketplace/Marketplace.xcodeproj/project.xcworkspace/xcuserdata/aleksey.xcuserdatad/UserInterfaceState.xcuserstate b/Marketplace/Marketplace.xcodeproj/project.xcworkspace/xcuserdata/aleksey.xcuserdatad/UserInterfaceState.xcuserstate index 960c9bd..fdfbd6a 100644 Binary files a/Marketplace/Marketplace.xcodeproj/project.xcworkspace/xcuserdata/aleksey.xcuserdatad/UserInterfaceState.xcuserstate and b/Marketplace/Marketplace.xcodeproj/project.xcworkspace/xcuserdata/aleksey.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Marketplace/Marketplace/.DS_Store b/Marketplace/Marketplace/.DS_Store index 785cd88..b37c829 100644 Binary files a/Marketplace/Marketplace/.DS_Store and b/Marketplace/Marketplace/.DS_Store differ diff --git a/Marketplace/Marketplace/AppDelegate.swift b/Marketplace/Marketplace/AppDelegate.swift index ff74630..ce091de 100644 --- a/Marketplace/Marketplace/AppDelegate.swift +++ b/Marketplace/Marketplace/AppDelegate.swift @@ -1,36 +1,30 @@ -// -// AppDelegate.swift -// Marketplace -// -// Created by Алексей Кобяков on 28.09.2022. -// - -import UIKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - // MARK: UISceneSession Lifecycle - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - -} - +// +// AppDelegate.swift +// Marketplace +// +// Created by Алексей Кобяков on 28.09.2022. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_: UIApplication, didDiscardSceneSessions _: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } +} diff --git a/Marketplace/Marketplace/Cart/CartProductCell.swift b/Marketplace/Marketplace/Cart/CartProductCell.swift index a5466da..e235c9f 100644 --- a/Marketplace/Marketplace/Cart/CartProductCell.swift +++ b/Marketplace/Marketplace/Cart/CartProductCell.swift @@ -1,74 +1,74 @@ -// -// CartProductCell.swift -// Marketplace -// -// Created by Алексей Кобяков on 06.10.2022. -// - -import UIKit - -class CartProductCell: UITableViewCell { - - private lazy var image: UIImageView = { - let image = UIImageView(image: UIImage(systemName: "tshirt")) - image.translatesAutoresizingMaskIntoConstraints = false - image.contentMode = .scaleAspectFit - return image - }() - - private lazy var titleLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 20, weight: .medium) - label.textColor = .black - label.textAlignment = .center - label.numberOfLines = 3 - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var priceLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .bold) - label.textColor = .black - label.textAlignment = .right - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - contentView.addSubview(image) - contentView.addSubview(titleLabel) - contentView.addSubview(priceLabel) - - setupConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupConstraints() { - image.widthAnchor.constraint(equalToConstant: contentView.frame.width/4).isActive = true - image.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true - image.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true - image.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true - - priceLabel.widthAnchor.constraint(equalToConstant: contentView.frame.width/4).isActive = true - priceLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true - priceLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true - priceLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true - - titleLabel.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 10).isActive = true - titleLabel.trailingAnchor.constraint(equalTo: priceLabel.leadingAnchor, constant: -10).isActive = true - titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true - titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true - } - - func configure(_ viewModel: ProductData) { - titleLabel.text = viewModel.productTitle - priceLabel.text = (viewModel.productPrice ?? "") + "$" - image.image = UIImage(data: viewModel.productImage ?? Data()) - } -} +// +// CartProductCell.swift +// Marketplace +// +// Created by Алексей Кобяков on 06.10.2022. +// + +import UIKit + +class CartProductCell: UITableViewCell { + private lazy var image: UIImageView = { + let image = UIImageView(image: UIImage(systemName: "tshirt")) + image.translatesAutoresizingMaskIntoConstraints = false + image.contentMode = .scaleAspectFit + return image + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20, weight: .medium) + label.textColor = .black + label.textAlignment = .center + label.numberOfLines = 3 + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var priceLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16, weight: .bold) + label.textColor = .black + label.textAlignment = .right + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(image) + contentView.addSubview(titleLabel) + contentView.addSubview(priceLabel) + + setupConstraints() + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupConstraints() { + image.widthAnchor.constraint(equalToConstant: contentView.frame.width / 4).isActive = true + image.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true + image.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true + image.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true + + priceLabel.widthAnchor.constraint(equalToConstant: contentView.frame.width / 4).isActive = true + priceLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true + priceLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true + priceLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true + + titleLabel.leadingAnchor.constraint(equalTo: image.trailingAnchor, constant: 10).isActive = true + titleLabel.trailingAnchor.constraint(equalTo: priceLabel.leadingAnchor, constant: -10).isActive = true + titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true + titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true + } + + func configure(_ viewModel: ProductData) { + titleLabel.text = viewModel.productTitle + priceLabel.text = (viewModel.productPrice ?? "") + "$" + image.image = UIImage(data: viewModel.productImage ?? Data()) + } +} diff --git a/Marketplace/Marketplace/Cart/CartViewController.swift b/Marketplace/Marketplace/Cart/CartViewController.swift index d7f320a..c486b27 100644 --- a/Marketplace/Marketplace/Cart/CartViewController.swift +++ b/Marketplace/Marketplace/Cart/CartViewController.swift @@ -1,167 +1,169 @@ -// -// CartViewController.swift -// Marketplace -// -// Created by Алексей Кобяков on 29.09.2022. -// - -import UIKit -import CoreData - -class CartViewController: UIViewController { - private let persistentContainer = NSPersistentContainer(name: "Model") - let cellID: String = "CartProductCell" - var productsInCart: [Product] = [] - - private lazy var tableView: UITableView = { - let tableView = UITableView() - tableView.translatesAutoresizingMaskIntoConstraints = false - tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - tableView.separatorColor = .systemGray4 - tableView.rowHeight = view.frame.height/6 - return tableView - }() - - private lazy var fetchedResultController: NSFetchedResultsController = { - let fetchRequest = ProductData.fetchRequest() - let sortDescriptor = NSSortDescriptor(key: "productTitle", ascending: true) - fetchRequest.sortDescriptors = [sortDescriptor] - let fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil) - fetchResultController.delegate = self - return fetchResultController - }() - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - print("viewWillAppear") - self.tableView.reloadData() - } - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .white - - tableView.register(CartProductCell.self, forCellReuseIdentifier: cellID) - tableView.delegate = self - tableView.dataSource = self - view.addSubview(tableView) - setupContstraints() - //print("cart open") - loadContainer() - } - - private func setupContstraints() { - tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true - tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true - tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true - tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true - } - - func updateTableViewElements(product: Product) { - self.productsInCart.append(product) - print(productsInCart.count) - //saveCart() - print("Add to cart item") - let productData = ProductData.init(entity: NSEntityDescription.entity(forEntityName: "ProductData", in: persistentContainer.viewContext)!, insertInto: persistentContainer.viewContext) - productData.productImage = product.productImage.pngData() - productData.productPrice = product.productPrice - productData.productDescription = product.productDescription - productData.productTitle = product.productTitle - try? productData.managedObjectContext?.save() - } - - private func loadContainer() { - persistentContainer.loadPersistentStores { persistentStoreDescription, error in - if let error = error { - print("Unable to load persistent store") - print("\(error)") - } else { - do { - try self.fetchedResultController.performFetch() - } catch { - print(error) - } - } - } - print("FetchedObj \(fetchedResultController.sections?[0].numberOfObjects ?? 99)") - } -} -// MARK: - extenstions -extension CartViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - let label = UILabel() - label.frame = CGRect.init(x: 15, y: 5, width: tableView.frame.width-10, height: tableView.frame.height-10) - label.text = "Cart" - label.font = .systemFont(ofSize: 25, weight: .bold) - label.textColor = .black - return label - } - func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - 45 - } -} - -extension CartViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if let sections = fetchedResultController.sections { - return sections[section].numberOfObjects - } - return 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let productData = fetchedResultController.object(at: indexPath) - let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as? CartProductCell - cell?.configure(productData) - return cell ?? UITableViewCell() - } - - func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - true - } - - func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if(editingStyle == .delete) { - let productData = fetchedResultController.object(at: indexPath) - persistentContainer.viewContext.delete(productData) - try? persistentContainer.viewContext.save() - } - } -} - -extension CartViewController: NSFetchedResultsControllerDelegate { - func controllerWillChangeContent(_ controller: NSFetchedResultsController) { - tableView.beginUpdates() - } - - func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - tableView.endUpdates() - } - - func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { - - switch type { - case .insert: - if let indexPath = newIndexPath { - tableView.insertRows(at: [indexPath], with: .automatic) - } - case .update: - if let indexPath = indexPath { - print(indexPath) - } - case .move: - if let indexPath = indexPath { - tableView.deleteRows(at: [indexPath], with: .automatic) - } - if let newIndexPath = newIndexPath { - tableView.insertRows(at: [newIndexPath], with: .automatic) - } - case .delete: - if let indexPath = indexPath { - tableView.deleteRows(at: [indexPath], with: .automatic) - } - @unknown default: - fatalError() - } - } -} +// +// CartViewController.swift +// Marketplace +// +// Created by Алексей Кобяков on 29.09.2022. +// + +import CoreData +import UIKit + +class CartViewController: UIViewController { + private let persistentContainer = NSPersistentContainer(name: "Model") + let cellID: String = "CartProductCell" + var productsInCart: [Product] = [] + + private lazy var tableView: UITableView = { + let tableView = UITableView() + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + tableView.separatorColor = .systemGray4 + tableView.rowHeight = view.frame.height / 6 + return tableView + }() + + private lazy var fetchedResultController: NSFetchedResultsController = { + let fetchRequest = ProductData.fetchRequest() + let sortDescriptor = NSSortDescriptor(key: "productTitle", ascending: true) + fetchRequest.sortDescriptors = [sortDescriptor] + let fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil) + fetchResultController.delegate = self + return fetchResultController + }() + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + print("viewWillAppear") + tableView.reloadData() + } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + + tableView.register(CartProductCell.self, forCellReuseIdentifier: cellID) + tableView.delegate = self + tableView.dataSource = self + view.addSubview(tableView) + setupContstraints() + // print("cart open") + loadContainer() + } + + private func setupContstraints() { + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + } + + func updateTableViewElements(product: Product) { + productsInCart.append(product) + print(productsInCart.count) + // saveCart() + print("Add to cart item") + let productData = ProductData(entity: NSEntityDescription.entity(forEntityName: "ProductData", in: persistentContainer.viewContext)!, insertInto: persistentContainer.viewContext) + productData.productImage = product.productImage.pngData() + productData.productPrice = product.productPrice + productData.productDescription = product.productDescription + productData.productTitle = product.productTitle + try? productData.managedObjectContext?.save() + } + + private func loadContainer() { + persistentContainer.loadPersistentStores { _, error in + if let error = error { + print("Unable to load persistent store") + print("\(error)") + } else { + do { + try self.fetchedResultController.performFetch() + } catch { + print(error) + } + } + } + print("FetchedObj \(fetchedResultController.sections?[0].numberOfObjects ?? 99)") + } +} + +// MARK: - extenstions + +extension CartViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, viewForHeaderInSection _: Int) -> UIView? { + let label = UILabel() + label.frame = CGRect(x: 15, y: 5, width: tableView.frame.width - 10, height: tableView.frame.height - 10) + label.text = "Cart" + label.font = .systemFont(ofSize: 25, weight: .bold) + label.textColor = .black + return label + } + + func tableView(_: UITableView, heightForHeaderInSection _: Int) -> CGFloat { + 45 + } +} + +extension CartViewController: UITableViewDataSource { + func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + if let sections = fetchedResultController.sections { + return sections[section].numberOfObjects + } + return 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let productData = fetchedResultController.object(at: indexPath) + let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as? CartProductCell + cell?.configure(productData) + return cell ?? UITableViewCell() + } + + func tableView(_: UITableView, canEditRowAt _: IndexPath) -> Bool { + true + } + + func tableView(_: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + let productData = fetchedResultController.object(at: indexPath) + persistentContainer.viewContext.delete(productData) + try? persistentContainer.viewContext.save() + } + } +} + +extension CartViewController: NSFetchedResultsControllerDelegate { + func controllerWillChangeContent(_: NSFetchedResultsController) { + tableView.beginUpdates() + } + + func controllerDidChangeContent(_: NSFetchedResultsController) { + tableView.endUpdates() + } + + func controller(_: NSFetchedResultsController, didChange _: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + switch type { + case .insert: + if let indexPath = newIndexPath { + tableView.insertRows(at: [indexPath], with: .automatic) + } + case .update: + if let indexPath = indexPath { + print(indexPath) + } + case .move: + if let indexPath = indexPath { + tableView.deleteRows(at: [indexPath], with: .automatic) + } + if let newIndexPath = newIndexPath { + tableView.insertRows(at: [newIndexPath], with: .automatic) + } + case .delete: + if let indexPath = indexPath { + tableView.deleteRows(at: [indexPath], with: .automatic) + } + @unknown default: + fatalError() + } + } +} diff --git a/Marketplace/Marketplace/CategoriesScreen/CategoryViewController.swift b/Marketplace/Marketplace/CategoriesScreen/CategoryViewController.swift index c7030e4..96a510a 100644 --- a/Marketplace/Marketplace/CategoriesScreen/CategoryViewController.swift +++ b/Marketplace/Marketplace/CategoriesScreen/CategoryViewController.swift @@ -1,91 +1,100 @@ -// -// ViewController.swift -// Marketplace -// -// Created by Алексей Кобяков on 28.09.2022. -// - -import UIKit - -class CategoryViewController: UIViewController { - - let categories: [CategoryCell] = [ - CategoryCell(iconCategory: "👕", - description: "Men's clothes", - jsonRequest: "men's%20clothing"), - CategoryCell(iconCategory: "👚", - description: "Women's clothes", - jsonRequest: "women's%20clothing" - ), - CategoryCell(iconCategory: "🖥", - description: "Electronics", - jsonRequest: "electronics"), - CategoryCell(iconCategory: "💎", - description: "Jeweliry", - jsonRequest: "jewelery") - ] - - private lazy var collectionView: UICollectionView = { - let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .vertical - layout.minimumLineSpacing = 15 - layout.minimumInteritemSpacing = 5 - layout.itemSize = CGSize(width: (view.frame.width/2) + 100 , - height: view.frame.height/6) - - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.backgroundColor = .white - return collectionView - }() - - override func viewDidLoad() { - super.viewDidLoad() - title = "Categories" - view.backgroundColor = .white - - collectionView.register(CategoriesCell.self, forCellWithReuseIdentifier: "CategoryCell") - collectionView.delegate = self - collectionView.dataSource = self - - view.addSubview(collectionView) - setupView() - } - - private func setupView() { - collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 90).isActive = true - collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true - collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true - collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -110).isActive = true - } -} -// MARK: - Extensions -extension CategoryViewController: UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let viewModel = categories[indexPath.row] - print("Selected: \(viewModel.description)") - - let categoryProductViewController = CategoryProductViewController() - categoryProductViewController.categoryTitle = viewModel.description - categoryProductViewController.productDownload( - urlString: "https://fakestoreapi.com/products/category/\(viewModel.jsonRequest)") - navigationController?.pushViewController(categoryProductViewController, - animated: true) - } -} - -extension CategoryViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for: indexPath) as! CategoriesCell - let viewModel = categories[indexPath.row] - cell.configure(viewModel) - return cell - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - categories.count - } - -} - +// +// CategoryViewController.swift +// Marketplace +// +// Created by Алексей Кобяков on 28.09.2022. +// + +import UIKit + +class CategoryViewController: UIViewController { + let categories: [CategoryCell] = [ + CategoryCell( + iconCategory: "👕", + description: "Men's clothes", + jsonRequest: "men's%20clothing" + ), + CategoryCell( + iconCategory: "👚", + description: "Women's clothes", + jsonRequest: "women's%20clothing" + ), + CategoryCell( + iconCategory: "🖥", + description: "Electronics", + jsonRequest: "electronics" + ), + CategoryCell( + iconCategory: "💎", + description: "Jeweliry", + jsonRequest: "jewelery" + ) + ] + + private lazy var collectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.minimumLineSpacing = 15 + layout.minimumInteritemSpacing = 5 + layout.itemSize = CGSize( + width: (view.frame.width / 2) + 100, + height: view.frame.height / 6 + ) + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.backgroundColor = .white + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + title = "Categories" + view.backgroundColor = .white + + collectionView.register(CategoriesCell.self, forCellWithReuseIdentifier: "CategoryCell") + collectionView.delegate = self + collectionView.dataSource = self + + view.addSubview(collectionView) + setupView() + } + + private func setupView() { + collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 90).isActive = true + collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true + collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true + collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -110).isActive = true + } +} + +// MARK: - Extensions + +extension CategoryViewController: UICollectionViewDelegate { + func collectionView(_: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let viewModel = categories[indexPath.row] + print("Selected: \(viewModel.description)") + + let categoryProductViewController = CategoryProductViewController() + categoryProductViewController.categoryTitle = viewModel.description + categoryProductViewController.productDownload( + urlString: "https://fakestoreapi.com/products/category/\(viewModel.jsonRequest)") + navigationController?.pushViewController( + categoryProductViewController, + animated: true + ) + } +} + +extension CategoryViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for: indexPath) as! CategoriesCell + let viewModel = categories[indexPath.row] + cell.configure(viewModel) + return cell + } + + func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int { + categories.count + } +} diff --git a/Marketplace/Marketplace/CategoriesScreen/CatgoriesCell.swift b/Marketplace/Marketplace/CategoriesScreen/CatgoriesCell.swift index c2295bb..a613185 100644 --- a/Marketplace/Marketplace/CategoriesScreen/CatgoriesCell.swift +++ b/Marketplace/Marketplace/CategoriesScreen/CatgoriesCell.swift @@ -1,69 +1,71 @@ -// -// CatgoriesCell.swift -// Marketplace -// -// Created by Алексей Кобяков on 29.09.2022. -// - -import UIKit - -struct CategoryCell { - var iconCategory: String - var description: String - var jsonRequest: String -} - -class CategoriesCell: UICollectionViewCell { - - private lazy var iconCategoryLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 50) - label.textColor = .black - label.clipsToBounds = true - label.textAlignment = .center - return label - }() - - private lazy var descriptionCategoryLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 20, weight: .semibold) - label.textColor = .black - label.textAlignment = .center - //translatesAutoresizingMaskIntoConstraints = false - return label - }() - - override init(frame: CGRect) { - super.init(frame: frame) - - contentView.addSubview(iconCategoryLabel) - contentView.addSubview(descriptionCategoryLabel) - backgroundColor = .systemGray6 - - setupView() - - } - - private func setupView() { - iconCategoryLabel.frame = CGRect(x: 0, - y: contentView.frame.size.height - 105, - width: contentView.frame.size.width, - height: 50) - descriptionCategoryLabel.frame = CGRect(x: 0, - y: contentView.frame.size.height - 60, - width: contentView.frame.size.width , - height: 50) - layer.cornerRadius = 20 - } - - func configure(_ viewModel: CategoryCell) { - iconCategoryLabel.text = viewModel.iconCategory - descriptionCategoryLabel.text = viewModel.description - //print(viewModel.iconCategory) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not be implemented") - } -} - +// +// CatgoriesCell.swift +// Marketplace +// +// Created by Алексей Кобяков on 29.09.2022. +// + +import UIKit + +struct CategoryCell { + var iconCategory: String + var description: String + var jsonRequest: String +} + +class CategoriesCell: UICollectionViewCell { + private lazy var iconCategoryLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 50) + label.textColor = .black + label.clipsToBounds = true + label.textAlignment = .center + return label + }() + + private lazy var descriptionCategoryLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + label.textColor = .black + label.textAlignment = .center + // translatesAutoresizingMaskIntoConstraints = false + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(iconCategoryLabel) + contentView.addSubview(descriptionCategoryLabel) + backgroundColor = .systemGray6 + + setupView() + } + + private func setupView() { + iconCategoryLabel.frame = CGRect( + x: 0, + y: contentView.frame.size.height - 105, + width: contentView.frame.size.width, + height: 50 + ) + descriptionCategoryLabel.frame = CGRect( + x: 0, + y: contentView.frame.size.height - 60, + width: contentView.frame.size.width, + height: 50 + ) + layer.cornerRadius = 20 + } + + func configure(_ viewModel: CategoryCell) { + iconCategoryLabel.text = viewModel.iconCategory + descriptionCategoryLabel.text = viewModel.description + // print(viewModel.iconCategory) + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not be implemented") + } +} diff --git a/Marketplace/Marketplace/Keys.swift b/Marketplace/Marketplace/Keys.swift index 915caa9..9987f0b 100644 --- a/Marketplace/Marketplace/Keys.swift +++ b/Marketplace/Marketplace/Keys.swift @@ -1,10 +1,10 @@ -// -// Keys.swift -// Marketplace -// -// Created by Алексей Кобяков on 22.10.2022. -// - -enum Keys { - static let productSaveKey = "productSaveKey" -} +// +// Keys.swift +// Marketplace +// +// Created by Алексей Кобяков on 22.10.2022. +// + +enum Keys { + static let productSaveKey = "productSaveKey" +} diff --git a/Marketplace/Marketplace/ProductScreen/CategoryProductViewController.swift b/Marketplace/Marketplace/ProductScreen/CategoryProductViewController.swift index 7c6b22b..2c2de13 100644 --- a/Marketplace/Marketplace/ProductScreen/CategoryProductViewController.swift +++ b/Marketplace/Marketplace/ProductScreen/CategoryProductViewController.swift @@ -1,180 +1,191 @@ -// -// ProductViewController.swift -// Marketplace -// -// Created by Алексей Кобяков on 04.10.2022. -// - -import Foundation -import UIKit - -class CategoryProductViewController: UIViewController { - let cellID: String = "CategoryProduct" - var categoryTitle: String? - var products: [Product] = [] - - private lazy var tableView: UITableView = { - let tableView = UITableView() - tableView.translatesAutoresizingMaskIntoConstraints = false - tableView.allowsSelection = true - tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - tableView.separatorColor = .systemGray4 - tableView.rowHeight = view.frame.height/2 - - return tableView - }() - - private lazy var activityIndicator: UIActivityIndicatorView = { - let indicator = UIActivityIndicatorView(frame: CGRect(x: view.frame.width/2 - 75, y: 40, width: 150, height: 150)) - return indicator - }() -// MARK: - ViewDidLoad - override func viewDidLoad() { - super.viewDidLoad() - - title = categoryTitle ?? "No category" - view.backgroundColor = .white - - tableView.register(ProductsCell.self, forCellReuseIdentifier: cellID) - tableView.delegate = self - tableView.dataSource = self - view.addSubview(tableView) - setupContstraints() - } - - private func setupContstraints() { - tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true - tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true - tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true - tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true - } - - func productDownload(urlString: String) { - activityIndicator.startAnimating() - let url = URL(string: urlString) - var request = URLRequest(url: url!) - request.httpMethod = "GET" - request.allHTTPHeaderFields = ["accept": "application/json"] - - let task = URLSession.shared.dataTask( - with: request) { - data, response, error in - DispatchQueue.global().async { - [weak self] in - guard let self = self else { return } - print("async") - if let error = error { - print(error) - } else { - print("try") - if let data = data, - let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]]{ - print("Yes!") - print("Succes") - var index: Int = 0 - for product in json { - self.products.append(Product(productTitle: "", - productPrice: "", - productDescription: "", - productURLImage: "", - productImage: UIImage() - )) - if let title = product["title"] as? String { - self.products[index].productTitle = title - } - if let description = product["description"] as? String { - self.products[index].productDescription = description - } - if let price = product["price"] as? Double { - self.products[index].productPrice = String(price) - } - if let imageURL = product["image"] as? String { - self.products[index].productURLImage = imageURL - } - index += 1 - } - DispatchQueue.main.async { - self.tableView.reloadData() - self.loadImages() - } - } - } - } - - } - task.resume() - } - - private func loadImages() { - let dispatchGroup = DispatchGroup() - view.addSubview(activityIndicator) - for index in 0...(products.count - 1) { - dispatchGroup.enter() - - asyncLoadImage(imageStringURL: products[index].productURLImage, - runQueue: DispatchQueue.global(), - completionQueue: DispatchQueue.main) { result, error in - guard let image = result else {return} - self.products[index].productImage = image - dispatchGroup.leave() - } - } - - dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in - guard let self = self else {return} - print("notify") - self.activityIndicator.stopAnimating() - self.activityIndicator.removeFromSuperview() - self.tableView.reloadData() - } - } - private func asyncLoadImage( - imageStringURL: String, - runQueue: DispatchQueue, - completionQueue: DispatchQueue, - completion: @escaping (UIImage?, Error?) -> () - ){ - runQueue.async { - do { - let image = self.downloadImage(urlString: imageStringURL) - completionQueue.async { completion(image, nil) } - } - } - } - private func downloadImage(urlString: String) -> UIImage? { - guard - let url = URL(string: urlString), - let data = try? Data(contentsOf: url) - else { - print("Ошибка, не удалось загрузить изображение") - return nil - } - - return UIImage(data: data) - } -} -// MARK: - TableViewDelegate -extension CategoryProductViewController: UITableViewDelegate { - -} -// MARK: - TableViewDataSource -extension CategoryProductViewController: UITableViewDataSource { - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as? ProductsCell - let viewModel = products[indexPath.row] - cell?.configure(viewModel) - return cell ?? UITableViewCell() - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - products.count - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let productVC = ProductViewController() - productVC.product = products[indexPath.row] - navigationController?.pushViewController(productVC, - animated: true) - } -} +// +// CategoryProductViewController.swift +// Marketplace +// +// Created by Алексей Кобяков on 04.10.2022. +// + +import Foundation +import UIKit + +class CategoryProductViewController: UIViewController { + let cellID: String = "CategoryProduct" + var categoryTitle: String? + var products: [Product] = [] + + private lazy var tableView: UITableView = { + let tableView = UITableView() + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.allowsSelection = true + tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + tableView.separatorColor = .systemGray4 + tableView.rowHeight = view.frame.height / 2 + + return tableView + }() + + private lazy var activityIndicator: UIActivityIndicatorView = { + let indicator = UIActivityIndicatorView(frame: CGRect(x: view.frame.width / 2 - 75, y: 40, width: 150, height: 150)) + return indicator + }() + + // MARK: - ViewDidLoad + + override func viewDidLoad() { + super.viewDidLoad() + + title = categoryTitle ?? "No category" + view.backgroundColor = .white + + tableView.register(ProductsCell.self, forCellReuseIdentifier: cellID) + tableView.delegate = self + tableView.dataSource = self + view.addSubview(tableView) + setupContstraints() + } + + private func setupContstraints() { + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + } + + func productDownload(urlString: String) { + activityIndicator.startAnimating() + let url = URL(string: urlString) + var request = URLRequest(url: url!) + request.httpMethod = "GET" + request.allHTTPHeaderFields = ["accept": "application/json"] + + let task = URLSession.shared.dataTask( + with: request) + { + data, _, error in + DispatchQueue.global().async { + [weak self] in + guard let self = self else { return } + print("async") + if let error = error { + print(error) + } else { + print("try") + if let data = data, + let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] + { + print("Yes!") + print("Succes") + var index = 0 + for product in json { + self.products.append(Product( + productTitle: "", + productPrice: "", + productDescription: "", + productURLImage: "", + productImage: UIImage() + )) + if let title = product["title"] as? String { + self.products[index].productTitle = title + } + if let description = product["description"] as? String { + self.products[index].productDescription = description + } + if let price = product["price"] as? Double { + self.products[index].productPrice = String(price) + } + if let imageURL = product["image"] as? String { + self.products[index].productURLImage = imageURL + } + index += 1 + } + DispatchQueue.main.async { + self.tableView.reloadData() + self.loadImages() + } + } + } + } + } + task.resume() + } + + private func loadImages() { + let dispatchGroup = DispatchGroup() + view.addSubview(activityIndicator) + for index in 0 ... (products.count - 1) { + dispatchGroup.enter() + + asyncLoadImage( + imageStringURL: products[index].productURLImage, + runQueue: DispatchQueue.global(), + completionQueue: DispatchQueue.main + ) { result, _ in + guard let image = result else { return } + self.products[index].productImage = image + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in + guard let self = self else { return } + print("notify") + self.activityIndicator.stopAnimating() + self.activityIndicator.removeFromSuperview() + self.tableView.reloadData() + } + } + + private func asyncLoadImage( + imageStringURL: String, + runQueue: DispatchQueue, + completionQueue: DispatchQueue, + completion: @escaping (UIImage?, Error?) -> Void + ) { + runQueue.async { + do { + let image = self.downloadImage(urlString: imageStringURL) + completionQueue.async { completion(image, nil) } + } + } + } + + private func downloadImage(urlString: String) -> UIImage? { + guard + let url = URL(string: urlString), + let data = try? Data(contentsOf: url) + else { + print("Ошибка, не удалось загрузить изображение") + return nil + } + + return UIImage(data: data) + } +} + +// MARK: - TableViewDelegate + +extension CategoryProductViewController: UITableViewDelegate {} + +// MARK: - TableViewDataSource + +extension CategoryProductViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as? ProductsCell + let viewModel = products[indexPath.row] + cell?.configure(viewModel) + return cell ?? UITableViewCell() + } + + func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { + products.count + } + + func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { + let productVC = ProductViewController() + productVC.product = products[indexPath.row] + navigationController?.pushViewController( + productVC, + animated: true + ) + } +} diff --git a/Marketplace/Marketplace/ProductScreen/ProductViewController.swift b/Marketplace/Marketplace/ProductScreen/ProductViewController.swift index a9cfd79..108d2d2 100644 --- a/Marketplace/Marketplace/ProductScreen/ProductViewController.swift +++ b/Marketplace/Marketplace/ProductScreen/ProductViewController.swift @@ -1,142 +1,145 @@ -// -// ProductViewController.swift -// Marketplace -// -// Created by Алексей Кобяков on 06.10.2022. -// - -import UIKit - -class ProductViewController: UIViewController { - var product: Product? - // let cartVC = CartViewController() - - private lazy var descriptionLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .medium) - label.textColor = .black - label.textAlignment = .justified - label.numberOfLines = 0 - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var priceLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 20, weight: .bold) - label.textColor = .black - label.textAlignment = .center - label.numberOfLines = 1 - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var image: UIImageView = { - let image = UIImageView(image: UIImage(systemName: "tshirt")) - image.translatesAutoresizingMaskIntoConstraints = false - image.contentMode = .scaleAspectFit - return image - }() - - private lazy var scrollView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.translatesAutoresizingMaskIntoConstraints = false - scrollView.alwaysBounceVertical = true - scrollView.delegate = self - scrollView.showsHorizontalScrollIndicator = false - return scrollView - }() - - private lazy var button: UIButton = { - let button = UIButton() - button.setTitle("Add to Cart", for: .normal) - button.setTitleColor(.black, for: .normal) - button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold) - button.backgroundColor = .systemCyan - button.addTarget(self, action: #selector(addToCart), for: .touchUpInside) - button.translatesAutoresizingMaskIntoConstraints = false - button.layer.cornerRadius = 12 - return button - }() - - override func viewDidLoad() { - super.viewDidLoad() - - title = product?.productTitle ?? "No" - view.backgroundColor = .white - descriptionLabel.text = product?.productDescription - priceLabel.text = (product?.productPrice ?? "") + "$" - image.image = product?.productImage - - view.addSubview(image) - view.addSubview(scrollView) - view.addSubview(priceLabel) - scrollView.addSubview(descriptionLabel) - view.addSubview(button) - setupConstraints() - } - - @objc private func addToCart() { - //print("Add to cart item") - let vc = tabBarController?.viewControllers?[1] as? CartViewController - vc?.updateTableViewElements(product: product!) - showAddCartAlert() - } - - private func showAddCartAlert() { - let alert = UIAlertController( - title: "Product add!", - message: "Item successfully added to cart!", - preferredStyle: .alert) - - alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) - - self.present(alert, animated: true, completion: nil) - } - - private func setupConstraints() { - // image constraints - image.heightAnchor.constraint(equalToConstant: 300).isActive = true - image.topAnchor.constraint(equalTo: view.topAnchor, constant: 115).isActive = true - //image.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -(view.frame.height/3)).isActive = true - image.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true - image.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true - - // price constraints - priceLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true - priceLabel.topAnchor.constraint(equalTo: image.bottomAnchor, constant: 15).isActive = true - //priceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true - priceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true - priceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true - - // button constraints - button.heightAnchor.constraint(equalToConstant: 50).isActive = true - //button.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 5).isActive = true - button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -95).isActive = true - button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true - button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true - - // description constraints -// descriptionLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: -5).isActive = true -// descriptionLabel.bottomAnchor.constraint(equalTo: button.topAnchor, constant: -5).isActive = true -// descriptionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true -// descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true - scrollView.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: -5).isActive = true - scrollView.bottomAnchor.constraint(equalTo: button.topAnchor, constant: -5).isActive = true - scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true - scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true - descriptionLabel.widthAnchor.constraint(equalToConstant: view.frame.width - 40).isActive = true - descriptionLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 5).isActive = true - descriptionLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -5).isActive = true - descriptionLabel.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: -1).isActive = true - descriptionLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 1).isActive = true - } -} -// MARK: - extensions -extension ProductViewController: UIScrollViewDelegate { - func scrollViewDidScroll(_ scrollView: UIScrollView) { - if scrollView.contentOffset.x != 0 { - scrollView.contentOffset.x = 0 - } - } -} +// +// ProductViewController.swift +// Marketplace +// +// Created by Алексей Кобяков on 06.10.2022. +// + +import UIKit + +class ProductViewController: UIViewController { + var product: Product? + // let cartVC = CartViewController() + + private lazy var descriptionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16, weight: .medium) + label.textColor = .black + label.textAlignment = .justified + label.numberOfLines = 0 + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var priceLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20, weight: .bold) + label.textColor = .black + label.textAlignment = .center + label.numberOfLines = 1 + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var image: UIImageView = { + let image = UIImageView(image: UIImage(systemName: "tshirt")) + image.translatesAutoresizingMaskIntoConstraints = false + image.contentMode = .scaleAspectFit + return image + }() + + private lazy var scrollView: UIScrollView = { + let scrollView = UIScrollView() + scrollView.translatesAutoresizingMaskIntoConstraints = false + scrollView.alwaysBounceVertical = true + scrollView.delegate = self + scrollView.showsHorizontalScrollIndicator = false + return scrollView + }() + + private lazy var button: UIButton = { + let button = UIButton() + button.setTitle("Add to Cart", for: .normal) + button.setTitleColor(.black, for: .normal) + button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold) + button.backgroundColor = .systemCyan + button.addTarget(self, action: #selector(addToCart), for: .touchUpInside) + button.translatesAutoresizingMaskIntoConstraints = false + button.layer.cornerRadius = 12 + return button + }() + + override func viewDidLoad() { + super.viewDidLoad() + + title = product?.productTitle ?? "No" + view.backgroundColor = .white + descriptionLabel.text = product?.productDescription + priceLabel.text = (product?.productPrice ?? "") + "$" + image.image = product?.productImage + + view.addSubview(image) + view.addSubview(scrollView) + view.addSubview(priceLabel) + scrollView.addSubview(descriptionLabel) + view.addSubview(button) + setupConstraints() + } + + @objc private func addToCart() { + // print("Add to cart item") + let vc = tabBarController?.viewControllers?[1] as? CartViewController + vc?.updateTableViewElements(product: product!) + showAddCartAlert() + } + + private func showAddCartAlert() { + let alert = UIAlertController( + title: "Product add!", + message: "Item successfully added to cart!", + preferredStyle: .alert + ) + + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + + present(alert, animated: true, completion: nil) + } + + private func setupConstraints() { + // image constraints + image.heightAnchor.constraint(equalToConstant: 300).isActive = true + image.topAnchor.constraint(equalTo: view.topAnchor, constant: 115).isActive = true + // image.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -(view.frame.height/3)).isActive = true + image.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true + image.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true + + // price constraints + priceLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true + priceLabel.topAnchor.constraint(equalTo: image.bottomAnchor, constant: 15).isActive = true + // priceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true + priceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true + priceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true + + // button constraints + button.heightAnchor.constraint(equalToConstant: 50).isActive = true + // button.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 5).isActive = true + button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -95).isActive = true + button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true + button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true + + // description constraints +// descriptionLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: -5).isActive = true +// descriptionLabel.bottomAnchor.constraint(equalTo: button.topAnchor, constant: -5).isActive = true +// descriptionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true +// descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true + scrollView.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: -5).isActive = true + scrollView.bottomAnchor.constraint(equalTo: button.topAnchor, constant: -5).isActive = true + scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true + scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true + descriptionLabel.widthAnchor.constraint(equalToConstant: view.frame.width - 40).isActive = true + descriptionLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 5).isActive = true + descriptionLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -5).isActive = true + descriptionLabel.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: -1).isActive = true + descriptionLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 1).isActive = true + } +} + +// MARK: - extensions + +extension ProductViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if scrollView.contentOffset.x != 0 { + scrollView.contentOffset.x = 0 + } + } +} diff --git a/Marketplace/Marketplace/ProductScreen/ProductsCell.swift b/Marketplace/Marketplace/ProductScreen/ProductsCell.swift index c925a57..492aa1f 100644 --- a/Marketplace/Marketplace/ProductScreen/ProductsCell.swift +++ b/Marketplace/Marketplace/ProductScreen/ProductsCell.swift @@ -1,80 +1,79 @@ -// -// ProductsCell.swift -// Marketplace -// -// Created by Алексей Кобяков on 04.10.2022. -// - -import UIKit - -struct Product { - var productTitle: String - var productPrice: String - var productDescription: String - var productURLImage: String - var productImage: UIImage -} - -class ProductsCell: UITableViewCell { - - private lazy var productTitleLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .medium) - label.textColor = .black - label.numberOfLines = 2 - label.textAlignment = .center - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var productPriceLabel: UILabel = { - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 20, weight: .bold) - label.textColor = .black - label.textAlignment = .center - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var productImage: UIImageView = { - let image = UIImageView(image: UIImage(systemName: "tshirt")) - image.contentMode = .scaleAspectFit - return image - }() - - private lazy var stackView: UIStackView = { - let stackView = UIStackView() - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.axis = .vertical - stackView.spacing = 1 - stackView.contentMode = .scaleToFill - stackView.distribution = .fillEqually - stackView.alignment = .fill - stackView.addArrangedSubview(productTitleLabel) - stackView.addArrangedSubview(productImage) - stackView.addArrangedSubview(productPriceLabel) - return stackView - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - contentView.addSubview(stackView) - stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true - stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true - stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10).isActive = true - stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10).isActive = true - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(_ viewModel: Product) { - print(viewModel.productTitle) - productTitleLabel.text = viewModel.productTitle - productPriceLabel.text = viewModel.productPrice + "$" - productImage.image = viewModel.productImage - } - -} +// +// ProductsCell.swift +// Marketplace +// +// Created by Алексей Кобяков on 04.10.2022. +// + +import UIKit + +struct Product { + var productTitle: String + var productPrice: String + var productDescription: String + var productURLImage: String + var productImage: UIImage +} + +class ProductsCell: UITableViewCell { + private lazy var productTitleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16, weight: .medium) + label.textColor = .black + label.numberOfLines = 2 + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var productPriceLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20, weight: .bold) + label.textColor = .black + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var productImage: UIImageView = { + let image = UIImageView(image: UIImage(systemName: "tshirt")) + image.contentMode = .scaleAspectFit + return image + }() + + private lazy var stackView: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + stackView.spacing = 1 + stackView.contentMode = .scaleToFill + stackView.distribution = .fillEqually + stackView.alignment = .fill + stackView.addArrangedSubview(productTitleLabel) + stackView.addArrangedSubview(productImage) + stackView.addArrangedSubview(productPriceLabel) + return stackView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(stackView) + stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true + stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true + stackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10).isActive = true + stackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10).isActive = true + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(_ viewModel: Product) { + print(viewModel.productTitle) + productTitleLabel.text = viewModel.productTitle + productPriceLabel.text = viewModel.productPrice + "$" + productImage.image = viewModel.productImage + } +} diff --git a/Marketplace/Marketplace/SceneDelegate.swift b/Marketplace/Marketplace/SceneDelegate.swift index 10857f5..0003923 100644 --- a/Marketplace/Marketplace/SceneDelegate.swift +++ b/Marketplace/Marketplace/SceneDelegate.swift @@ -1,73 +1,71 @@ -// -// SceneDelegate.swift -// Marketplace -// -// Created by Алексей Кобяков on 28.09.2022. -// - -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let windowScene = (scene as? UIWindowScene) else { return } - let window = UIWindow(windowScene: windowScene) - - let categoryVC = CategoryViewController() - let cartVC = CartViewController() - cartVC.viewDidLoad() - let navigationController = UINavigationController(rootViewController: categoryVC) - - categoryVC.tabBarItem = UITabBarItem(title: "Categories", - image: UIImage(systemName: "magnifyingglass.circle"), - selectedImage: UIImage(systemName: "magnifyingglass.circle.fill")) - cartVC.tabBarItem = UITabBarItem(title: "Cart", - image: UIImage(systemName: "cart"), - selectedImage: UIImage(systemName: "cart.fill")) - - let tabBarController = UITabBarController() - tabBarController.setViewControllers([navigationController, cartVC], animated: true) - - - window.rootViewController = tabBarController - self.window = window - window.makeKeyAndVisible() - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } - - -} - +// +// SceneDelegate.swift +// Marketplace +// +// Created by Алексей Кобяков on 28.09.2022. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo _: UISceneSession, options _: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + let window = UIWindow(windowScene: windowScene) + + let categoryVC = CategoryViewController() + let cartVC = CartViewController() + cartVC.viewDidLoad() + let navigationController = UINavigationController(rootViewController: categoryVC) + + categoryVC.tabBarItem = UITabBarItem( + title: "Categories", + image: UIImage(systemName: "magnifyingglass.circle"), + selectedImage: UIImage(systemName: "magnifyingglass.circle.fill") + ) + cartVC.tabBarItem = UITabBarItem( + title: "Cart", + image: UIImage(systemName: "cart"), + selectedImage: UIImage(systemName: "cart.fill") + ) + + let tabBarController = UITabBarController() + tabBarController.setViewControllers([navigationController, cartVC], animated: true) + + window.rootViewController = tabBarController + self.window = window + window.makeKeyAndVisible() + } + + func sceneDidDisconnect(_: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } +} diff --git a/README.md b/README.md index f58a9a0..6acacc5 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,7 @@ CategoriesCell - отвечат за ячейку CollectionView CategoryViewCon - UICollectionView 3. Для сохранения корзины использовал CoreData. +``` +Brew install swiftformat +``` +