#ios #swift #uitableview
#iOS #swift #uitableview
Вопрос:
Я знаю, что об этом спрашивали раньше, но не мог понять, как добиться этого как можно быстрее в моем случае.
Когда я нажимаю кнопку «Добавить» (см. GIF ниже), функция анимации анимирует «Просмотр изображения» (в данном случае изображение галактики), чтобы перейти в корзину, т.Е. «notificationButton». Также кнопка «Добавить» меняется на «Удалить», а цвет кнопки меняется с черного на красный (см. GIF ниже). С этим все в порядке.
Теперь, когда я нажимаю кнопку во второй раз, т.Е. отменяю ее выбор, т.Е. Переводю ее в состояние по умолчанию, все меняется на противоположное, но изображение все равно отправляется в корзину!
Теперь я хочу изменить анимацию анимации полета ImageView в исходное положение, когда я нажимаю кнопку обратно в положение по умолчанию во второй раз, и снова оригинальную анимацию полета, если кнопка нажимается столько раз, сколько я хочу.
Хотя я добавил сюда полный код ProductViewController, но вы все пропускаете и смотрите на последнее расширение ProductViewController
Я знаю, что это, скорее всего, состоит из двух шагов —
i) Определение того, что кнопка «buttonHandlerAddToCart» нажата во второй раз, т.е. с выбранного / выбранного шага на шаг по умолчанию.
ii) Изменение функции анимации «func animation» в ProductViewController.
Как это сделать?
Соответствующий код:
SSBadgeButton:-
импорт UIKit
class SSBadgeButton: UIButton {
var badgeLabel = UILabel()
var badge: String? {
didSet {
addBadgeToButon(badge: badge)
}
}
public var badgeBackgroundColor = UIColor.red {
didSet {
badgeLabel.backgroundColor = badgeBackgroundColor
}
}
public var badgeTextColor = UIColor.white {
didSet {
badgeLabel.textColor = badgeTextColor
}
}
public var badgeFont = UIFont.systemFont(ofSize: 12.0) {
didSet {
badgeLabel.font = badgeFont
}
}
public var badgeEdgeInsets: UIEdgeInsets? {
didSet {
addBadgeToButon(badge: badge)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addBadgeToButon(badge: nil)
}
func addBadgeToButon(badge: String?) {
badgeLabel.text = badge
badgeLabel.textColor = badgeTextColor
badgeLabel.backgroundColor = badgeBackgroundColor
badgeLabel.font = badgeFont
badgeLabel.sizeToFit()
badgeLabel.textAlignment = .center
let badgeSize = badgeLabel.frame.size
let height = max(18, Double(badgeSize.height) 5.0)
let width = max(height, Double(badgeSize.width) 10.0)
var vertical: Double?, horizontal: Double?
if let badgeInset = self.badgeEdgeInsets {
vertical = Double(badgeInset.top) - Double(badgeInset.bottom)
horizontal = Double(badgeInset.left) - Double(badgeInset.right)
let x = (Double(bounds.size.width) - 10 horizontal!)
let y = -(Double(badgeSize.height) / 2) - 10 vertical!
badgeLabel.frame = CGRect(x: x, y: y, width: width, height: height)
} else {
let x = self.frame.width - CGFloat((width / 2.0))
let y = CGFloat(-(height / 2.0))
badgeLabel.frame = CGRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height))
}
badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2
badgeLabel.layer.masksToBounds = true
addSubview(badgeLabel)
badgeLabel.isHidden = badge != nil ? false : true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addBadgeToButon(badge: nil)
fatalError("init(coder:) has not been implemented")
}
}
Код ProductViewController
:
import UIKit
class ProductViewController: UIViewController, UITableViewDataSource,
UITableViewDelegate {
let notificationButton = SSBadgeButton()
let rightbarbuttonimage = UIImage(named:"ic_cart")
fileprivate var cart = Cart()
let scrollView = UIScrollView()
let sections = ["Section A", "Section B","Section C", "Section D","Section E","Section F","Section G","Section H", "Section I","Section J","Section K","Section L"]
let rowspersection = [2,3,1,2,2,3,3,1,4,2,1,2]
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.tableView.backgroundColor = UIColor.gray
//Add and setup scroll view
self.tableView.addSubview(self.scrollView)
self.scrollView.translatesAutoresizingMaskIntoConstraints = false;
//Constrain scroll view
self.scrollView.leadingAnchor.constraint(equalTo: self.tableView.leadingAnchor, constant: 20).isActive = true;
self.scrollView.topAnchor.constraint(equalTo: self.tableView.topAnchor, constant: 20).isActive = true;
self.scrollView.trailingAnchor.constraint(equalTo: self.tableView.trailingAnchor, constant: -20).isActive = true;
self.scrollView.bottomAnchor.constraint(equalTo: self.tableView.bottomAnchor, constant: -20).isActive = true;
// customising rightBarButtonItems as notificationbutton
notificationButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
notificationButton.setImage(UIImage(named: "ic_cart")?.withRenderingMode(.alwaysTemplate), for: .normal)
notificationButton.badgeEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: notificationButton)
//following register is needed because I have rightbarbuttonitem customised as uibutton i.e. notificationbutton
notificationButton.addTarget(self, action: #selector(self.registerTapped(_:)), for: .touchUpInside)
}
@objc func registerTapped(_ sender: UIButton) {
self.performSegue(withIdentifier: "showCart", sender: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Workaround to avoid the fadout the right bar button item
self.navigationItem.rightBarButtonItem?.isEnabled = false
self.navigationItem.rightBarButtonItem?.isEnabled = true
//Update cart if some items quantity is equal to 0 and reload the product table and right button bar item
cart.updateCart()
//self.navigationItem.rightBarButtonItem?.title = "Checkout ((cart.items.count))"
notificationButton.badge = String(cart.items.count)// making badge equal to no.ofitems in cart
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// this segue to transfer data
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showCart" {
if let cartViewController = segue.destination as? CartViewController {
cartViewController.cart = self.cart
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return productMap.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productMap[section]?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let product = productMap[indexPath.section]![indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as! ProductTableViewCell
cell.imageView?.image = product.imagename
cell.delegate = self as CartDelegate
cell.setButton(state: self.cart.contains(product: product))
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch(section) {
case 0: return "Section A"
case 1: return "Section B"
case 2: return "Section C"
case 3: return "Section D"
case 4: return "Section E"
case 5: return "Section F"
case 6: return "Section G"
case 7: return "Section H"
case 8: return "Section I"
case 9: return "Section J"
case 10: return "Section K"
case 11: return "Section L"
default: return ""
}
}
}
extension ProductViewController: CartDelegate {
// MARK: - CartDelegate
func updateCart(cell: ProductTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
let product = productMap[indexPath.section]![indexPath.row]
//Update Cart with product
cart.updateCart(with: product)
// self.navigationItem.rightBarButtonItem?.title = "Checkout ((cart.items.count))"
notificationButton.badge = String(cart.items.count) // making badge equal to noofitems in cart
}
}
***// Most relevant code begins here -***
extension ProductViewController {
@IBAction func buttonHandlerAddToCart(_ sender: UIButton) {
let buttonPosition : CGPoint = sender.convert(sender.bounds.origin, to: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)!
let cell = tableView.cellForRow(at: indexPath) as! ProductTableViewCell
let imageViewPosition : CGPoint = cell.imageView!.convert(cell.imageView!.bounds.origin, to: self.view)
let imgViewTemp = UIImageView(frame: CGRect(x: imageViewPosition.x, y: imageViewPosition.y, width: cell.imageView!.frame.size.width, height: cell.imageView!.frame.size.height))
imgViewTemp.image = cell.imageView!.image
animation(tempView: imgViewTemp)
}
func animation(tempView : UIView) {
self.view.addSubview(tempView)
UIView.animate(
withDuration: 1.0,
animations: {
tempView.animationZoom(scaleX: 1.5, y: 1.5)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
tempView.animationZoom(scaleX: 0.2, y: 0.2)
tempView.animationRoted(angle: CGFloat(Double.pi))
tempView.frame.origin.x = self.notificationButton.frame.origin.x
tempView.frame.origin.y = self.notificationButton.frame.origin.y
}, completion: { _ in
tempView.removeFromSuperview()
UIView.animate(withDuration: 1.0, animations: {
self.notificationButton.animationZoom(scaleX: 1.4, y: 1.4)
}, completion: {_ in
self.notificationButton.animationZoom(scaleX: 1.0, y: 1.0)
})
})
}
)
}
}
extension UIView{
func animationZoom(scaleX: CGFloat, y: CGFloat) {
self.transform = CGAffineTransform(scaleX: scaleX, y: y)
}
func animationRoted(angle : CGFloat) {
self.transform = self.transform.rotated(by: angle)
}
}
I have also included ProductTableViewCell code, just in case:
import UIKit
protocol CartDelegate {
func updateCart(cell: ProductTableViewCell)
}
class ProductTableViewCell: UITableViewCell {
weak var myParent:ProductViewController?
@IBOutlet weak var imagename: UIImageView!
@IBOutlet weak var addToCartButton: UIButton!
var delegate: CartDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
addToCartButton.layer.cornerRadius = 5
addToCartButton.clipsToBounds = true
}
func setButton(state: Bool) {
addToCartButton.isUserInteractionEnabled = true
addToCartButton.isSelected = state
addToCartButton.backgroundColor = (!addToCartButton.isSelected) ? .blue : .red
}
@IBAction func addToCart(_ sender: Any) {
setButton(state: !addToCartButton.isSelected)
self.delegate?.updateCart(cell: self)
}
}
Редактировать: по запросу @aheze :
struct Product: Equatable {
let imagename: UIImage
}
var productMap = [
0: [ Product(imagename:#imageLiteral(resourceName: "blue")), Product( imagename:#imageLiteral(resourceName: "CakeImage")) ]
1: [ Product(imagename:#imageLiteral(resourceName: "vectorlogo")), Product(imagename:#imageLiteral(resourceName: "PeasImge")), Product(imagename:#imageLiteral(resourceName: "castle"))],
2: [ Product( imagename:#imageLiteral(resourceName: "scoobydoo")),Product(imagename:#imageLiteral(resourceName: "ufo"))] ,
3: [ Product( imagename:#imageLiteral(resourceName: "wolfsky")),Product( imagename:#imageLiteral(resourceName: "universe")) ],
4: [ Product(imagename:#imageLiteral(resourceName: "werewolf")),Product( imagename:#imageLiteral(resourceName: "galaxy")) ]
]
Редактировать 2: класс Cart
, по запросу @aheze:
import Foundation
class Cart {
var items : [CartItem] = []
}
extension Cart {
var totalQuantity : Int {
get { return items.reduce(0) { value, item in
value item.quantity
}
}
}
func updateCart(with product: Product) {
if !self.contains(product: product) {
self.add(product: product)
} else {
self.remove(product: product)
}
}
func updateCart() {
for item in self.items {
if item.quantity == 0 {
updateCart(with: item.product)
}
}
}
func add(product: Product) {
let item = items.filter { $0.product == product }
if item.first != nil {
item.first!.quantity = 1
} else {
items.append(CartItem(product: product))
}
}
func remove(product: Product) {
guard let index = items.firstIndex(where: { $0.product == product }) else { return}
items.remove(at: index)
}
func contains(product: Product) -> Bool {
let item = items.filter { $0.product == product }
return item.first != nil
}
}
Дополнительная информация, которая вам нужна, не стесняйтесь…
Комментарии:
1. Как вы можете видеть, я немного в бедной части города, что касается репутации, но все же я поставил награду, потому что у меня мало времени. Пожалуйста, разберитесь в этом вопросе.
2. Можете ли вы показать, где
productMap
определено?3. Извините за поздний ответ. Пожалуйста, смотрите Редактирование.
4. но, насколько я могу понять, это не имеет ничего общего с productMap. Это связано с тегированием кнопки в ProductTableViewCell и / или ProductViewController и обратным завершением-hanslers в ProductViewController.
5. Спасибо. Чтобы отслеживать, какой продукт выбран, вы должны создать новый массив,
selectedProducts
, и добавлять продукты к нему при их выборе. Затем вы можете проверить, какие продукты там есть, и настроить анимацию. Для обратного я рекомендую просто создать новую функцию анимации. Сейчас мне нужно идти, но я постараюсь опубликовать некоторый код завтра
Ответ №1:
Работает ли это? (gif был слишком большим) https://imgur.com/a/jrcwEWv
Я создал отдельные функции для adding
корзины и removing
из нее.
extension ProductViewController: CartDelegate {
// MARK: - CartDelegate
func updateCart(cell: ProductTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
let product = productMap[indexPath.section]![indexPath.row]
/// `var selectedIndexPaths = [IndexPath]()` defined inside `ProductViewController`, to keep track of the selected products
if selectedIndexPaths.contains(indexPath) {
if let index = selectedIndexPaths.firstIndex(of: indexPath) {
selectedIndexPaths.remove(at: index)
removeProductFromCart(indexPath: indexPath)
}
} else {
selectedIndexPaths.append(indexPath)
addProductToCart(indexPath: indexPath)
}
// addProductToCart(indexPath: indexPath)
/// **I commented this out because I don't have the code for `Cart`**
//Update Cart with product
// cart.updateCart(with: product)
// self.navigationItem.rightBarButtonItem?.title = "Checkout ((cart.items.count))"
// notificationButton.badge = String(cart.items.count) // making badge equal to noofitems in cart
}
}
func addProductToCart(indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? ProductTableViewCell {
if let imageView = cell.imagename {
let initialImageViewFrame = imageView.convert(imageView.frame, to: self.view)
let targetImageViewFrame = self.notificationButton.frame
let imgViewTemp = UIImageView(frame: initialImageViewFrame)
imgViewTemp.clipsToBounds = true
imgViewTemp.contentMode = .scaleAspectFill
imgViewTemp.image = imageView.image
self.view.addSubview(imgViewTemp)
UIView.animate(withDuration: 1.0, animations: {
imgViewTemp.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}) { _ in
UIView.animate(withDuration: 0.5, animations: {
imgViewTemp.transform = CGAffineTransform(scaleX: 0.2, y: 0.2).rotated(by: CGFloat(Double.pi))
imgViewTemp.frame = targetImageViewFrame
}) { _ in
imgViewTemp.removeFromSuperview()
UIView.animate(withDuration: 1.0, animations: {
self.notificationButton.transform = CGAffineTransform(scaleX: 1.4, y: 1.4)
}, completion: {_ in
self.notificationButton.transform = CGAffineTransform.identity
})
}
}
}
}
}
func removeProductFromCart(indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? ProductTableViewCell {
if let imageView = cell.imagename {
let initialImageViewFrame = self.notificationButton.frame
let targetImageViewFrame = imageView.convert(imageView.frame, to: self.view)
let imgViewTemp = UIImageView(frame: initialImageViewFrame)
imgViewTemp.clipsToBounds = true
imgViewTemp.contentMode = .scaleAspectFill
imgViewTemp.image = imageView.image
self.view.addSubview(imgViewTemp)
var initialTransform = CGAffineTransform.identity
initialTransform = initialTransform.scaledBy(x: 0.2, y: 0.2)
initialTransform = initialTransform.rotated(by: CGFloat(Double.pi))
UIView.animate(withDuration: 0.5, animations: {
self.notificationButton.animationZoom(scaleX: 1.4, y: 1.4)
imgViewTemp.transform = initialTransform
}) { _ in
UIView.animate(withDuration: 1, animations: {
self.notificationButton.animationZoom(scaleX: 1, y: 1)
imgViewTemp.transform = CGAffineTransform.identity
imgViewTemp.frame = targetImageViewFrame
}) { _ in
imgViewTemp.removeFromSuperview()
}
}
}
}
}
Некоторые вещи, которые вы должны исправить:
- Вместо того, чтобы использовать
imagename
(представление изображения, которое вы добавили в ячейку представления таблицы), вы использовалиcell.imageView!
встроенное представление изображения, которое есть во всех ячейках. Не используйте это. - Внутри
ProductTableViewCell
вы должны создать отдельное свойство для отслеживания выбранного / не выбранного состояния вместо использованияUIButton
‘sisSelected
. Таким образом, вы не столкнетесь с нежелательным поведением при изменении цвета кнопки (в настоящее время за текстом кнопки на мгновение появляется красный прямоугольник) - Если вы комбинируете преобразования, вы должны сделать это:
var initialTransform = CGAffineTransform.identity
initialTransform = initialTransform.scaledBy(x: 0.2, y: 0.2)
initialTransform = initialTransform.rotated(by: CGFloat(Double.pi))
tempView.transform = initialTransform
вместо:
tempView.animationZoom(scaleX: 0.2, y: 0.2)
tempView.animationRoted(angle: CGFloat(Double.pi))
Вот полный проект (добавлены еще несколько комментариев).
Комментарии:
1. да, я запускаю ваш ProductViewController из github после тщательного сопоставления и исправления, у него по-прежнему нет обратной анимации, только прямая анимация. Кроме того, если я скопирую код ProductTableViewcell из вашего в мой, он показывает ошибку sigbart в AppDelegate и эту ошибку в консоли — «Завершение работы приложения из-за неперехваченного исключения ‘NSUnknownKeyException’, причина: ‘[<PracticeCart.ProductTableViewCell 0x7fbe720af000> setValue:forUndefinedKey:]: этот класс не совместим с кодированием значений ключадля имени ключа.’*** Первый стек вызовов throw:»
2. Добавлена «корзина классов», но у меня все еще есть ощущение, что это связано только с переключением кнопки добавления в ProductTableViewCell и изменением анимации.
3. @askit
Terminating app due to uncaught exception
вероятно, это связано с тем, что вам нужно установить модуль ячейки. В раскадровке просто удалите текущий класс и вставьте его снова (он должен быть автозаполнен).4. @askit По-прежнему нет обратной анимации? Убедитесь, что вы
selectedIndexPaths
определили5. Да, сегодня я с вами в Сети. Хорошо, теперь вы имеете в виду » var selectedIndexPaths = [indexPath]()» в ProductViewcontroller? Да, это там. по-прежнему нет обратной анимации. Кроме того, он печатает «Внутри» в консоли каждый раз, когда я нажимаю на кнопку «Добавить». Остальное в порядке, т.Е. Его добавление и удаление из корзины.