I have been trying to programmatically create two separate view controllers in which I have a login, and sign up view controller. While my initial view controller, Login allows me to see the email/password text field, my sign up controller does not. I have plugged in the correct information however when I run my application, it is blank. I will post my code below, can anyone help me solve this issue?
let emailTextField: UITextField = {
let e = UITextField()
let attributedPlaceholder = NSAttributedString(string: "Email", attributes:
[NSAttributedStringKey.foregroundColor : UIColor.white])
e.textColor = .white
e.attributedPlaceholder = attributedPlaceholder
e.setBottomBorder(backGroundColor: GREEN_Theme, borderColor: .white)
return e
}()
let passwordTextField: UITextField = {
let p = UITextField()
let attributedPlaceholder = NSAttributedString(string: "Password", attributes: [NSAttributedStringKey.foregroundColor : UIColor.white])
p.textColor = .white
p.isSecureTextEntry = true
p.attributedPlaceholder = attributedPlaceholder
p.setBottomBorder(backGroundColor: GREEN_Theme, borderColor: .white)
return p
}()
override func viewDidLoad (){
super.viewDidLoad()
view.backgroundColor = GREEN_Theme
func setupFileComponents() {
setupEmailField()
setupPasswordField()
}
func setupEmailField() {
view.addSubview(emailTextField)
emailTextField.anchors(top: nil, topPad: 0, bottom: nil, bottomPad: 0, left: view.leftAnchor, leftPad: 24, right: view.rightAnchor, rightPad: 24, height: 30, width: 0)
emailTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
func setupPasswordField() {
view.addSubview(passwordTextField)
passwordTextField.anchors(top: emailTextField.bottomAnchor, topPad: 8, bottom: nil, bottomPad: 0, left: emailTextField.leftAnchor, leftPad: 0, right: emailTextField.rightAnchor, rightPad: 0, height: 30, width: 0)
}
Thank you in advance.
You should move setupFileComponents, setupEmailField, setupPasswordField out of viewDidLoad then you have to add te calls in viewDidLoad like that:
override func viewDidLoad (){
super.viewDidLoad()
view.backgroundColor = GREEN_Theme
setupFileComponents()
setupEmailField()
setupPasswordField()
}
Related
I just started learning how to code without using storyboards programatically, so please treat me as a beginner... Even though my image shows up on the screen, the label doesn't... I am a little confused as to why this is going on and I appreciate your help. Again, I'm a beginner, do don't mind if its a silly mistake!!!
import UIKit
class LocationRequestController : UIViewController
{
let imageView: UIImageView =
{
let iconImageView = UIImageView()
iconImageView.contentMode = .scaleAspectFit
iconImageView.image = UIImage(named: "blue-pin")
return iconImageView
}()
let allowLocationLabel : UILabel =
{
let label = UILabel()
let attributedText = NSMutableAttributedString(string: "Allow Location\n", attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 24)])
attributedText.append(NSAttributedString(string: "Please enable location services For The Map To Work!", attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]))
label.numberOfLines = 0
label.textAlignment = .center
label.attributedText = attributedText
return label
}()
override func viewDidLoad()
{
super.viewDidLoad()
configureViewAppearance()
}
func configureViewAppearance()
{
view.backgroundColor = .white
view.addSubview(imageView)
imageView.anchor(top: view.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 140, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 200, height: 200)
imageView.centerX(inView: view)
view.addSubview(allowLocationLabel)
allowLocationLabel.anchor(top: imageView.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 32, paddingLeft: 32, paddingBottom: 0, paddingRight: 32, width: 0, height: 0)
allowLocationLabel.centerX(inView: view)
}
}
Thanks again!
You just need to add one line label.translatesAutoresizingMaskIntoConstraints
let allowLocationLabel : UILabel =
{
let label = UILabel()
let attributedText = NSMutableAttributedString(string: "Allow Location\n", attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 24)])
attributedText.append(NSAttributedString(string: "Please enable location services For The Map To Work!", attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]))
label.numberOfLines = 0
label.textAlignment = .center
label.attributedText = attributedText
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
This was the problem:
The text color was automatically set to .white, so that's the reason why it didn't seem to show up! Thanks for your help everyone!
I'm trying to animate a stackview and its elements upon pressing a UIButton. the problem is that the stackview is created inside a function which is called in viewdidload.
Because the stackview is created inside a function I cannot call it inside the function that manages the animation... I think i'm doing something wrong...
my stackview creation function
fileprivate func variableContent() {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
let stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
}
viewdidload
override func viewDidLoad() {
variableContent()
}
You can return the stackview from your function and keep a reference to it as an instance variable. That way you can reuse it later.
class Controller: UIViewController {
var stackView: UIStackView!
fileprivate func variableContent() -> UIStackView {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
let stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
return stackview
}
override func viewDidLoad() {
super.viewDidLoad()
self.stackView = variableContent()
}
}
An other option would be to give it a tag (stackview.tag = 42)
and recover it with self.view.viewWithTag(42)
In addition to Marcio's answer, you can also declare the stackView optionally and set it in the function:
class Controller: UIViewController {
var stackview: UIStackView?
fileprivate func variableContent() {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
self.stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
}
// Whatever your animating function is:
func animateStackView() {
guard let stackview = self.stackview else { return }
// You now have access to your stackView for the rest of this function.
}
}
Note: For consistency with your question, I kept the name of the UIStackView as stackview in the code, but according to Swift naming conventions, you should name variables in camelcase (stackView)
I am trying to retrieve some data that Is stored in firebase, and allow that Information to be displayed inside of a collection view. Such as profileImageUrl, name label and email label.
I have tried to read the documentation on firebase and apple website, but for some reason, my code returns plain. I don't receive any errors, but my code is not running as expected. When I go to my app's profile view, the only texts that are displayed are the placeholder that I programmatically placed.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
if Auth.auth().currentUser != nil {
guard let uid = Auth.auth().currentUser?.uid else {
return }
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else {
return }
let user = CurrentUser(uid: uid, dictionary: dictionary)
self.uidLabel.text = uid
self.nameLabel.text = user.name
self.emailLabel.text = user.email
self.profileImageView.loadImageUsingCacheWithUrlString(user.profileImageUrl)
}, withCancel: { (err) in
print("attempting to load information")
})
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
}
}
I would really appreciate it if someone could help critique what I have, as well as help show me what the correct implementation would look like. Any and all help would be greatly appreciated
There are several things that are not correct.
your cell should not be the one in charge of downloading or doing any network call, leave that to the viewController or even better a separate handler for all the network calls.
Firebase is asynchronous so by the time your data is downloaded all the views have already been set and displayed, that is why you are only seeing your placeholders.
You have a lot of redundant code for checking optionals, this is not much of a problem but it is not needed.
You should really think about changing all this code and separating the different tasks to different handlers. For example, you could create a separate class that controls all the calls to Firebase and returns the values with completion blocks, from the viewController or the collectionView itself ask the network handler to give you the data that you need, pass this data to the cell and let the cell display the data, the cell should only display the data, not download it or ask for networks requests. I cannot write up a complete example for you but I think that if you go around in google you will find a lot of very good examples and tutorials.
Anyway, just to get you started and so you have a look at the mistakes that you have in your code, here is a quick fix for it. Even if it works, don't take for granted that everything is fixed. You should really refactor all the code and separate the different tasks. Hope this helps at least so you can solve the problem of displaying your downloaded data in the cell.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
// Here you add a variable that is observed for SET, once a set happens bind() will be called and the views will be updated.
var currentUser : CurrentUser? {
didSet {
self.bind()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
//...
}
// setupView() should only add the UI appearance, and in this case it is calling firebase and setting currentUser, but it should not do that. This is what you have to refactor.
func setupView() {
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
guard let user = Auth.auth().currentUser else { return }
let uid = user.uid
let reference = Database.database().reference().child("users").child(uid)
reference.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
self.currentUser = CurrentUser(uid: uid, dictionary: dictionary)
}, withCancel: { (err) in
print("attempting to load information")
})
}
// This is the method that updates the cell views.
func bind() {
self.uidLabel.text = currentUser.uid
self.nameLabel.text = currentUser.name
self.emailLabel.text = currentUser.email
self.profileImageView.loadImageUsingCacheWithUrlString(currentUser.profileImageUrl)
}
i've been trying to implementation Material design on iOS, i'm using google material components
class ViewController: UIViewController{
let userNameTextField : MDCTextField = {
let textFieldFloating = MDCTextField()
textFieldFloating.placeholder = "Full Name"
return textFieldFloating
}()
override func viewDidLoad() {
view.addSubview(m_userNameTextField)
m_userNameTextField.acnchor(top: signUpButton.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 20, paddingLeft: 40, paddingBottom: 0, paddingRight: 40, width: 0, height: 200 )
m_userNameTextField.delegate = self as? UITextFieldDelegate
let mUsernameController = MDCTextInputControllerUnderline
mUsernameController = MDCTextInputControllerUnderline(textInput: m_userNameTextField)
super.viewDidLoad()
}
}
the MDCTextInputControllerUnderline get error :
Use of unresolved identifier 'MDCTextInputControllerUnderline'
I want to design a button respecting these rules:
Content horizontally centered
18px margin on both side of the content
10px between image and title
Title will wrap on multiple line if needed
Use attributed title
I have a working solution using:
button.setTitle(title, for: .normal)
Result:
But I can't figure why it's not working if I use:
button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
Result:
It does not respect the 10px and 18px insets. Also note that the problem only occurs when the title becomes multiline.
Here is the playground I used to pinpoint my problem:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
extension UIImage {
class func imageWithView(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
class ActionBarView: UIView {
// MARK: Properties
private var icon: UIImage?
private let stackView = UIStackView()
private let proposeButton = UIButton()
private let declineButton = UIButton()
// MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
designView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Design
private func designView() {
designIcon()
designStackView()
designProposeButton()
designDeclineButton()
}
private func designIcon() {
let image = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
image.backgroundColor = .magenta
icon = UIImage.imageWithView(view: image)
}
private func designStackView() {
stackView.addArrangedSubview(proposeButton)
stackView.addArrangedSubview(declineButton)
stackView.distribution = .fillEqually
stackView.spacing = 1
addSubview(stackView)
stackView.frame = frame
}
private func designProposeButton() {
design(button: proposeButton,
icon: icon,
title: "Propose a task")
}
private func designDeclineButton() {
design(button: declineButton,
icon: icon,
title: "Decline")
}
private func design(button: UIButton, icon: UIImage?, title: String) {
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
button.titleLabel?.backgroundColor = .green
button.imageView?.backgroundColor = .orange
button.backgroundColor = .white
button.setImage(icon, for: .normal)
// button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
button.setTitle(title, for: .normal)
button.titleLabel?.lineBreakMode = .byWordWrapping
}
}
let view = ActionBarView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
PlaygroundPage.current.liveView = view