I need to get the first cell in my tableView to be a different size from the rest. The rest of my cells are all under the class CustomPFTableViewCell, but the first one is a different cell so its under the class FirstPFTableViewCell, both of which extend from the class PFTableViewCell. Right now, I just used an if depending on the indexPath.row for whether or not the cell was the first cell. When its not it will load data for the cell from Parse.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
if(indexPath.row >= 1){
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomPFTableViewCell!
print("Loading Parse Database Files...")
// Extract values from the PFObject to display in the table cell
if let name = object?["Name"] as? String {
cell?.nameTextLabel?.text = name
print("Loading " + name)
}
if let author = object?["authorName"] as? String {
cell?.authorTextLabel?.text = author
}
if let likes = object?["Likes"] as? Int {
let stringVal = String(likes)
cell?.numLikes.text = stringVal
}
if let descrip = object?["Description"] as? String {
cell?.descriptionHolder = descrip
}
let initialThumbnail = UIImage(named: "Unloaded")
cell.customFlag.image = initialThumbnail
if let thumbnail = object?["imageCover"] as? PFFile {
cell.customFlag.file = thumbnail
cell.customFlag.loadInBackground()
}
return cell
}
print("Finished loading!")
let cell = tableView.dequeueReusableCellWithIdentifier("firstCell") as! PFTableViewCell
return cell
}
The end is empty because I'm not sure how to go about changing the one/first cell's size. (In the Interface Builder its set to 60). I guess the most important part in solving this is this:
let cell = tableView.dequeueReusableCellWithIdentifier("firstCell") as! PFTableViewCell
return cell
}
In order to play with the size of the cell you have to implement the UITableViewDelegate function
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return firstCellHeight
} else {
return customCellHeight
}
Related
I have three different types of custom UITableCells. I have an if statement that sets them up:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if somePosts[indexPath.row].typeOfPost == .linkPost {
let cell: LinkTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .picturePost {
let cell: PictureTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .textPost {
let cell: TextTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
} else {
print("Type of post is not link, picture, or text")
}
}
Each of the custom cells has similar labels such as title and time. I would like to set these labels using the same line of code, such as:
cell.titleLabel.text = "Some title here"
However, in this example, I get an error saying I am using an unresolved identifier "cell," obviously because my variables are being declared non-globally. Is there a way around this since swift is strongly typed? Thanks!
Make a protocol that your TableViewCell classes extend, and store cell as a variable of that type.
protocol MyTableViewCell {
var titleLabel: UILabel { get }
// ...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier: String
switch somePosts[indexPath.row].typeOfPost {
case .linkPost: identifier = "linkTableViewCell"
case .picturePost: identifier = "pictureTableViewCell"
case .textPost: identifier = "textTableViewCell"
default: fatalError("Type of post is not link, picture, or text")
}
guard let cell = self.tableView.dequeueReusableCell(withIdentifier: identifier) as? MyTableViewCell else {
fatalError("Cell isn't castable to MyTableViewCell")
}
cell.titleLabel.text = "Some title here"
// ...
}
You have three basic solutions.
Repeat cell.text = ... inside each block. But this isn't what you really want as stated in your question.
Have your three custom cell classes all extend a common base class. Have this base class define any common properties.
Define a protocol with the common properties and have each of your custom cell classes conform to the protocol.
For options 2 and 3 you would declare a variable of the base/protocol type before the first if statement. Then after the whole if/else block, you can assign any of the common properties.
If you need to update any cell type specific properties, you can do that inside the appropriate block as well.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: BaseTableViewCell?
if somePosts[indexPath.row].typeOfPost == .linkPost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .picturePost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .textPost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
} else {
print("Type of post is not link, picture, or text")
}
if let cell = cell {
cell.commonProperty = ...
return cell
} else {
return nil // this shouldn't happen but if it does, you have a bug to fix
}
}
If the subclasses each have their own titleLabel property, you will need to make them all conform to a protocol. Let's call it ConfigurableCell.
protocol ConfigurableCell {
var titleLabel: UILabel { get set }
}
Then, you can initialize your cells all the same way, but declare them as a ConfigurableCell:
var cell: ConfigurableCell? = nil // not set yet
if somePosts[indexPath.row].typeOfPost == .linkPost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .picturePost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
} else if somePosts[indexPath.row].typeOfPost == .textPost {
cell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
}
guard let cell = cell else {
// how to handle this error case is up to you
print("Type of post is not link, picture, or text")
return UITableViewCell()
}
// now, cell is a ConfigurableCell with a titleLabel property, regardless of class
cell.titleLabel.text = "Some title"
Of course, UITableViewCell does have a built-in textLabel property, which you could try to utilize in your cell classes, and then a protocol wouldn't be necessary, because the property is in UITableViewCell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("Wall Delegate: caso 2")
let cell = tableView.dequeueReusableCellWithIdentifier("you1on1Cell", forIndexPath: indexPath) as! OneToOneCell
let cell1 = tableView.dequeueReusableCellWithIdentifier("other1on1Cell", forIndexPath: indexPath) as! OneToOneCell
let newMessage = messages[indexPath.row]
if(newMessage.you == true){
if let picture = imageYou { cell.profilePictureyou.image = picture }
if let textmsg = newMessage.text {
cell.messageTextYou.text = textmsg
}
return cell
} else {
if let picture = imageOther { cell1.profilePicture.image = picture }
if let textmsg = newMessage.text { cell1.messageText.text = textmsg }
return cell1
}
I don't know what is the problem, this error is being showed up, however app is still running, but text is only displayed when I click on tableview with mouse on simulator - it is the first app I am developing and it is the first time I am using a table view with two different cell structure (however it is the same cell file). This is meant to be a chat.
I have UITableView with images in each cell and I want my scrolling be smooth. So I read some post on stackerflow and now I am loading my images in background thread:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
var node = nodesArray[indexPath.row] as! NSMutableDictionary
if !checkIfImagesLoaded(node[Api.pictures] as! NSMutableArray) {
cell.id = node[Api.buildingStatusId] as! Int
cell.date.text = node[Api.date] as? String
cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
cell.indicator.hidesWhenStopped = true
cell.indicator.startAnimating()
dbHelper.getBuildingStatusNode(node, callback: self)
} else {
cell.id = node[Api.buildingStatusId] as! Int
cell.date.text = node[Api.date] as? String
cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
var image = WorkWithImage.loadImageFromSD((node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)! // Bad
dispatch_async(dispatch_get_main_queue()) {
cell.imgView.image = image
cell.indicator.stopAnimating()
}
}
}
return cell
}
dbHelper.getBuildingStatusNode(node, callback: self) method executes in background thread also. But for some reasons when I scroll I still get some delay. I read that it is good to fill my cell with data in tableView:willDisplayCell method instead tableView:cellForRowAtIndexPath and I should return cell as faster as I can in tableView:cellForRowAtIndexPath method. The question is should I now use the code like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
var cell: BuildingStatusCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! BuildingStatusCell
var node = nodesArray[indexPath.row] as! NSMutableDictionary
if !checkIfImagesLoaded(node[Api.pictures] as! NSMutableArray) {
cell.id = node[Api.buildingStatusId] as! Int
cell.date.text = node[Api.date] as? String
cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
cell.indicator.hidesWhenStopped = true
cell.indicator.startAnimating()
dbHelper.getBuildingStatusNode(node, callback: self)
} else {
cell.id = node[Api.buildingStatusId] as! Int
cell.date.text = node[Api.date] as? String
cell.count.text = String((node[Api.pictures] as! NSMutableArray).count)
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
var image = WorkWithImage.loadImageFromSD((node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)!
dispatch_async(dispatch_get_main_queue()) {
cell.imgView.image = image
cell.indicator.stopAnimating()
}
}
}
}
And what else I can do to make my scrolling more smooth? BCS I still have lags even when I use willDisplayCell method.
P.S. Image size in my UITableViewCells is fixed.
Try the following
Try removing any shadows.
Make the cell and its subviews opaque. Don't use alpha/transparency.
Try decoding the images on a background thread :
Decode images in background thread?
First of all it is better to subclass UITableViewCell and just pass your Api object to cell and make this mapping inside cell.
Also it is better to use some library like: AFNetworking's extension or AsyncImageView - it is possible to use in Swift.
Try to remove any border rounding, shadow, transparencies - they can cause delays. In this case you need rasterization:
Related question:
Sluggish scrolling experience when using QuartzCore to round corners on UIImageView's within a UITableViewCell
When you load image from URL it takes time to download image and that cause block in scrolling UITableView.
You are doing so much work simply do
Use this class SDWebImage
and in your bridging header file :
#import "UIImageView+WebCache.h"
Here is a code example that should work :
let block: SDWebImageCompletionBlock! = {(image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
println(self)
}
let url = NSURL(string: node[Api.pictures] as! NSMutableArray)[0]["image"] as! String)
cell.imgView.sd_setImageWithURL(url, completed: block)
As the title suggests, I am having trouble with my UISearchController displaying the wrong cell prototype for the first cell in the search results.
Background Information: I have two cell prototypes, one without an image (identifier: basicCell) and another with a UIImageView (identifier: imageCell). Cells work perfectly when not searching.
Detailed Description of the Problem: When I click on the search bar everything is fine until I start searching for something. When I do, the first cell always has the imageCell identifier (a gray empty image view is shown denoting the lack of an image), no matter what. NB: Before searching anything, the first cell in the tableview has a custom image... Maybe that's of note?
Anyway I have no idea what I am doing wrong. Would anyone mind helping?
Code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (self.resultSearchController.active) {
if hasImageAtIndexPath(indexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCellImage
let event = filteredTableData[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
//This is the image
cell.attachment.image = profile.image
cell.attachment.layer.cornerRadius = 1
cell.attachment.clipsToBounds = true
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCell
let event = filteredTableData[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
return cell
}
} else {
if hasImageAtIndexPath(indexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCellImage
let event = events[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
cell.attachement.image = profile.image
cell.attachment.layer.cornerRadius = 1
cell.attachment.clipsToBounds = true
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier, forIndexPath: indexPath) as! TimelineTableViewCell
let event = events[indexPath.row]
cell.content.text = profile.content
cell.name.text = profile.name
return cell
}
}
}
And this is my code that checks for an image:
func hasImageAtIndexPath(indexPath:NSIndexPath) -> Bool {
let event = events[indexPath.row]
let imageArray = [event.image]
for eventImage in imageArray {
if eventImage != nil {
return true
}
}
return false
}
You need to have an if-else clause in your hasImageAtIndexPath: function just like you have in your cellForRowAtIndexPath:. If the table view is the search table, then event needs to be defined the same way as you have in cellForRowAtIndexPath:,
func hasImageAtIndexPath(indexPath:NSIndexPath sender:UITableView) -> Bool
if (self.resultSearchController.active){
let event = filteredTableData[indexPath.row]
}else{
let event = events[indexPath.row]
}
let imageArray = [event.image]
for eventImage in imageArray {
if eventImage != nil {
return true
}
}
return false
}
I want the highlight to change the size and appearance of an object inside the collection view.
How can I set object properties in a collection view cell, within the "didHighlight" method?
In "cellForItemAtIndexPath" you declare the reusable cells as the class
and just use "cell.MyOutlet.backgroundColor = UIColor.blueColor()"
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if collectionView == self.CollectionViewController {
let (FriendFirstName,FriendLastName) = friends[indexPath.row]
let cell: CustomCellA = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell", forIndexPath: indexPath) as! CustomCellA
if indexPath.section == 0 {
cell.cellTitle.text = Name
cell.imgCell.image = UIImage(named: Pics[indexPath.row])
cell.imgCell.layer.masksToBounds = true
cell.self.imgCell.layer.cornerRadius = 20
return cell
} else {
let cell2: AddCell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell2", forIndexPath: indexPath) as! AddCell
return cell2
}
} else if collectionView == self.EmojiCollectionViewController {
let cellB: CustomCellB = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellB", forIndexPath: indexPath) as! CustomCellB
cellB.MyLabel.text = arrayOne[indexPath.row]
return cellB
} else {
let cellC: CustomCellC = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellC", forIndexPath: indexPath) as! CustomCellC
// ...Set up cell
let height = self.CollectionViewController2.frame.height
cellC.frame = CGRectMake(cellB.frame.origin.x, 0, cellB.frame.size.width, height)
cellC.updateConstraintsIfNeeded()
cellC.layoutIfNeeded()
cellC.imgVw.image = UIImage(named: pictures[indexPath.row] as! String)
return cellC
}
}
func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
if collectionView == self.CollectionViewController {
if indexPath.section == 0 {
let cell: CustomCellA = CustomCellB()
cell.MyLabel.backgroundColor = UIColor.blueColor() //crashes due to nil value)
}
} else {
}
}
I tried using a similar definition in didHighlight and it keeps crashing.
Let didHighlightItemAtIndexPath only change the data, not the view. So, make friends[indexPath.row] an object or add another parameter to tuple. And in didHighlightItemAtIndexPath do something like the following:
if collectionView == self.CollectionViewController {
if indexPath.section == 0 {
let (fname, lname, color) = friends[indexPath.row];
friends[indexPath.row] = (fname, lname, UIColor.blueColor())
}
}
And in cellForItemAtIndexPath:
if collectionView == self.CollectionViewController {
let (FriendFirstName, FriendLastName, color) = friends[indexPath.row]
if indexPath.section != 0 {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell2", forIndexPath: indexPath) as! AddCell;
return cell;
} else if color == nil {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell", forIndexPath: indexPath) as! CustomCellA;
cell.cellTitle.text = Name
cell.imgCell.image = UIImage(named: Pics[indexPath.row])
cell.imgCell.layer.masksToBounds = true
cell.self.imgCell.layer.cornerRadius = 20
return cell
} else {
cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellB", forIndexPath: indexPath) as! CustomCellB;
// your code for CustomCellB
return cell;
}
}
EDIT: Updated, so instead of objects it uses tuples. Also added the functionality that you need. Basically, you need to create two prototype cells in the interface builder with different Reuse Identifiers and Classes. And then dequeue the correct identifier in the index path. Also, I refactored some of your code and if I were you I would create a different function for each collectionView and do something like:
if collectionView == self.CollectionViewController {
return self.dequeueCollectionCell(indexPath);
} else if collectionView == self.EmojiCollectionViewController {
return self.dequeuEmojiCell(indexPath);
} else {
return self.dequeueSomeOtherCell(indexPath);
}
Also, the code that you provided... I hope it is not an actual production code and you changed the values for this forum. Otherwise, in couple of days even, you are going to get lost in what is happening here. Too many inconsistent variable names and identifiers.
One more also. Use naming conventions in your class names. Read this forum post for more information. Apple uses camelCase everywhere. In majority of instances, the first letter is capitalized for class names, not object names.
first you have to define the collectionView Cell then do what ever you want on that cell. to define your sell add the below lines into didHighlightItemAtIndexPath
if let cellToUpdate = self.dataCollection.cellForItemAtIndexPath(indexPath) {
//your code here.
}