How to localize tableView in Swift? - swift

I'm translating my app to multiple languages, storyboard translations are working perfectly, but I'm struggling to localize the content of tableView (tableView cell) and corresponding viewController.
I have created string file Localizable.strings for 2 languages so far.
FirstViewController contains an array with names which is passed to TableViewCell as UILabel and later to corresponding DetailViewController.
TableViewCell:
class UpperTableViewCell: UITableViewCell {
#IBOutlet weak var upperImage: UIImageView!
#IBOutlet weak var upperBodyType: UILabel!
#IBOutlet weak var upperLblName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
The objectID reference in Main.strings of the name label is
"BkN-Wi-Ai3.text" = "Label1";
However I can't figure out how to translate and parse the translated data of the name array and bodyType array into labels in TableView.
Thank you for suggestions.

Try with the following steps:
Make String extension (Tablename should be your .strings file):
extension String {
 
    func localized(bundle: Bundle = .main, tableName: String = "Localized”) -> String {
        return NSLocalizedString(self, tableName: tableName, value: "**\(self)**", comment: "")
    }
}
After that update your awakeFromNib:
override func awakeFromNib() {
super.awakeFromNib()
upperBodyType.text = upperBodyType.text?.localized()
upperLblName.text = upperLblName.text?.localized()
}

Related

How can I create a protocol for a UITableViewCell?

I have a few UITableViewCell's that have a few different layouts depending on if they're teammates, enemies, etc. But I have to treat each type differently etc. So I tried to cut down on replicated code by using a protocol,
I've created a protocol for a UITableViewCell like so:
protocol ViewCellProtocol {
var teamRank: UILabel! { get set }
var ranking: UILabel! { get set }
var rankDelta: UILabel! { get set }
var upDownIndicator: UILabel! { get set }
var textLabel : UILabel? { get }
}
This is my class:
import UIKit
class TeamStatsTableViewCell: UITableViewCell {
#IBOutlet weak var teamRank: UILabel!
#IBOutlet weak var ranking: UILabel!
#IBOutlet weak var rankDelta: UILabel!
#IBOutlet weak var upDownIndicator: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Then when I try to use it
func playerViewCell(_ tableView: UITableView, indexPath: IndexPath) -> ViewCellProtocol {
let cell = tableView.dequeueReusableCell(withIdentifier: "teamNameCell")! as! TeamStatsTableViewCell
let rank = sections[indexPath.section].data[indexPath.row]["rank"] as? Int
let (rating, ratingDelta) = selectRating(section: indexPath.section, row: indexPath.row)
let indicator = decideRatingIndicator(ratingDelta: ratingDelta, cell: cell)
cell.upDownIndicator?.text = "\(indicator)"
cell.teamRank?.text = "#\(rank!)"
cell.ranking?.text = String(rating)
cell.rankDelta.text = String(format: "%.0f", ratingDelta)
cell.textLabel?.text = sections[indexPath.section].data[indexPath.row]["username"] as? String
return cell as! ViewCellProtocol
}
I get an error:
Could not cast value of type '.TeamStatsTableViewCell' (0x1008d35d8) to '.ViewCellProtocol' (0x10ea37db8).
2018-04-24 22:00:27.137516-0600[434:72294] Could not cast value of type '.TeamStatsTableViewCell' (0x1008d35d8) to '.ViewCellProtocol' (0x10ea37db8).
I wish it would tell me what part it doesn't conform to. Any suggestions? Fairly new to swift kind of looking at protocols like interface{} in Go. Thanks.
Your custom cell class might be implementing all the stuff that's in the protocol, and that'd be enough in objC perhaps, but here you must declare that you implement that protocol explicitly
class TeamStatsTableViewCell: UITableViewCell, ViewCellProtocol {
#IBOutlet weak var teamRank: UILabel!
#IBOutlet weak var ranking: UILabel!
#IBOutlet weak var rankDelta: UILabel!
#IBOutlet weak var upDownIndicator: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
You need to declare that TeamStatsTableViewCell conforms to ViewCellProtocol, for example by adding this extension:
extension TeamStatsTableViewCell: ViewCellProtocol { }

Passing a variable from a customCell.xib's button to another UIViewController

My custom cell has a button that when clicked, the user can be taken to another ViewControler. That button has a titleLabel with the String of a user id. What I want to do is take the user to a new UIViewController, passing that clicked buttons's titleLabel (user id) to a variable on the new View Controller. That way, I can use that variable (user id) get further information from firebase and display it on the UI View controller.
on the .xib of the custom cell, I print the UID to make sure each button prints with the correspondent ID, which it does. I can't figure out a way to pass that ID to a new ViewController.
I tried researching online and I found out you can't do prepare(for segue) or performsegue(WithIdentifier) on a customCell.xib.
I tried doing delegates and then protocols but still couldn't get it to work. I am new with Swift. Any help would be great, thank you!
This is the customCell.Xib's Swift file:
class CustomCellTableViewCell: UITableViewCell {
#IBOutlet weak var postImage: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var descriptionLbl: UITextView!
#IBOutlet weak var profileImageBtn: UIButton!
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var view: UIView!
var btnSelected : Bool = false
var vcInstance: ProfilesTableVC?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
view.clipsToBounds = true
view.layer.cornerRadius = view.frame.size.width / 2
profileImage.layer.cornerRadius = profileImage.frame.size.width / 2
descriptionLbl.alpha = 0.7
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func profileBtnPressed(_ sender: Any) {
let passValue = UserProfileVC()
let profileTvC = ProfilesTableVC()
print (profileImageBtn.titleLabel!.text!)
var id = (profileImageBtn.titleLabel!.text!)
profileTvC.didSelectProfileBtn(senderID: id)
}
This is the tableViewController, where I everything gets loaded (not where I want to pass the value). I tried passing the value here and then do a prepareForSegue to pass the value to the new UIViewController but the value becomes nil after the segue happens. I am just including code where the .xib call the function from the table view.
class ProfilesTableVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate,UINavigationControllerDelegate, UIImagePickerControllerDelegate{
let cell = CustomCellTableViewCell()
func didSelectProfileBtn(senderID: String) {
senderIDArray.append(senderID)
var index = senderIDArray.count - 1
selectedCellUserID = senderIDArray[index]
performSegue(withIdentifier: "showSendersProfile", sender: Any?.self)
}
This is the UIViewController where I want to pass the variable and display further information from Firebase using that ID
import UIKit
import Firebase
class UserProfileVC: UIViewController {
let customCell = CustomCellTableViewCell()
let profileTvC = ProfilesTableVC()
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var nameLbl: UILabel!
var delegate : Any?
var getName = String()
var getsenderID = String()
let userDataRef = Database.database().reference().child("Users")
override func viewDidLoad() {
super.viewDidLoad()
print("ID is: \(profileTvC.selectedCellUserID)")
let sender = getsenderID
print(sender)
}
}
If you don't want to use protocols, you can addTarget to the button in the tableView - cellForRowAt method, also in that method you set the tag to the row index value.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let profileCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! MoviewReviewCell
reviewCell.profileImageBtn.tag = indexPath.row
reviewCell.profileImageBtn.addTarget(self, action: #selector(tappedOnXibCellButton), for: .touchUpInside)
return reviewCell
}
#objc func tappedOnXibCellButton(sender: UIButton) {
print(sender.tag)
performSegue(withIdentifier: reviewListToDetails, sender: sender.tag)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let identifier = segue.identifier
if identifier == "segueName" {
let destViewController = segue.destination as! DestinationClassViewController
let selectedIndex = sender as! Int
let profileId = profilesList[selectedIndex].id
destViewController.profileId = profileId
}
}
You seem to create a NEW ProfilesTableVC, and try to perform the segue on. The newly created one is not on your view stack, and not from your storyboard.
You could add this while returned cellForRowAt (at the ProfilesTableVC of course):
cell.vcInstance = self
Then in the button click
#IBAction func profileBtnPressed(_ sender: Any) {
print (profileImageBtn.titleLabel!.text!)
var id = (profileImageBtn.titleLabel!.text!)
vcInstance?.didSelectProfileBtn(senderID: id)
}
You can do it with protocols/delegates. I think you tried but there is something wrong with your trial.
Define a callback delegate:
protocol CustomCellDelegate: AnyObject {
func customCell(didSelectButton name: String)
}
You will notice extending AnyObject. This is to allow weak references, try to read about Swift ARC
Then, in your cell:
weak var delegate: CustomCellDelegate?
Then in the profileBtnPressed(_ sender: Any)
#IBAction func profileBtnPressed(_ sender: Any) {
var id = (profileImageBtn.titleLabel!.text!)
delegate?.customCell(didSelectButton: id)
}
When dequeing the cell:
(cell as? CustomCellTableViewCell)?.delegate = self

Custom TableViews + CustomCell

This is probably a newbie mistake and question but what's wrong with this code below?
class CustomCell: UITableViewCell {
#IBOutlet weak var TitleLabel: UILabel!
#IBOutlet weak var ImageView: UIImageView!
#IBOutlet weak var NumbersView: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func customInit(text: String, numbersText: String, photo: UIImage) {
self.TitleLabel.text = text
self.NumbersView.text = text
self.ImageView.photo = UIImage)
}
}
I'm following a tutorial called " SegmentedControl - Switch 'Custom' TableViews: Swift 3" and the user used the following code: func customInit(text: String, accessoryText: String but I changed it to include an UIImage but I don't know if thats correct or needed.
It should be like this:
self.ImageView.image = photo
self.NumbersView.text = numbersText

