Swift UICollectionView 滚动时消失 label.text 和颜色混合可能是BUG?
Posted
技术标签:
【中文标题】Swift UICollectionView 滚动时消失 label.text 和颜色混合可能是BUG?【英文标题】:Swift UICollectionView when scrolled disappears label.text and colors are mixed maybe BUG? 【发布时间】:2016-08-17 08:25:56 【问题描述】:你好我有 UICollectionView 当滚动消失标签。文本和颜色混合。
我的代码在这里
CustomCollectionViewCell
import UIKit
@IBDesignable
class CustomCollectionViewCell: UICollectionViewCell
@IBOutlet weak var label: UILabel!
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
setup()
override init(frame: CGRect)
super.init(frame: frame)
setup()
func setup()
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.blackColor().CGColor
self.layer.cornerRadius = 5.0
CustomCollectionViewLayout
import UIKit
public var CELL_HEIGHT = 22.0
public var CELL_WIDTH = 40.0
class CustomCollectionViewLayout: UICollectionViewLayout
// Used for calculating each cells CGRect on screen.
// CGRect will define the Origin and Size of the cell.
let STATUS_BAR = UIApplication.sharedApplication().statusBarFrame.height
// Dictionary to hold the UICollectionViewLayoutAttributes for
// each cell. The layout attribtues will define the cell's size
// and position (x, y, and z index). I have found this process
// to be one of the heavier parts of the layout. I recommend
// holding onto this data after it has been calculated in either
// a dictionary or data store of some kind for a smooth performance.
var cellAttrsDictionary = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()
// Defines the size of the area the user can move around in
// within the collection view.
var contentSize = CGSize.zero
// Used to determine if a data source update has occured.
// Note: The data source would be responsible for updating
// this value if an update was performed.
var dataSourceDidUpdate = true
override func collectionViewContentSize() -> CGSize
return self.contentSize
override func prepareLayout()
// Only update header cells.
if !dataSourceDidUpdate
// Determine current content offsets.
let xOffset = collectionView!.contentOffset.x
let yOffset = collectionView!.contentOffset.y
if collectionView?.numberOfSections() > 0
for section in 0...collectionView!.numberOfSections()-1
// Confirm the section has items.
if collectionView?.numberOfItemsInSection(section) > 0
// Update all items in the first row.
if section == 0
for item in 0...collectionView!.numberOfItemsInSection(section)-1
// Build indexPath to get attributes from dictionary.
let indexPath = NSIndexPath(forItem: item, inSection: section)
// Update y-position to follow user.
if let attrs = cellAttrsDictionary[indexPath]
var frame = attrs.frame
// Also update x-position for corner cell.
if item == 0
frame.origin.x = xOffset
frame.origin.y = yOffset
attrs.frame = frame
// For all other sections, we only need to update
// the x-position for the fist item.
else
// Build indexPath to get attributes from dictionary.
let indexPath = NSIndexPath(forItem: 0, inSection: section)
// Update y-position to follow user.
if let attrs = cellAttrsDictionary[indexPath]
var frame = attrs.frame
frame.origin.x = xOffset
attrs.frame = frame
// Do not run attribute generation code
// unless data source has been updated.
return
// Acknowledge data source change, and disable for next time.
dataSourceDidUpdate = false
var maxItemInASection: Int?
// Cycle through each section of the data source.
if collectionView?.numberOfSections() > 0
for section in 0...collectionView!.numberOfSections()-1
// Cycle through each item in the section.
if collectionView?.numberOfItemsInSection(section) > 0
for item in 0...collectionView!.numberOfItemsInSection(section)-1
// Build the UICollectionVieLayoutAttributes for the cell.
let cellIndex = NSIndexPath(forItem: item, inSection: section)
let xPos = Double(item) * CELL_WIDTH
let yPos = Double(section) * CELL_HEIGHT
let cellAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: cellIndex)
cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)
// Determine zIndex based on cell type.
if section == 0 && item == 0
cellAttributes.zIndex = 4
else if section == 0
cellAttributes.zIndex = 3
else if item == 0
cellAttributes.zIndex = 2
else
cellAttributes.zIndex = 1
// Save the attributes.
cellAttrsDictionary[cellIndex] = cellAttributes
if maxItemInASection < item
maxItemInASection = item
// Update content size.
let contentWidth = Double(maxItemInASection ?? 0) * CELL_WIDTH
let contentHeight = Double(collectionView!.numberOfSections()) * CELL_HEIGHT
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
// Create an array to hold all elements found in our current view.
var attributesInRect = [UICollectionViewLayoutAttributes]()
// Check each element to see if it should be returned.
for cellAttributes in cellAttrsDictionary.values.elements
if CGRectIntersectsRect(rect, cellAttributes.frame)
attributesInRect.append(cellAttributes)
// Return list of elements.
return attributesInRect
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
return cellAttrsDictionary[indexPath]!
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool
return true
HttpManager
import Foundation
class HttpManager
class func getRequest(url: String, parameter: Dictionary <String, AnyObject>?, completionHandler: (responseData: [Item]?, errorMessage: String?) -> ())
guard let url = NSURL(string: url) else
completionHandler(responseData: .None, errorMessage: "URL string was malformed")
return
let task = NSURLSession.sharedSession().dataTaskWithURL(url) (data, response, error) in
guard error == nil else
completionHandler(responseData: .None, errorMessage: error?.localizedDescription)
return
guard let data = data else
completionHandler(responseData: .None, errorMessage: "Empty Data")
return
guard let jsonSerialization = try? NSJSONSerialization.JSONObjectWithData(data, options:[]), jsonArray = jsonSerialization as? NSArray else
completionHandler(responseData: .None, errorMessage: "NSJSONSerialization error")
return
var items = [Item]()
jsonArray.forEach( (eachItem) -> () in
guard let dic = eachItem as? NSDictionary else return
guard let service = dic["Seats"] as? String, base = dic["Base"] as? String else
completionHandler(responseData: .None, errorMessage: "JSON structure missmatch")
return
let services = service.componentsSeparatedByString(",")
let item = Item(base: base, services: services)
items.append(item)
)
completionHandler(responseData: items, errorMessage: .None)
task.resume()
CustomCollectionViewController
class CustomCollectionViewController: UIViewController
@IBOutlet weak var collectionView: UICollectionView!
var items = [Item]()
override func viewDidLoad()
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
getDataFromServer()
func getDataFromServer()
HttpManager.getRequest(url, parameter: .None) [weak self] (responseData, errorMessage) -> () in
guard let strongSelf = self else return
guard let responseData = responseData else
print("Get request error \(errorMessage)")
return
guard let customCollectionViewLayout = strongSelf.collectionView?.collectionViewLayout as? CustomCollectionViewLayout else return
strongSelf.items = responseData
customCollectionViewLayout.dataSourceDidUpdate = true
NSOperationQueue.mainQueue().addOperationWithBlock( () -> Void in
strongSelf.collectionView!.reloadData()
)
// MARK: UICollectionViewDataSource
extension CustomCollectionViewController
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
return items.count
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return items[section].services.count + 1
选择了代码
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
// You must call super to animate selection
if indexPath.item == 0
print(items[indexPath.section].base)
else
let clickeditem = items[indexPath.section].services[indexPath.item - 1]
if let myStringc: String = items[indexPath.section].services[indexPath.item - 1]
var myStringArrc = myStringc.componentsSeparatedByString("*")
let satimdurum:Int = Int(myStringArrc[3])!
let sirano:Int = Int(myStringArrc[0])!
if sirano == 1 || sirano == 2
if sirano == 2
if satimdurum == 0
if koltuksiraid.contains(clickeditem)
if let index = koltuksiraid.indexOf(clickeditem)
koltuksiraid.removeAtIndex(index)
let selectedCell:UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
selectedCell.contentView.backgroundColor = UIColor.orangeColor()
else
koltuksiraid.append(clickeditem)
let selectedCell:UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
selectedCell.contentView.backgroundColor = UIColor(red: 62/256, green: 211/256, blue: 238/256, alpha: 1)
else
else
if satimdurum == 0
if koltuksiraid.contains(clickeditem)
if let index = koltuksiraid.indexOf(clickeditem)
koltuksiraid.removeAtIndex(index)
let selectedCell:UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
selectedCell.contentView.backgroundColor = UIColor.greenColor()
else
koltuksiraid.append(clickeditem)
let selectedCell:UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)!
selectedCell.contentView.backgroundColor = UIColor(red: 62/256, green: 211/256, blue: 238/256, alpha: 1)
else
print(koltuksiraid)
@pkc456 回答后的最新合集查看代码。
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
cell.label.font.fontWithSize(12)
cell.label.textColor = UIColor.whiteColor()
if indexPath.item == 0
cell.label.text = items[indexPath.section].base
cell.label.frame.size.width = 30
cell.label.frame.size.height = 30
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.contentView.backgroundColor = UIColor.clearColor()
else
if let myStringc: String = items[indexPath.section].services[indexPath.item - 1]
var myStringArrc = myStringc.componentsSeparatedByString("*")
let satimdurum:Int = Int(myStringArrc[3])!
let sirano:Int = Int(myStringArrc[0])!
if sirano == 1 || sirano == 2
cell.label.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.orangeColor()
cell.contentView.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.greenColor()
cell.label.layer.borderColor = UIColor.blackColor().CGColor
cell.alpha = 1
if sirano == 2
cell.frame.size.width = 80
cell.layer.frame.size.width = 80
CELL_WIDTH = 80.0
CELL_HEIGHT = 22.0
if satimdurum == 1
cell.label.alpha = 1
cell.label.layer.borderColor = UIColor.redColor().CGColor
cell.contentView.backgroundColor = UIColor.redColor()
cell.label.text = myStringArrc[1]
else
if myStringArrc[1] == "NULL"
cell.label.alpha = 0
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
else
cell.label.alpha = 1
cell.label.backgroundColor = UIColor.orangeColor()//Or put orange color as per your logic based on myStringArrc
cell.label.frame.size.width = 40
cell.label.frame.size.height = 40
cell.contentView.backgroundColor = UIColor.orangeColor()//Or put orange color as per your logic based on myStringArrc
cell.label.text = myStringArrc[1]
cell.label.text = "\(myStringArrc[1])-\(myStringArrc[5])"
else
cell.frame.size.width = 40
cell.layer.frame.size.width = 40
CELL_HEIGHT = 22.0
CELL_WIDTH = 40
if satimdurum == 1
cell.label.alpha = 1
cell.label.layer.borderColor = UIColor.redColor().CGColor
cell.contentView.backgroundColor = UIColor.redColor()
cell.label.text = myStringArrc[1]
else
if myStringArrc[1] == "NULL"
cell.label.alpha = 0
cell.label.backgroundColor = UIColor.clearColor()
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
else
cell.label.text = myStringArrc[1]
cell.label.alpha = 1
cell.label.frame.size.width = 40
cell.label.frame.size.height = 40
cell.contentView.backgroundColor = UIColor.greenColor()//Or put orange color as per your logic based on myStringArrc
else
cell.label.alpha = 0
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
cell.alpha = 0
cell.label.backgroundColor = UIColor.clearColor()
return cell
以及新 collectionview 代码之后的最新问题。
【问题讨论】:
【参考方案1】:-
您在 statring 和
if sirano == 2
循环的结尾设置 cell.frame.size.width, CELL_WIDTH, layer.width
。我删除了这个副本。
因为cell.label.font.fontWithSize
对所有人都很常见。我把它放在开头。
label.backgroundColor
设置三次。我使用三元运算符更新您的嵌套 if satimdurum == 1
条件,如下所示。
myStringArrc[1] is not NULL
时没有设置值。所以我更新 if-else 条件语句如下
cell.label.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.orangeColor()
cell.contentView.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.greenColor()
我删除了上述条件循环下方的静态行,因为这些值的设置与上限条件无关。
还将cell.label.text = "\(myStringArrc[1])-\(myStringArrc[5])"
移动到正确的条件状态。当sirano == 2
时,此值一直在标签上设置
转到 else 条件,再次检查 if satimdurum == 1
。这个条件已经在我上面的第 4 点检查过了。所以我在 if-loop (if sirano == 1 || sirano == 2
) 的初始阶段写这个检查。
前进到下一个if myStringArrc[1] == "NULL"
循环,我发现这个if-else和上面的if-else在检查上没有任何区别。当sirano == 2
或else
时,您正在做同样的事情。但是有可能我在这里遗漏了您的一些业务逻辑,因此我将第二个条件保持不变,并将遗漏的代码添加到您的else loop
。
更新了cellForItemAtIndexPath
的代码:-
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
cell.label.font.fontWithSize(12)
cell.label.textColor = UIColor.whiteColor()
if indexPath.item == 0
cell.label.text = items[indexPath.section].base
cell.label.frame.size.width = 30
cell.label.frame.size.height = 30
cell.label.layer.borderColor = UIColor.clearColor().CGColor
else
if let myStringc: String = items[indexPath.section].services[indexPath.item - 1]
var myStringArrc = myStringc.componentsSeparatedByString("*")
let satimdurum:Int = Int(myStringArrc[3])!
let sirano:Int = Int(myStringArrc[0])!
if sirano == 1 || sirano == 2
cell.label.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.orangeColor()
cell.contentView.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.greenColor()
cell.label.layer.borderColor = UIColor.blackColor().CGColor
cell.alpha = 1
if sirano == 2
cell.frame.size.width = 40
cell.layer.frame.size.width = 40
CELL_HEIGHT = 22.0
CELL_WIDTH = 40
if myStringArrc[1] == "NULL"
cell.label.alpha = 0
cell.label.backgroundColor = UIColor.clearColor()
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
else
cell.label.alpha = 1
cell.label.backgroundColor = UIColor.redColor()//Or put orange color as per your logic based on myStringArrc
cell.label.frame.size.width = 40
cell.label.frame.size.height = 40
cell.contentView.backgroundColor = UIColor.redColor()//Or put orange color as per your logic based on myStringArrc
cell.label.text = myStringArrc[1]
cell.label.text = "\(myStringArrc[1])-\(myStringArrc[5])"
else
if myStringArrc[1] == "NULL"
cell.label.alpha = 0
cell.label.backgroundColor = UIColor.clearColor()
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
else
cell.label.text = myStringArrc[1]
cell.label.alpha = 1
cell.label.backgroundColor = UIColor.redColor()//Or put orange color as per your logic based on myStringArrc
cell.label.frame.size.width = 40
cell.label.frame.size.height = 40
cell.contentView.backgroundColor = UIColor.redColor()//Or put orange color as per your logic based on myStringArrc
else
cell.label.alpha = 0
cell.label.backgroundColor = UIColor.clearColor()
cell.label.layer.borderColor = UIColor.clearColor().CGColor
cell.label.frame.size.width = 0
cell.label.frame.size.height = 0
cell.contentView.backgroundColor = UIColor.clearColor()
cell.alpha = 0
return cell
【讨论】:
那一行 cell.label.backgroundColor = (satimdurum == 1) ? : UIColor.redColor() : UIColor.orangeColor() 在“?”之后给出预期的表达式三元表达式中的错误 哦!我犯了一个语法错误。更新语法:-cell.label.backgroundColor = (satimdurum == 1) ? UIColor.redColor() : UIColor.orangeColor()
所有显示红色背景我会检查是否有其他的,我会在这里写好。
在我的回答中,请检查第8点。我认为由于相同的条件检查,可能存在一些漏洞。
我添加了您的代码还不错,但有问题我添加了顶部选择代码,我确实选择了单元格用户在单击执行背景蓝色并在用户再次单击相同单元格后添加到该单元格后的数组中删除它进入数组一切都很好,但是当我在左右滚动后选择了 4-5 个单元格时,我所有选择的蓝色背景在你的代码之后都丢失了。【参考方案2】:
滚动丢失后是否选择了单元格(蓝色)
您在didSelectItemAtIndexPath
上的koltuksiraid
数组中添加或删除clickeditem
koltuksiraid.removeAtIndex(index)
koltuksiraid.append(clickeditem)
当您滚动单元格时,正在重用视图并调用cellForItemAtIndexPath
。您应该在此处添加一个检查以确定单元格颜色,例如
let color = koltuksiraid.contains(myStringc) ? UIColor(red: 62/256, green: 211/256, blue: 238/256, alpha: 1) : UIColor.orangeColor()
单元格标签右下角后滚动
无论单元格大小如何,单元格标签都应位于单元格的中心位置。所以从storyboard
开始。从您的viewController
中删除所有与标签框相关的代码
一些像这样的双宽单元格
这是一个大问题。单元格是cellForItemAtIndexPath
之前的布局。您不能在此处更改单元格的帧大小。正确的地方是你的CustomCollectionViewLayout
的prepareLayout()
方法。在这里,当您计算每个项目的位置时,您需要检查哪个项目应该是双倍的。
从您的cellForItemAtIndexPath
中删除所有与单元格框架相关的代码。创建protocol
protocol CustomCollectionViewDelegateLayout: NSObjectProtocol
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, widthForItemAtIndexPath indexPath: NSIndexPath) -> CGFloat
在你的viewController
中实现了这个协议方法。返回值将是给定indexPath
的宽度
extension CustomCollectionViewController: CustomCollectionViewDelegateLayout
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, widthForItemAtIndexPath indexPath: NSIndexPath) -> CGFloat
//from indexPath.section and indexPath.item identify double width item
if sirano == 2 && satimdurum == 0
return regularWidth * 2
return regularWidth
在https://github.com/rishi420/UICollectionviewsample 处使用 xcode 7.2 制作了一个演示项目
【讨论】:
【参考方案3】:仔细查看您在 CustomCollectionViewController 类中对该函数的实现:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
你调用这个方法来获取一个新的单元格:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
没关系。但它不会每次都给你一个新的空白单元格。如果之前使用过该单元格,那么您将获得与上次相同的颜色和标签文本(以及其他所有内容)。
因此,您需要做的是检查该函数中的逻辑,并确保您关心的每个 属性每次 都被设置。以函数中的最后一个 else 块为例——它没有设置 cell.label.text——并且不能保证它是空的。这就是 ios 重绘单元格时文本和颜色跳跃的原因。
有时,如果单元格逻辑很复杂,最好在单元格出列后立即清理旧格式。以下是一些属性的示例:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
cell.label.text = nil
cell.label.backgroundColor = UIColor.clearColor()
cell.contentView.backgroundColor = UIColor.clearColor()
【讨论】:
感谢我使用reuseIdentifier 变量更改单元格但看起来相同的想法。我还能做什么? 问题不在于重用标识符。假设你有一个袋子,里面有几张纸。你随便拿出一个写在上面,然后放回包里。你继续做。最终你会挑选出一张已经有文字的纸吗?所以,如果你想让它们保持清洁,那么你必须擦掉旧的文字并更换它。这就是 dequeueReusableCell 的工作原理。您正在重复使用细胞,而不是每次都获得新的细胞。 试试这个。单元格出列后,直接在下一行将 cell.label.text 设置为 nil 并将背景颜色设置为清除。这样,您就会知道每次为每个单元格设置这些属性。看看有没有帮助。 是的,你能给我集成这个的示例代码吗? 我添加了它们但相同:(当我左右滚动消失标签时不起作用。以上是关于Swift UICollectionView 滚动时消失 label.text 和颜色混合可能是BUG?的主要内容,如果未能解决你的问题,请参考以下文章
Swift 4 UICollectionView 检测滚动结束
UICollectionView 滚动 SWIFT 滞后/缓慢
UITableViewCell 中的 UICollectionView 在 swift 3 中滚动不流畅
swift iOS - 快速滚动后 UICollectionView 图像混淆