I am trying to access a variable from a different class. What am I doing wrong?
class ViewController: UIViewController {
var restaurantName = "Test"
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnClicked(_ sender: Any) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
let pop = popView()
self.view.addSubview(pop)
}
}
}
here is the class I am trying to access it from:
class popView: UIView{
fileprivate let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize:28, weight: .bold)
label.textAlignment = .center
//label.text = "TITLE"
label.text = restaurantName
return label
}()
}
How can I access the 'restaurantName' variable in the 'popView' class?
thanks in advance
You don't want to tightly couple the view and the view controller.
You should have a property on your PopView to hold the text. You can then assign a value to this property when you create the PopView instance.
class PopView: UIView{
fileprivate let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize:28, weight: .bold)
label.textAlignment = .center
//label.text = "TITLE"
label.text = restaurantName
return label
}()
var titleText: String? {
didSet {
self.titleLabel.text = titleText
}
}
}
class ViewController: UIViewController {
var restaurantName = "Test"
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnClicked(_ sender: Any) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
let pop = popView()
pop.titleText = restaurantName
self.view.addSubview(pop)
}
}
}
You simply cant access 'restaurantName' variable in the 'popView' class since the "popupView" class is an instance of "ViewController".
If you want to assign property "restaurantName" to "titleLabel" simply remove the "fileprivate" from property "titleLabel" and add this line before the "addSubview" func.
pop.titleLabel.text = restaurantName
also change your "popView" class to the following
class popView: UIView{
weak var titleLabel: UILabel!
func awakeFromNib() {
super.awakeFromNib()
titleLabel = UILabel()
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.font = UIFont.systemFont(ofSize:28, weight: .bold)
titleLabel.textAlignment = .center
}
Related
SOLUTION FOUND - credit Sanzio Angeli, used lazy var
Trying the access the pageIndex in order to update the page indicator , i try and create an instance of a class which has the public property of pageIndex, but the moment i try and do so its crashing the app, can any on one please suggest where i am doing wrong, Strange enough if i do not declare the instance globally but inside a method, the app does not crash
Error i get - Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeeebccea8)
Below is class whose instance i am trying to create and after that the class where i am trying to call it by creating a global instance at top, i am trying to use it in func moveToNext()
import UIKit
class ContentViewController: UIViewController {
let contentDesign = ContentView()
var pageIndex = 0
var pageHeading = ""
var pageContent = ""
var pageImage = ""
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(contentDesign)
contentDesign.pagerContent.text = pageContent
contentDesign.pagerHeader.text = pageHeading
contentDesign.pagerImage.image = UIImage(named: pageImage)
// Do any additional setup after loading the view.
}
}
//////---------
class MasterView: UIViewController {
var container = UIView()
var lowerCotainer = UIView()
var pageNumbering: UIPageControl = UIPageControl()
var nextButton = UIButton()
var skipButton = UIButton()
var pageController = PageViewController()
**var content = ContentViewController()**
override func viewDidLoad() {
super.viewDidLoad()
commonInit()
}
func commonInit()
{
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
container.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
container.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height * 0.6).isActive = true
}
#objc func moveToNext() {
// let index = pageController.currentIndex
let index = content.pageIndex
pageNumbering.currentPage = index + 1
print(index)
switch index {
case 0...1:
pageController.forwardPage()
case 2:
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
default:
break
}
}
}
Where the app crashes is strange here in another class at var pagerHeader = UILabel()
import UIKit
class ContentView: UIView {
var pagerImage = UIImageView()
var pagerHeader = UILabel()
var pagerContent = UILabel()
let master = MasterView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit()
{
pagerImage.translatesAutoresizingMaskIntoConstraints = false
addSubview(pagerImage)
}
}
After working with multiverse:
lazy var content = ContentViewController()
Instead of:
var content = ContentViewController()
Has fixed the issue
I've a custom text field in which I'm adding an error label below text field. I want to resize this custom text field so that it expands with multiple line error label and doesn't overlap with other fields below it. In IB I've pinned view properly so that is not an issue.
How to fix it?
class LoginViewController: UIViewController {
#IBOutlet weak var emailTextField: CustomTextField!
#IBOutlet weak var passwordTextField: CustomTextField!
override func viewDidLoad() {
super.viewDidLoad()
emailTextField.setError("Multiple line error. Multiple line error. Multiple line error. Multiple line error.")
}
}
class CustomTextField: UITextField {
var bottomBorder = UIView()
var errorLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.initialize()
// Setup Bottom-Border
// ....
errorLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(errorLabel)
errorLabel.topAnchor.constraint(equalTo: self.bottomBorder.bottomAnchor, constant: 4).isActive = true
errorLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
errorLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
errorLabel.numberOfLines = 0
errorLabel.lineBreakMode = .byWordWrapping
errorLabel.sizeToFit()
}
func initialize() {
self.text = ""
self.clearError()
// ...
}
func setError(error: String) {
self.errorLabel.text = error
self.errorLabel.isHidden = false
self.setNeedsLayout()
self.layoutIfNeeded()
}
func clearError() {
self.errorLabel.text = ""
self.errorLabel.isHidden = true
}
}
UITextField is only 1 line you need to use UITextView or better do
class CustomView: UIView {
let textfield = UITextField()
let bottomBorder = UIView()
let errorLabel = UILabel()
.....
}
So the view will expand according to sum of textfield height , border height and label text height
Simple question, but I don't get the answer of satisfaction.
So, I want to add some constraints to my added UIViewController via code.
Thank you so much :)
override func viewDidLoad() {
let imageLogoName = "pictureIsInAssets"
let imageLogo = UIImage(named: imageLogoName)
let imageLogoView = UIImageView(image: imageLogo!)
setImageContraints()
}
func setImageContraints(){
imageLogoView.translatesAutoresizingMaskIntoConstraints = false
imageLogoView.widthAnchor.constraint(equalToConstant: 180).isActive = true
imageLogoView.heightAnchor.constraint(equalToConstant: 180).isActive = true
imageLogoView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imageLogoView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 28).isActive = true
}
In the func setImageContraints:
Error: Use of unresolved identifier 'imageLogoView'
You are using variable imageLogoView outside of it's visibility/life cycle scope. You should have an instance variable instead:
class SomeViewController {
var imageLogoView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad() // <-- notice this
let imageLogoName = "pictureIsInAssets"
let imageLogo = UIImage(named: imageLogoName)
self.imageLogoView = UIImageView(image: imageLogo!)
setImageContraints()
}
}
or simpler:
class SomeViewController {
let imageLogoView = UIImageView(image: UIImage(imageLiteralResourceName: "pictureIsInAssets"))
override func viewDidLoad() {
super.viewDidLoad() // <-- notice this
setImageContraints()
}
}
And don't forget to add the image view to the view.
imageLogoView is no global variable.
You have to declare it like that:
class ViewController: UIViewController{
let imageLogoView: UIImageView!
override func viewDidLoad() {
let imageLogoName = "pictureIsInAssets"
let imageLogo = UIImage(named: imageLogoName)
imageLogoView = UIImageView(image: imageLogo!)
setImageContraints()
}
}
I have a view controller which has a programmatically created label like below.
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupUI()
}
func setupUI() {
// added an setup the view constraints.
}
}
This works properly. Then I tried to move all the UI element of the view controller to it's extension by creating a extension like below :
private extension MyController {
var label: UILabel = {
**// tried to initialize the label like this. but getting cannot use stored property in extension error**
}()
// then tried like below
var mylabel: UILabel! {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}
func setupUI() {
// with the second option I getting nil value error.
}
}
How can I initialize UI elements in viewcontroller extentions programmatically to access internally.
Try this:
uielements.swift
extension MyController {
static let myLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}()
}
myController.swift
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.addSubview(RootVC.myLabel)
// Add layout constraints here
}
}
As the error says you are not allowed to declare variables in extension.
You mention that you want to initialize the UI element in the
extensions. of course you can do that. But, what you have in the
extension is a declaration not only initialization.
Have the declaration in the controller and the initialization in an extension method.
I want to test the .text of my dashboardLabel, but i don't know how to access it via XCTest.
The DashboardView.swift looks like this:
import UIKit
class DashBoardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
createSubviews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
// MARK: - Create Subviews
func createSubviews() {
backgroundColor = .white
var dashboardLabel : UILabel
dashboardLabel = {
let label = UILabel()
label.text = "Dashboard Label"
label.textColor = .black
label.frame = CGRect(x:60, y:80, width: 200, height: 30)
label.backgroundColor = .green
label.backgroundColor = .lightGray
label.font = UIFont(name: "Avenir-Oblique", size: 13)
label.textAlignment = .center
return label
}()
}
The DashboardViewController.swift looks like this:
import UIKit
class DashBoardViewController: UIViewController {
var dashboardview = DashBoardView()
//MARK: View Cycle
override func loadView() {
view = dashboardview
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
title = "DashBoard"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I know how to test the Title of the DashboardViewController.swift
import XCTest
#testable import DashBoard
class DashBoardTests: XCTestCase {
func test_if_title_is_DashBoard() {
let vc = DashBoardViewController()
let _ = vc.view
XCTAssertEqual(vc.navigationItem.title, "Dashboard")
}
but i have absolutely no clue, how to access the dashboardLabel on the DashBoardView.swift.
I hope this explains my problem and anyone of you can help me, or point me in the right direction!
Thx ✌️
you can do that using accessibilityIdentifier
Refer : iOS XCUITests access element by accesibility
A fellow Software developer told me a solution which is pretty cool! You need to declare the Label as a property as follows
private(set) var dashboardLabel = UILabel()
and now you are able to access the property within the XCTest. Which makes sense, because you can only test things that are accessible from outside
import UIKit
class DashBoardView : UIView {
private(set) var dashboardLabel = UILabel()
}
XCTestFile
import XCTest
#testable import DashBoard
class DashBoardTests: XCTestCase {
func test_if_dashboard_label_has_title_DashBoard() {
let vc = DashBoardView()
XCTAssertEqual(vc.dashboardLabel.text, "DashBoard")
}
}
Hope that helps!