Possible to add tag to a static cell within a Dynamic Prototype UITableView? (Swift)

I have a Dynamic Prototype table that also has a few static cell. I am trying to allow one of these static cells to have two textfields within the single cell. I believe to do this, I will need to set tags for each of the textfields.
But, I am not sure how (if possible) to assign the tags to the below lines.
TableViewController:
case DiveMasterIndex:
cell = tableView.dequeueReusableCellWithIdentifier(Resource.DiveMasterCell)
(cell as! DiveMasterTableViewCell).textField.placeholder = Strings.DiveMaster.localized // tag 1001
case DiveMasterIDIndex:
cell = tableView.dequeueReusableCellWithIdentifier(Resource.DiveMasterCell)
(cell as! DiveMasterIDTableViewCell).textField.placeholder = Strings.DiveMasterID.localized // tag 1002
The two TableViewCells
class DiveMasterTableViewCell: UITableViewCell, UITextFieldDelegate
{
#IBOutlet var textField: UITextField!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
func textFieldDidEndEditing(textField: UITextField)
{
(self.tableViewController as! DiveDetailsNew2TableVC).diveModel.name = textField.text!
}
the second
class DiveMasterIDTableViewCell: UITableViewCell, UITextFieldDelegate
{
#IBOutlet var textField: UITextField!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
func textFieldDidEndEditing(textField: UITextField)
{
(self.tableViewController as! DiveDetailsNew2TableVC).diveModel.name = textField.text!
}
If you want one cell to have 2 textfields better way would be to create 2 outlets with different names for text fields instead of assigning tags to them. You do not need 2 cells for such case.

Does not have a member named ( Swift )

I have two labels (CountryCode, CountryName) in prototype cells, and I created a class for them called " CountryCell " and I created the TableViewController class called " CountriesVC " I made the labels' outlets in the CountryCell but I want to use them in CountriesVC and when I do so it reports an error that says 'CountriesVC' Does not have a member named 'CountryCode' .
Thanks a lot.
Here is the code where I want to use CountryCode and CountryName
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ChangeCountryCode" {
SelectedCCode = self.CountryCode.text
SelectedCName = self.CountryName.text
}
}
Here is CountryCell code :
class CountryCell: UITableViewCell {
#IBOutlet weak var CountryCode: UILabel!
#IBOutlet weak var CountryName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
These properties that you are trying to access are not declared in the current class. You must first create a variable of type "CountryCell" and then access the properties of that class.