I want to create a table view that loads its data from two different data sources and chooses a specific nib depending on which one it is. How can I create a feed that mixes elements from two arrays of objects?
I was thinking about creating an array of objects with the objects being sorted by date. Then, I would check what the type of each object is at my cellforrowatindexpath method and use the nib that corresponds.
Is this the most efficient way of doing this?
Thank you
Creating a single array of objects and dealing with them correctly in cellForRowAtIndexPath is a good approach.
You should register each of your nibs with the table view, probably in viewDidLoad using registerNibForCellReuseIdentifier. Make sure you use a different reuseIdentifier for each, then dequeue the correct type in cellForRowAtIndexPath, once you've determined which type of cell it should be.
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "fooCell", bundle: .mainBundle()), forCellReuseIdentifier: "fooCellIdentifier");
tableView.registerNib(UINib(nibName: "barCell", bundle: .mainBundle()), forCellReuseIdentifier: "barCellIdentifier");
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell?
if (indexPathIsFooCell(indexPath)) {
cell = tableView.dequeueReusableCellWithIdentifier("fooCellIdentifier", forIndexPath: indexPath)
} else {
cell = tableView.dequeueReusableCellWithIdentifier("barCellIdentifier", forIndexPath: indexPath)
}
//customise the cell
return cell!
}
Here is Another Solution which Might help You
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
var cell = tableView.dequeueReusableCellWithIdentifier("relatedTableViewCell") as? relatedTableViewCell
if cell == nil {
let nib:Array = NSBundle.mainBundle().loadNibNamed("relatedTableViewCell", owner: self, options: nil)
cell = nib[0] as? relatedTableViewCell
}
let info = relatedQuizArr.objectAtIndex(indexPath.row) as! relatedInfo
cell?.relatedImage.sd_setImageWithURL(NSURL(string: info.rqImage!))
cell?.relatedLabel.text = (String: info.rqName!)
cell?.rQuizView.layer.shadowOffset = CGSizeMake(-1, 1)
cell?.rQuizView.layer.shadowOpacity = 0.4
return cell!
} else {
var cell = tableView.dequeueReusableCellWithIdentifier("relatedTableViewCell") as? relatedTableViewCell
if cell == nil {
let nib:Array = NSBundle.mainBundle().loadNibNamed("relatedTableViewCell", owner: self, options: nil)
cell = nib[0] as? relatedTableViewCell
}
if indexPath.row == 0 {
let label = UILabel(frame: CGRectMake(22, 10, 200, 40))
label.font = label.font.fontWithSize(25)
label.textAlignment = NSTextAlignment.Center
label.text = "Featured Quiz"
cell!.addSubview(label)
}
else {
}
let info = featuredQuizArray.objectAtIndex(indexPath.row) as! relatedInfo
cell?.relatedImage.sd_setImageWithURL(NSURL(string: info.featuredqImage!))
cell?.relatedLabel.text = (String: info.featuredqName!)
cell?.rQuizView.layer.shadowOffset = CGSizeMake(-1, 1)
cell?.rQuizView.layer.shadowOpacity = 0.4
return cell!
}
Hope It will help.
Related
I have implemented facebook native Ads in UITableView, for first 1-2 times it clickable but when I scroll tableview and come again back to the same cell, now Ads are not clicking, I am using swift 3.2
Below is the cell implementation.
let ad = adsManager.nextNativeAd
let cell = self.tableHome.dequeueReusableCell(withIdentifier: "HomeAdsTableViewCell", for: indexPath) as! HomeAdsTableViewCell
cell.message.text = ad?.body
cell.title.text = ad?.title
cell.callToActionButton.setTitle(ad?.callToAction, for: .normal)
if let pic = ad?.coverImage {
cell.postImage.setImageWithIndicator(imageUrl:pic.url.absoluteString)
}
ad?.registerView(forInteraction: cell.postView, with: self)
cell.selectionStyle=UITableViewCellSelectionStyle.none
return cell
//create a new object of nativead in your class//
var previousNativead : FBNativeAd?
let ad = adsManager.nextNativeAd
self.previousNativead?.unregisterView()
let cell = self.tableHome.dequeueReusableCell(withIdentifier: "HomeAdsTableViewCell", for: indexPath) as! HomeAdsTableViewCell
cell.message.text = ad?.body
cell.title.text = ad?.title
cell.callToActionButton.setTitle(ad?.callToAction, for: .normal)
if let pic = ad?.coverImage {
cell.postImage.setImageWithIndicator(imageUrl:pic.url.absoluteString)
}
previousNativead = ad
ad?.registerView(forInteraction: cell.postView, with: self)
cell.selectionStyle=UITableViewCellSelectionStyle.none
return cell
I suggest you follow the steps here. He told me about putting a facebook ad between cells.
https://www.appcoda.com/facebook-ads-integration/
It's like you do not make mistakes in the place you turn the idiot.
You should adapt this part in the link to your own.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if adsCellProvider != nil && adsCellProvider.isAdCellAtIndexPath(indexPath, forStride: UInt(adRowStep)) {
return adsCellProvider.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("idCellSample", forIndexPath: indexPath) as! SampleCell
cell.lblTitle.text = sampleData[indexPath.row - Int(indexPath.row / adRowStep)]
return cell
} }
You should also use this method on screen refreshes and turns.
func configureAdManagerAndLoadAds() {
if adsManager == nil {
adsManager = FBNativeAdsManager(placementID: "PLACEMENT_ID", forNumAdsRequested: 5)
adsManager.delegate = self
adsManager.loadAds()
}}
override func viewWillAppear(animated: Bool) {
configureAdManagerAndLoadAds()}
Finally you should check the relevant fields, which may not be compatible with content swift 3.
First in your viewDidLoad() check that you add
override func viewDidLoad() {
super.viewDidLoad()
adsManager = FBNativeAdsManager(placementID: "YOUR_PLACEMENT_ID_HERE", forNumAdsRequested: "YourNumAds")
adsManager.delegate = self
adsManager.loadAds()
}
then to comply with the FBNativeAdsManagerDelegate, you will need to add the following method, nativeAdsLoaded().
func nativeAdsLoaded() {
adsCellProvider = FBNativeAdTableViewCellProvider(manager: adsManager, forType: FBNativeAdViewType.genericHeight300)
adsCellProvider.delegate = self
if self.tableview != nil {
self.tableview.reloadData()
}
}
the last thing you should do is to registerView directly to the cell not to postView
ad?.registerView(forInteraction: cell, with: self)
hope that will help you.
When selecting multiple cells in my tabeview the cells out of view are being selected too. I understand that this is because i am reusing the cell and its maintaining its selection as i scroll down. I have found a few people with similar issues but cant translate their solutions across to resolve my issue. I have tried not dequeing a cell and just use:
let cell = NewBillSplitterItemCell()
but get:
unexpectedly found nil while unwrapping an Optional value
on the line:
cell.currentSplitters.text = splitterList
in the following code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
fetchBillItems()
let cell: NewBillSplitterItemCell = tableView.dequeueReusableCellWithIdentifier("NewBillSplitterItemCell") as! NewBillSplitterItemCell
let item = allItems[indexPath.row]
let numberOfSplitters = item.billSplitters?.count
if numberOfSplitters == 0 {
cell.currentSplitters.text = "No one is paying for this item yet."
} else {
var splitterList = "Split this item with "
let itemSplitters = item.billSplitters?.allObjects as! [BillSplitter]
for i in 0...Int((numberOfSplitters)!-1) {
if numberOfSplitters == 1 {
splitterList += "\(itemSplitters[i].name!)"
} else {
splitterList += ", \(itemSplitters[i].name!)"
}
}
cell.currentSplitters.text = splitterList
}
cell.name.text = item.name
cell.price.text = "£\(item.price!)"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark
{
cell.accessoryType = .None
selectedItems.removeAtIndex(selectedItems.indexOf(allItems[indexPath.row])!)
} else {
cell.accessoryType = .Checkmark
selectedItems.append(allItems[indexPath.row])
}
}
}
I dont quite understand what to do and any help would be great. Thanks
In addition to what #Mike said, inside of cellForRowAtIndexPath you need an additional check because cells get reused.
Something along the line
let isSelected = selectedItems[indexPath.row].selected
if isSelected{
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
Same thing inside of didSelectRowAtIndexPath you should update the data source instead of relying on the UI of your cell for that condition.
Assuming your cell is nil, you should use
let cell = tableView.dequeueReusableCellWithIdentifier("..." forIndexPath:indexPath) as! NewBillSplitterItemCell
instead of
let cell= tableView.dequeueReusableCellWithIdentifier("...") as! NewBillSplitterItemCell
This ensures that cell will never be nil.
Also, I would check if the correct identifier is being used in all of your .xib .storyboard files.
I know it is possible that we can load multiple uitableview cells under one xib in Objective C but is it possible also in swift?
I tried to use same logic I used in Objective C
var cellAuditDetails:AuditDetailsTableViewCell! = tableView.dequeueReusableCellWithIdentifier("customTrialDetailsCell", forIndexPath: indexPath) as! AuditDetailsTableViewCell
if indexPath.row == 0{
if(cellAuditDetails == nil)
{
cellAuditDetails = NSBundle.mainBundle().loadNibNamed("AuditDetailsTableViewCell", owner: self, options: nil)[0] as! AuditDetailsTableViewCell;
}
}
else{
cellAuditDetails = NSBundle.mainBundle().loadNibNamed("AuditDetailsTableViewCell", owner: self, options: nil)[1] as! AuditDetailsTableViewCell;
}
But got the following error ***Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'invalid nib registered for identifier (customTrialDetailsCell) - nib must contain exactly one top level object which must be a UITableViewCell instance'
Now If I use only one cell then its fine. But how do I load multiple cells under same xib? Because its irritating to take another xib for each new cell.
Somehow I managed to solve this problem in Swift 3. Thanks to #waris-shams. Here is the solution:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "customCell")
if indexPath.row == 0{
if(cell == nil)
{
let cellAuditDetails:AuditDetailsTableViewCell = Bundle.main.loadNibNamed("AuditDetailsTableViewCell", owner: self, options: nil)? [0] as! AuditDetailsTableViewCell
cell = cellAuditDetails
}
}
else{
if(cell == nil)
{
let cellAuditDetails:AuditDetailsTableViewCell = Bundle.main.loadNibNamed("AuditDetailsTableViewCell", owner: self, options: nil)? [1] as! AuditDetailsTableViewCell
cell = cellAuditDetails
}
}
}
It is possible if you distinguishing the views in your XIB (tags, custom classes, etc), then you don't even need to register the nib – all you have to do is this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cellIndex = 12345
var cellIdentifier = "MyAwesomeCell"
// dequeue cell or, if it doesn't exist yet, create a new one from the nib
var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
if !cell {
var topLevelObjects = Bundle.main.loadNibNamed("MyNibWithLotsOfCustomCells", owner: self, options: nil)
var index = (topLevelObjects as NSArray).indexOfObject(passingTest: {(_ obj: Any, _ idx: Int, _ stop: Bool) -> BOOL in
// if you distinguish the views by class
return type(of: obj) === NSClassFromString(cellIdentifier)
// if you distinguish the views by tag
return (obj as! UIView).tag == cellIndex
})
cell = topLevelObjects[index]
}
}
and one more thing to remember that use
dequeueReusableCell(withIdentifier:cellIdentifier)
instead of
dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
I have a collection view. On one of the collection cells, I have a tableview with 5 different custom cells. I am reusing each cell of the table view.
When the table is reloaded, I see new rows added just above the previously loaded cells. This appears like a new table has been added just above the previous table. I got this visual when I see the 3D view of the collection view:
Blue color is one of the 5 cells. Each time the table loads, it has added 5 sets of rows above previously loaded cells. What is happening here and how could this be possible?
override func viewDidLoad() {
super.viewDidLoad()
self.cabinTableView.registerNib(UINib(nibName:"CabinNetNib", bundle:
nil), forCellReuseIdentifier: cabinNetNib)
}
internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellDetail = tableViewDataSourceArray[indexPath.row] as? cabinDetailsMO
let cellType = cellDetails?.cellTypeEnum
if cellType == CellType.Net {
let netLabelCell = self.questionsTableView
.dequeueReusableCellWithIdentifier(questionLabelCellIdentifier) as?
NetLabelTableViewCell
netLabelCell!.selectionStyle = UITableViewCellSelectionStyle.None
netLabelCell!.netLabel.attributedText = cellDetails?.attributedQuestionString
return netLabelCell!
}
else if cellType == CellType.Video {
let videoCell = self.cabinTableView.dequeueReusableCellWithIdentifier(cabinVideoCellIdentifier)
as! cabinVideoTableViewCell
videoCell.videoName = cellDetails?.questionVideo
videoCell.loadVideoItem(cellDetails?.questionVideo, videoFields: nil)
videoCell.selectionStyle = UITableViewCellSelectionStyle.None
shouldReloadAllCells = true
image_videoCellIndex = indexPath.row
return videoCell
}
else if (cellType == CellType.captian) {
let captianCell = self.cabinTableView .dequeueReusableCellWithIdentifier(captianCellIdentifier) as!
CaptianTableViewCell
captianCell .selectionStyle = UITableViewCellSelectionStyle.None
captianCell.question_idLabel.text = cellDetails?.contentidName
return aptianCell
}
}
First cell is alone a nib.
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
}