I made a view in a xib file which is loaded in the main ViewController called "ExperienceScreen". Adding this xib view to the ExperienceScreen works perfectly. The problem is that I would like to add this xib view in a UITableViewCel. I am using the following code to do that :
let experiences = service.getExperiences()
// 3. Loop through the array of experiences
for element in experiences {
if let customView = Bundle.main.loadNibNamed("ExperienceDetail", owner: self, options: nil)?.first as? ExperienceDetail
{
customView.lblTitle.text = element.title
customView.lblCompany.text = element.company
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
cell.addSubview(customView)
cell.bringSubview(toFront: customView)
}
}
tableView.reloadData()
When launching, the subview is not shown in the UITableViewCell. The customView View is filled correctly with the xib View. I checked this using a breakpoint.
Someone knows what I'am doing wrong?
Many thanks for helping !!
If you want to display your xib file as UITableViewCell, then following scenario works
1. make sure your xib class is sub class of UITableViewCell.
2. register your xib
//class of xib file
class TableCell: UITableViewCell {
static let identifier = "TableCell"
static let nib = UINib(nibName: "TableCell", bundle: nil)
}
// In view controller
func setupTableView() {
tableView.dataSource = self
tableView.delefate = self
tableView.register(TableCell.nib, forCellReuseIdentifier:
TableCell.identifier)
}
call setupTableView() in viewDidLoad()
Don't try to set up your cells all at once.
You should implement the UITableViewDataSource protocol and configure each cell using the method tableView(_:cellForRowAt:). In this method, you can call register(_:forCellReuseIdentifier:) to use your XIB for newly created cells.
There are lots of tutorials on creating table views where you can find step-by-step instructions on doing this.
Related
I've a custom table view cell which has a label view. I added tapGesture to invoke a function when that view is clicked.
Currently my custom view cell is in it's own swift file. I added following code to enable 'Share extension' when clicked on that label.
CustomCellView.swift
10 myLabel.isUserInteractionEnabled = true
11 let tap = UITapGestureRecognizer(target: self, action: #selector(tapLabelGesture))
12 myLabel.addGestureRecognizer(tap)
13 func tapLabelGesture() {
14 print("Clicked on Label ")
let url="www.google.com"
15 let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
16 activityVC.popoverPresentationController?.sourceView = self.view
17 self.present(activityVC, animated: true, completion: nil)
18 }
I get compiler error on line 16 for self.view and 17 for self.present(). Question is how do I provide view for the popup?
This code I used for another view (without table view or cell) as a test and it worked fine. So I'm trying do the same technique for a tableview/cell. How do I resolve this issue? Any help is appreciated.
For line 16:
You are getting an error which says your class CustomCellView has no member view because your class is subclass of UITableViewCell not UIViewController because UIViewController has that property and your CustomCellView have contentView for that.
For line 17:
same as above your class is not subclass of UIViewController thats why you can not use self.present for that.
Solution:
Instead of using UITapGestureRecognizer you can use UIButton which you can place on UILabel and in your UIViewController class add button.tag and button.addTarget in your cellForRowAt method.
Then you can add your code in your button method and present UIActivityViewController.
Hope this will help.
As #DharmeshKheni mentioned you cannot use a subclass of UITableViewCell like UIViewController. It doesn't provide view property and present method.
Answering your question, you could store a closure in the CustomCellView:
var onLabelTappedCallback: (() -> Void)?
call it in your selector:
#objc private func tapLabelGesture() {
onLabelTappedCallback?()
}
and finally implement in the cellForRowAt method:
cell.onLabelTappedCallback = {
print("Label tapped")
//Present additional vc
}
This solution will work with UIButton as well.
I got more ideas from this thread on SO,
how to recognize label click on custom UITableViewCell - Swift 3
With the an extension, I was able to solve this issue.
I have created a structure for different Cell Identifiers:
enum CustomCellIdentifiers
{
static let cellForCountry = "cellForCountry"
static let cellForCity = "cellForCity"
static let cellForStoreType = "cellForStoreType"
}
and I am registering the cell to the table as per switch case, like:
view.tableForItems.register(UINib.init(nibName: cellIdentifier, bundle: nil), forCellReuseIdentifier:cellIdentifier)
but when I registered the cell I got at error that the reuseIdentifier is nil:
class CustomTableCell: UITableViewCell
{
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
switch self.reuseIdentifier ?? "cellForCountry" //It only work with country cell
{
case CustomCellIdentifiers.cellForCountry:
print("cellForCountry") break;
case CustomCellIdentifiers.cellForCity:
print("cellForCity") break;
case CustomCellIdentifiers.cellForStoreType:
print("cellForStoreType")
break;
default: break
}
}
}
set reUseIdentifier in the xib file too.
if you are not able to set the reuseIdentifier in xib class means you created xib file from UIView. Instead
Take the UITableviewcell Class to create xib not the UIView..
then you are able to assign reuseIdentifier from xib file
Please see the image below
I have a UICollectionViewCell class "ProductCell"; I am trying to access the current navigation controller in order to update a barbuttonicon. I have tried the following code as this is what I use in my other UIViewControllers:
let nav = self.navigationController as! MFNavigationController
nav.updateCartBadgeValue()
However it states that the
value of type ProductCell has no member navigationController
I am aware that this is not a UIViewController but surely you should be able to access the current navigation controller the same way?
I also know that you can access the navigation controller by using UIApplication in the following way:
let navigationController = application.windows[0].rootViewController as! UINavigationController
I am not sure if that is a good way of doing it though.
Any help is much appreciated
Thanks
UIResponder chain will help here.
You can search the responder chain to find the controller for any view
extension UIView {
func controller() -> UIViewController? {
if let nextViewControllerResponder = next as? UIViewController {
return nextViewControllerResponder
}
else if let nextViewResponder = next as? UIView {
return nextViewResponder.controller()
}
else {
return nil
}
}
func navigationController() -> UINavigationController? {
if let controller = controller() {
return controller.navigationController
}
else {
return nil
}
}
}
controller() will return the closest responder that is of type UIViewController
Then on the returned controller you just need to find its navigation controller. You can use navigationController() here.
The simplest way is to add a property to you cell class that weakly references a UINavigationController
weak var navigationController: UINavigationController?
you will need to assign it in your cellForRow(atIndexPath:_) method.
let cell = tableView.dequeueReusableCell(withIdentifier: "yourReuseID") as! YourCellClass
cell.navigationController = navigationController //will assign your viewController's navigation controller to the cell
return cell
Unless things change, this is a good way to do it. To give you an example of a messier solution... You could add a
let hostViewController:UIViewController
property to your cell and add an initializer to handle it
let cell = ProductCell(vc: self)
But I don't think that's a better way to do it. your suggestion works fine.
let navigationController = application.windows[0].rootViewController as! UINavigationController
I'm having the hardest time finding an answer for this.
I have a xib view that is within a scrollview that is within a view controller. In the xib I have a button with an action and I need to segue to a view controller I have in my storyboard. I also would like to be able to use a custom segue.
So far, I have read that I can instantiate the viewcontroller from the storyboard to segue to it. But then I don't know how to present that controller.
thanks for any help...
UPDATE:
this is the code I'm using to perform the segue.
In parent ViewController:
static var referenceVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
LevelSelectViewController.referenceVC = self
setupScrollView()
}
code in xib view file
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sightWordController")
let parent = LevelSelectViewController.referenceVC!
let segue = InFromRightCustomSegue(identifier: "test", source: parent, destination: vc)
segue.perform()
As noted in the comments, Segues are typically confined to storyboard usage as noted in the documentation. You can implement a custom xib view in a storyboard via #IBDesignable like approaches and have you're view load from the xib into the storyboard file/class. This way, you gain the benefits of both worlds. Otherwise, you may want to approach this in another fashion (such as delegates/target-action events, etc).
You may also climb the responder chain and call a segue related to the VC loaded from the storyboard (the segue doesn't necessarily have to be attached to any particular action) via getting a reference to the VC and calling the segue. You can climb the responder chain in a manner such as the example code below:
protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
protocol ViewControllerTraversable {
func viewController<T: UIViewController>() -> T?
}
extension UIView: ViewControllerTraversable {
func viewController<T: UIViewController>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UITableViewCell: ChildViewControllerContainer {
weak var parentViewController: UIViewController? {
return viewController() as UIViewController?
}
}
I'm working with the PFQueryTableViewController and setting it up to find only friendships for this user which has been approved or sent to him.
"fromUser" and "toUser" are pointers to the user class, where I need the username and profilePicture from for each of the users contained in the queries results.
Now I try to fetch those in my cellForRowAtIndexPath method and load the image:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cellIdentifier = "contactCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! PFTableViewCell
if let user = object?["fromUser"] as? PFUser {
user.fetchInBackgroundWithBlock({ (user, error) -> Void in
if error != nil {
println("Could not fetch user object")
}
let user = user as! PFUser
cell.textLabel?.text = user.username!
cell.imageView?.file = user["profilePicture"] as? PFFile
cell.imageView?.loadInBackground()
})
} }
Getting the username to display in the tableView works just fine, but the image is actually never loaded. I tried different approaches to get the image loaded, but my cell's imageView property is always nil.
The prototype's class is set to PFTableViewCell
The controller is linked to the view in storyboard
Please let me know, if you guys have any idea why this built in property is nil and how to fix that.
Thanks,
Well I found a workaround which actually works quite good:
Create a prototype cell in your TableView (Storyboard) and set it's class to the normal "UITableViewCell"
Set the reuseIdentifier property of this cell to a value of your liking
Let your custom cell's file owner (I created a nib file for this cell) to be a subclass of PFTableViewCell
Create custom outlets for the textLabel and imageView
Register that Nib for the reuseIdentifier set in Step 2 in your TableViewController
Finally, use that class in your cellForRowAtIndexPath method like this:
let cell: PeopleTableViewCell! = tableView.dequeueReusableCellWithIdentifier("peopleCell") as? PeopleTableViewCell
I hope this will fix your problems too.
Regards,
[Edit]: It seems like SO doesn't like my code snippet to be formatted. It's embedded in code tags..
The PFTableViewCell does not work well with standard styles. So you have to make your cell a custom cell.
These re the steps I took to make it work (in Storyboard):
subclass PFTableViewCell with you own class
in the storyboard, customize the prototype cell using a custom cell
drag a UIImageView from the palette but then set its class as PFImageView
connect it to an IBOutlet in your PFQueryTableViewController subclass
implement cellForRowAtIndexPath: in the PFQueryTableViewController the way you did (your code was OK).
That way worked for me.