问题描述
我正在尝试为UICollectionViewCell创建一个自定义叠加层,当用户选择图像时,它会在灰色叠加层上放置用户选择该图像的数字(即顺序)。运行代码时,我不会得到任何错误,但它似乎也无能为力。我添加了一些打印语句来帮助调试,当我运行代码时,我得到“ Count:0”打印15次。那就是我在库中拥有的图像数量。当我选择第一行中的第一张图像时,仍然会像预期的那样获得“计数:0”,但是当我选择下一张图像时,我得到的打印件如下所示。似乎计数不起作用,但我不确定为什么。我究竟做错了什么?我不知道为什么计数错误,但是我要解决的主要问题/原因是重叠式广告无法正常显示?
打印声明
Cell selected: [0,0]
Count :0
Count :0
Count :0
Cell selected: [0,4]
Count :0
视图控制器
func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
cell.setupView()
print("Cell selected: \(indexPath)")
}
}
func collectionView(_ collectionView: UICollectionView,diddeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
cell.backgroundColor = nil
cell.imageView.alpha = 1
}
}
自定义叠加层
lazy var circleView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.layer.cornerRadius = self.countSize.width / 2
view.alpha = 0.4
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var countLabel: UILabel = {
let label = UILabel()
let font = UIFont.preferredFont(forTextStyle: .headline)
label.font = UIFont.systemFont(ofSize: font.pointSize,weight: UIFont.Weight.bold)
label.textAlignment = .center
label.textColor = .white
label.adjustsFontSizetoFitWidth = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private func setup(){addSubview(circleView)
addSubview(circleView)
addSubview(countLabel)
NSLayoutConstraint.activate([
circleView.leadingAnchor.constraint(equalTo: leadingAnchor),circleView.trailingAnchor.constraint(equalTo: trailingAnchor),circleView.topAnchor.constraint(equalTo: topAnchor),circleView.bottomAnchor.constraint(equalTo: bottomAnchor),countLabel.leadingAnchor.constraint(equalTo: leadingAnchor),countLabel.trailingAnchor.constraint(equalTo: trailingAnchor),countLabel.topAnchor.constraint(equalTo: topAnchor),countLabel.bottomAnchor.constraint(equalTo: bottomAnchor),])
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
TestCVCell:UICollectionViewCell
override var isSelected: Bool {
didSet { overlay.isHidden = !isSelected }
}
var imageView: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.contentMode = .scaleAspectFill
view.backgroundColor = UIColor.gray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var count: Int = 0 {
didSet { overlay.countLabel.text = "\(count)" }
}
let overlay: CustomAssetCelloverlay = {
let view = CustomAssetCelloverlay()
view.isHidden = true
return view
}()
func setupView() {
addSubview(imageView)
addSubview(overlay)
print("Count :\(count)")
NSLayoutConstraint.activate([
overlay.topAnchor.constraint(equalTo: imageView.topAnchor),overlay.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),overlay.leftAnchor.constraint(equalTo: imageView.leftAnchor),overlay.rightAnchor.constraint(equalTo: imageView.rightAnchor),])
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = self.bounds
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
fatalError("init(coder:) has not been implemented")
}
解决方法
根据您的另一个问题,我猜您正在尝试执行类似的操作...
显示设备“照片”中的图像,并允许依次选择:
,然后,当您取消选择一个单元格时(例如,取消选择我的第二个选择项),您想对其余选择项重新编号:
要完成此操作,您需要跟踪数组中的单元格选择-进行选择-以便保持编号。
解决这个问题的几种方法...这是一种。
首先,建议您将count
属性重命名为index
,并在设置值时显示或隐藏overlay
:
var index: Int = 0 {
didSet {
overlay.countLabel.text = "\(index)"
// hide if count is Zero,show if not
overlay.isHidden = index == 0
}
}
当您从cellForItemAt
中退出一个单元格时,请查看indexPath是否在我们的“跟踪”数组中,并适当设置该单元格的.index
属性(该属性还将显示/隐藏叠加层)。
下一步,当您选择一个单元格时:
- 将
indexPath
添加到我们的跟踪数组 - 我们可以使用跟踪数组的数量直接设置
.index
属性,以更新单元格的外观,因为它不会影响其他任何单元格
取消选择单元格时,我们必须做其他工作:
- 从我们的跟踪数组中删除indexPath
- 重新加载单元格,以便重新编号
这是一个完整的示例-代码中包含很多注释。
CircleView
class CircleView: UIView {
// simple view subclass that keeps itself "round"
// (assuming it has a 1:1 ratio)
override func layoutSubviews() {
layer.cornerRadius = bounds.width * 0.5
}
}
CustomAssetCellOverlay
class CustomAssetCellOverlay: UIView {
lazy var circleView: CircleView = {
let view = CircleView()
view.backgroundColor = UIColor(red: 0.0,green: 0.5,blue: 1.0,alpha: 1.0)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var countLabel: UILabel = {
let label = UILabel()
let font = UIFont.preferredFont(forTextStyle: .headline)
label.font = UIFont.systemFont(ofSize: font.pointSize,weight: UIFont.Weight.bold)
label.textAlignment = .center
label.textColor = .white
label.adjustsFontSizeToFitWidth = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private func setup(){addSubview(circleView)
addSubview(circleView)
addSubview(countLabel)
NSLayoutConstraint.activate([
// circle view at top-left
circleView.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 4.0),circleView.topAnchor.constraint(equalTo: topAnchor,// circle view Width: 28 Height: 1:1 ratio
circleView.widthAnchor.constraint(equalToConstant: 28.0),circleView.heightAnchor.constraint(equalTo: circleView.widthAnchor),// count label constrained ot circle view
countLabel.leadingAnchor.constraint(equalTo: circleView.leadingAnchor),countLabel.trailingAnchor.constraint(equalTo: circleView.trailingAnchor),countLabel.topAnchor.constraint(equalTo: circleView.topAnchor),countLabel.bottomAnchor.constraint(equalTo: circleView.bottomAnchor),])
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
}
TestCVCell
class TestCVCell: UICollectionViewCell {
var imageView = UIImageView()
var index: Int = 0 {
didSet {
overlay.countLabel.text = "\(index)"
// hide if count is Zero,show if not
overlay.isHidden = index == 0
}
}
let overlay: CustomAssetCellOverlay = {
let view = CustomAssetCellOverlay()
view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
view.isHidden = true
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
contentView.addSubview(imageView)
contentView.addSubview(overlay)
imageView.translatesAutoresizingMaskIntoConstraints = false
overlay.translatesAutoresizingMaskIntoConstraints = false
// constrain both image view and overlay to full contentView
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),overlay.topAnchor.constraint(equalTo: imageView.topAnchor),overlay.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),overlay.leadingAnchor.constraint(equalTo: imageView.leadingAnchor),overlay.trailingAnchor.constraint(equalTo: imageView.trailingAnchor),])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
TrackSelectionsViewController
class TrackSelectionsViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UINavigationControllerDelegate {
var myCollectionView: UICollectionView!
// array to track selected cells in the order they are selected
var selectedCells: [IndexPath] = []
// to load assests when needed
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
// will be used to get photos data
var fetchResult: PHFetchResult<PHAsset>!
override func viewDidLoad() {
super.viewDidLoad()
// set main view background color to a nice medium blue
view.backgroundColor = UIColor(red: 0.25,alpha: 1.0)
// request Options to be used in cellForItemAt
requestOptions.isSynchronous = false
requestOptions.deliveryMode = .opportunistic
// vertical stack view for the full screen (safe area)
let mainStack = UIStackView()
mainStack.axis = .vertical
mainStack.spacing = 0
mainStack.translatesAutoresizingMaskIntoConstraints = false
// add it to the view
view.addSubview(mainStack)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
mainStack.topAnchor.constraint(equalTo: g.topAnchor,constant:0.0),mainStack.leadingAnchor.constraint(equalTo: g.leadingAnchor),mainStack.trailingAnchor.constraint(equalTo: g.trailingAnchor),mainStack.bottomAnchor.constraint(equalTo: g.bottomAnchor),])
// create a label
let label = UILabel()
// add the label to the main stack view
mainStack.addArrangedSubview(label)
// label properties
label.textColor = .white
label.textAlignment = .center
label.text = "Select Photos"
label.heightAnchor.constraint(equalToConstant: 48.0).isActive = true
// setup the collection view
setupCollection()
// add it to the main stack view
mainStack.addArrangedSubview(myCollectionView)
// start the async call to get the assets
grabPhotos()
}
func setupCollection() {
let layout = UICollectionViewFlowLayout()
myCollectionView = UICollectionView(frame: self.view.frame,collectionViewLayout: layout)
myCollectionView.delegate = self
myCollectionView.dataSource = self
myCollectionView.backgroundColor = UIColor.white
myCollectionView.allowsMultipleSelection = true
myCollectionView.register(TestCVCell.self,forCellWithReuseIdentifier: "cvCell")
}
//MARK: CollectionView
func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
// add newly selected cell (index path) to our tracking array
selectedCells.append(indexPath)
// when selecting a cell,// we can update the appearance of the newly selected cell
// directly,because it won't affect any other cells
cell.index = selectedCells.count
}
}
func collectionView(_ collectionView: UICollectionView,didDeselectItemAt indexPath: IndexPath) {
// when de-selecting a cell,// we can't update the appearance of the cell directly
// because if it's not the last cell selected,the other
// selected cells need to be re-numbered
// get the index of the deselected cell from our tracking array
guard let idx = selectedCells.firstIndex(of: indexPath) else { return }
// remove from our tracking array
selectedCells.remove(at: idx)
// reloadData() clears the collection view's selected cells,so
// get a copy of currently selected cells
let curSelected: [IndexPath] = collectionView.indexPathsForSelectedItems ?? []
// reload collection view
// we do this to update all cells' appearance,// including re-numbering the currently selected cells
collectionView.reloadData()
// save current Y scroll offset
let saveY = collectionView.contentOffset.y
collectionView.performBatchUpdates({
// re-select previously selected cells
curSelected.forEach { pth in
collectionView.selectItem(at: pth,animated: false,scrollPosition: .centeredVertically)
}
},completion: { _ in
// reset Y offset
collectionView.contentOffset.y = saveY
})
}
func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
guard fetchResult != nil else { return 0 }
return fetchResult.count
}
func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cvCell",for: indexPath) as! TestCVCell
imgManager.requestImage(for: fetchResult.object(at: indexPath.item) as PHAsset,targetSize: CGSize(width:120,height: 120),contentMode: .aspectFill,options: requestOptions,resultHandler: { (image,error) in
cell.imageView.image = image
})
// get the index of this indexPath from our tracking array
// if it's not there (nil),set it to -1
let idx = selectedCells.firstIndex(of: indexPath) ?? -1
// set .count property to index + 1 (arrays are zero-based)
cell.index = idx + 1
return cell
}
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width
return CGSize(width: width/4 - 1,height: width/4 - 1)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
myCollectionView.collectionViewLayout.invalidateLayout()
}
func collectionView(_ collectionView: UICollectionView,minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(_ collectionView: UICollectionView,minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
//MARK: grab photos
func grabPhotos(){
DispatchQueue.global(qos: .background).async {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate",ascending: false)]
self.fetchResult = PHAsset.fetchAssets(with: .image,options: fetchOptions)
if self.fetchResult.count == 0 {
print("No photos found.")
}
DispatchQueue.main.async {
self.myCollectionView.reloadData()
}
}
}
}
注意:这仅是 示例代码! 不应将其视为“生产就绪”。
,不是要在您的CollectionView代表处设置您的var count: Int = 0
吗?
func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? TestCVCell {
cell.setupView()
cell.count = indexPath.item
print("Cell selected: \(indexPath)")
}
}