Swift change UITextfield subclass value in another viewcontroller - ios

I have a UItextfield subclass called textfieldEdit where I change the border color of the textfield.
In my viewcontroller where I have textfieldEdit attached to a textfield, I want to be able to change the color of the of the textfield if the user didn't enter a value in the textfield. How can I do this?
textfieldEdit.swift :
class textfieldEdit: UITextField, UITextFieldDelegate {
let border = CALayer()
let width = CGFloat(2.0)
required init?(coder aDecoder: (NSCoder!)) {
super.init(coder: aDecoder)
self.delegate=self;
border.borderColor = UIColor(red: 225/255, green: 225/255, blue: 225/255, alpha: 100).cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
#objc(textFieldDidBeginEditing:) func textFieldDidBeginEditing(_ textView: UITextField) {
border.borderColor = UIColor( red: 116/255, green: 193/255, blue: 166/255, alpha: 100 ).cgColor
textColor = UIColor( red: 116/255, green: 193/255, blue: 166/255, alpha: 100 )
}
#objc(textFieldDidEndEditing:) func textFieldDidEndEditing(_ textView: UITextField) {
border.borderColor = UIColor(red: 225/255, green: 225/255, blue: 225/255, alpha: 1).cgColor
}
override func draw(_ rect: CGRect) {
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
}
override func awakeFromNib() {
super.awakeFromNib()
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
}
}
RegisterViewController:
class RegisterVC: UIViewController, UITextFieldDelegate {
// declare UI objects
#IBOutlet weak var usernameTxt: UITextField!
#IBOutlet weak var passwordTxt: textfieldEdit!
#IBOutlet weak var emailTxt: UITextField!
#IBOutlet weak var fullnameTxt: UITextField!
#IBOutlet weak var registerBtn: UIButton!
// first function that is loaded
override func viewDidLoad() {
super.viewDidLoad()
} // end of viewDidLoad function
// function that will be performed when clicking the register button
#IBAction func register_click(_ sender: Any) {
// if registered button is clicked and textfields are empty
if usernameTxt.text!.isEmpty {
// display red placeholders for empty textfields
usernameTxt.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSForegroundColorAttributeName: UIColor.red])
}

I have edit this code .My code will help you.
public func textFieldDidBeginEditing(_ textField: UITextField) // became first responder
{
if (txtCurrent != nil)
{
txtCurrent.layer.borderColor = UIColor(red: 206/255.0, green: 215/255.0, blue: 221/255.0, alpha: 1.000).cgColor
}
txtCurrent = textField
txtCurrent.layer.borderColor = UIColor(red: 67/255.0, green: 153/255.0, blue: 201/255.0, alpha: 1.000).cgColor
}
public func textFieldDidEndEditing(_ textField: UITextField)
{
txtCurrent.layer.borderColor = UIColor(red: 206/255.0, green: 215/255.0, blue: 221/255.0, alpha: 1.000).cgColor
}

Related

Applying Gradient to button doesn't reflect in swift

I have written the following code and don't know what I'm doing wrong the button is not reflecting the gradient
I also tried calling my function under awakefromNib
class ButtonGradient: UIButton {
var color1 = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
var color2 = #colorLiteral(red: 0.521568656, green: 0.1098039225, blue: 0.05098039284, alpha: 1)
var gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
applyGradient()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
applyGradient()
}
func applyGradient() {
gradient.frame = self.bounds
gradient.colors = [color1,color2]
gradient.startPoint = CGPoint.zero
gradient.endPoint = CGPoint.init(x: 0.0, y: 0.5)
gradient.locations = [0,1]
self.layer.addSublayer(gradient)
}
}
I want to apply gradient vertically to my button.
It's pretty simple, here you are
#IBDesignable class GradientButton: UIButton {
#IBInspectable var topColor: UIColor = .white
#IBInspectable var bottomColor: UIColor = .black
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
override func layoutSubviews() {
(layer as? CAGradientLayer)?.colors = [topColor.cgColor, bottomColor.cgColor]
}
}
Just change class from UIButton to GradientButton

How to stop creating a new layer with gradient UICollectionCell ? override func prepareForReuse()

I have a UICollectionViewCell with image and button which have a gradient. When I scroll - I have a bug: The gradient is drawn with a new layer on the image, and the text from the button disappears. This happens when I scroll back. I think I should use override func prepareForReuse(). But I don't know how to do it. Perhaps someone could write code that would fix the bug, because I don't know how to do it.
import UIKit
class TrainingProgramCollectionViewCell: UICollectionViewCell {
private var gradient = CAGradientLayer()
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var buttonOutlet: UIButton!
#IBOutlet weak var featuredImageView: UIImageView!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
var trainingPrograms: TrainingProgram? {
didSet {
self.updateUI()
}
}
private func updateUI()
{
if let trainingProgram = trainingPrograms {
featuredImageView.image = trainingPrograms!.featuredImage
titleLabel.text = trainingPrograms!.title
descriptionLabel.text = trainingPrograms!.description
// bgView.layer.cornerRadius = 10
// buttonOutlet.backgroundColor = UIColor(red: 21/255, green: 126/255, blue: 238/255, alpha: 1)
buttonOutlet.setGradientBackground(colorOne: UIColor(red: 0, green: 0.52, blue: 1, alpha: 1), colorTwo: UIColor(red: 0, green: 0.39, blue: 0.81, alpha: 1), cornerRadius: 25)
buttonOutlet.layer.cornerRadius = 25
buttonOutlet.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4).cgColor
buttonOutlet.layer.shadowOffset = CGSize(width: 0, height: 5)
buttonOutlet.layer.shadowRadius = 15
buttonOutlet.layer.shadowOpacity = 1
bgView!.layer.cornerRadius = 20
featuredImageView.contentMode = .scaleAspectFill
let view = UIView(frame: featuredImageView.frame)
view.clipsToBounds = true
// view.contentMode = .scaleAspectFill
featuredImageView.clipsToBounds = true
let gradient = CAGradientLayer()
gradient.frame = view.frame
gradient.colors = [UIColor(red: 0, green: 0.48, blue: 1, alpha: 0).cgColor,UIColor(red: 0, green: 0.48, blue: 1, alpha: 1).cgColor]
gradient.locations = [0.1, 1.0]
view.layer.insertSublayer(gradient, at: 0)
featuredImageView.addSubview(view)
featuredImageView.bringSubviewToFront(view)
} else {
featuredImageView.image = nil
titleLabel.text = nil
descriptionLabel.text = nil
}
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = 3.0
layer.shadowRadius = 10
layer.shadowOpacity = 0.2
layer.shadowOffset = CGSize(width: 5, height: 10)
self.clipsToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
}
}
Set gradient for Button
extension UIView {
func setGradientBackground(colorOne: UIColor, colorTwo: UIColor, cornerRadius: CGFloat) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.bounds
gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.cornerRadius = cornerRadius
layer.insertSublayer(gradientLayer, at: 0)
}
Try this
var gradLayer: CAGradientLayer! = layer.sublayers?.first(where: { $0.name == "grad_layer" }) as? CAGradientLayer
if gradLayer == nil {
gradLayer = CAGradientLayer()
gradLayer.name = "grad_layer"
layer.addSublayer(gradLayer)
}
// do your changes to grad layer
// edited
gradient.frame = self.bounds

UIButton press wont call function

I am new to swift and have created a viewcontroller and a mainView.swift which holds all the styling and ui elements however the button in the mainView.swift doesn't call the function i have created i should add that all the elements load in fine its just after they load and i tap the login button nothing happens , i did some research into the hierarchy of views in swift however noting helped solve the issue , any help would be appreciated
MainView.swift
import Foundation
import UIKit
import PureLayout
class login: UIView {
var shouldSetupConstraints = true
var logoImageContainer = UIView()
var logoImage = UIImageView(image: UIImage(named: "logo"))
var loginButton = UIButton()
var registerButton = UIButton()
let screenSize = UIScreen.main.bounds
let gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
logoImage.center = CGPoint(x: screenSize.width/2, y:0)
logoImage.alpha = 0.0
self.addSubview(logoImage)
loginButton.frame = CGRect(x: screenSize.width/14, y: screenSize.height/1.35, width: 320, height: 50)
gradient.frame = loginButton.bounds
gradient.colors = [UIColor(red: (0/255.0), green: (114/255.0), blue: (255/255.0), alpha: 1).cgColor , UIColor(red: (0/255.0), green: (198/255.0), blue: (255/255.0), alpha: 1).cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.cornerRadius = 4
loginButton.layer.addSublayer(gradient)
loginButton.setTitle("SIGN IN", for: .normal)
loginButton.titleLabel?.font = UIFont(name: "HelveticaNeue-medium", size: 30)
loginButton.titleLabel?.textAlignment = .center
loginButton.alpha = 0.0
loginButton.addTarget(self, action: #selector(loginPressed), for: .touchUpInside)
self.addSubview(loginButton)
registerButton.frame = CGRect(x: screenSize.width/14, y: screenSize.height/1.175, width: 320, height: 50)
registerButton.setTitle("Create Account", for: .normal)
registerButton.setTitleColor(UIColor.gray, for: UIControlState.normal)
registerButton.titleLabel?.font = UIFont(name: "HelveticaNeue-medium", size: 24)
registerButton.titleLabel?.textAlignment = .center
registerButton.alpha = 0.0
self.addSubview(registerButton)
UIView.animate(withDuration: 1.0, animations: { () -> Void in
self.loginButton.alpha = 1.0
self.registerButton.alpha = 1.0
self.logoImage.alpha = 1.0
self.logoImage.center = CGPoint(x: self.screenSize.width/2, y:self.screenSize.height/2.5)
})
}
#objc func loginPressed() {
print("press")
/* UIView.animate(withDuration: 1.0, animations: { () -> Void in
self.loginButton.center = CGPoint(x: self.screenSize.width/14, y: self.screenSize.height/1.175)
self.registerButton.alpha = 0.0
self.registerButton.center = CGPoint(x: self.screenSize.width/2, y:self.screenSize.height/1)
self.logoImage.center = CGPoint(x: self.screenSize.width/2, y:self.screenSize.height/1.5)
}) */
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
// AutoLayout constraints
shouldSetupConstraints = false
}
super.updateConstraints()
}
}
MainViewController.swift
class MainViewController: UIViewController {
var loginView: login!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(red: (255/255.0), green: (255/255.0), blue: (255/255.0), alpha: 1)
loginView = login()
loginView.isUserInteractionEnabled = true
self.view.addSubview(loginView)
}
}
the problem in your code is your loginPressed method is missing a parameter. When a button is clicked it will call your method and as parameter it will send a button object which is clicked to your method.
But the signature of method being called and your loginPressed method is not matching. It should be like this
#objc func loginPressed(sender: UIButton!) {
print("login Clicked")
}
Since all actions work on the basis of the superView's frame making it like this
loginView = login()
and
loginButton.frame = CGRect(x: screenSize.width/14, y: screenSize.height/1.35, width: 320, height: 50)
where elemnts have there frame values relative to non-zero value because of
let screenSize = UIScreen.main.bounds
screenSize.width/height
despite the superView has a zero frame , will cause all actions to drop

xcode Interface Builder not updating IBDesignable class

In my project, I have several custom class files based on UITextField and UIButton.
As an example, the UITextField class looks like this:
import UIKit
#IBDesignable class RoundedTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
layer.cornerRadius = 25
layer.borderWidth = 2
layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
clipsToBounds = true
textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
// Placeholder colour
attributedPlaceholder = NSAttributedString(string: placeholder!, attributes: [NSAttributedStringKey.foregroundColor: #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)])
}
// Placeholder text indent
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 5)
}
// Editing text indent
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 5)
}
}
Here is the correct version that the simulator shows me:
SimScreenshot
Here is what IB shows me:
IBScreenshot
As you can see from my IB screenshot, the only element that updates is the indentation of the placeholder text. The rest of the customisation from the class is not updating.
Any thoughts?
P.s. when I select an element, in the identity inspector, the 'Designables' field is "Up to date".
I have tried cleaning, building and restarting Xcode.
Thanks!
Scott
I solved the issue with help from Reinier's 2nd link and a course I previously took on Udemy.
I implemented my customisation in a setupView() function. I called this in prepareForInterfaceBuilder() and also awakeFromNib()
import UIKit
#IBDesignable class RoundedTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setupView()
}
// Placeholder text indent
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 5)
}
// Editing text indent
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 20, dy: 5)
}
func setupView() {
layer.cornerRadius = 25
layer.borderWidth = 2
layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
clipsToBounds = true
textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
// Placeholder colour
attributedPlaceholder = NSAttributedString(string: placeholder!, attributes: [NSAttributedStringKey.foregroundColor: #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)])
}
}
//It will update both Simulator and InterfaceBuilder
import UIKit
#IBDesignable class ViewExtended: UIView {
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
func setupView() {
layer.borderColor = borderColor.cgColor
layer.borderWidth = borderWidth
layer.cornerRadius = cornerRadius
layer.shadowOpacity = shadowOpacity
layer.shadowRadius = shadowRadius
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
#IBInspectable var borderColor: UIColor = UIColor.red {
didSet {
layer.borderColor = borderColor.cgColor
setNeedsLayout()
}
}
#IBInspectable var borderWidth: CGFloat = 1.0 {
didSet {
layer.borderWidth = borderWidth
setNeedsLayout()
}
}
#IBInspectable var cornerRadius: CGFloat = 10.0 {
didSet {
layer.cornerRadius = cornerRadius
setNeedsLayout()
}
}
#IBInspectable var shadowColor: UIColor = UIColor.black {
didSet {
layer.borderColor = borderColor.cgColor
setNeedsLayout()
}
}
#IBInspectable var shadowOpacity: Float = 0.7 {
didSet {
layer.shadowOpacity = shadowOpacity
setNeedsLayout()
}
}
#IBInspectable var shadowRadius: CGFloat = 4.0 {
didSet {
layer.shadowRadius = shadowRadius
setNeedsLayout()
}
}
#IBInspectable var shadowOffset: CGSize = CGSize(width: 3, height: 3) {
didSet {
layer.shadowOffset = shadowOffset
setNeedsLayout()
}
}
}

Properly Subclassing UITextField in swift

So i have these textfields that i realised that they all have same properties, so i created new class called "UserInputs" and extended from UITextField, everything works properly except one thing, UITextFieldDelegate functions doesn't work, i mean when i focus on them it doesn't work, i want to add it in code because when you focus on my input fields they change border, how do i properly subclass from UITextField
the only problems that i have are that functions:
textFieldDidBeginEditing
textFieldDidEndEditing
and so doesn't work.
this is my class where everything happens:
import Foundation
import UIKit
class RegistrationViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var firstName: UserInputs!
#IBOutlet weak var test: UserInputs!
override func viewDidLoad() {
super.viewDidLoad()
self.firstName.delegate = self
self.test.delegate = self
}
}
This is my subclass:
class UserInputs: UITextField{
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
createBorder()
}
func createBorder(){
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
border.frame = CGRect(x: 0, y: self.frame.size.height-width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
//print("border created")
}
func textFieldDidBeginEditing() {
print("focused")
self.pulseBorderColor()
}
func textFieldDidEndEditing() {
print("lost focus")
self.reversePulseBorderColor()
}
func pulseBorderColor(){
let pulseAnimation = CABasicAnimation(keyPath: "borderColor")
pulseAnimation.duration = 0.35
pulseAnimation.fromValue = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
pulseAnimation.toValue = UIColor(red: 252/255, green: 180/255, blue: 29/255, alpha: 1.0).CGColor
pulseAnimation.fillMode = kCAFillModeForwards
pulseAnimation.removedOnCompletion = false
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
self.layer.sublayers![0].addAnimation(pulseAnimation,forKey: nil)
}
func reversePulseBorderColor(){
let pulseAnimation = CABasicAnimation(keyPath: "borderColor")
pulseAnimation.duration = 0.35
pulseAnimation.fromValue = UIColor(red: 252/255, green: 180/255, blue: 29/255, alpha: 1.0).CGColor
pulseAnimation.toValue = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
pulseAnimation.fillMode = kCAFillModeForwards
pulseAnimation.removedOnCompletion = false
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
self.layer.sublayers![0].addAnimation(pulseAnimation,forKey: nil)
}
}
this code worked when i had no subclass and was doing it inside my main class, but after creating subclass focus functions stopped working, everything else works
main problem is that i want to implement
func textFieldDidBeginEditing() {
print("focused")
}
func textFieldDidEndEditing() {
print("lost focus")
}
these in my textfields so i don't write it over and over again
It looks like the UITextFieldDelegate functions you have in your code are a little off. They should be:
func textFieldDidBeginEditing(textField: UITextField) {
print("focused")
}
func textFieldDidEndEditing(textField: UITextField) {
print("lost focus")
}
And since you want the UserInputs objects to be their own delegates, I've added that code, too. To demonstrate this, I have the following two files:
ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var textField: UserInputs!
override func viewDidLoad() {
super.viewDidLoad()
textField = UserInputs(frame: CGRectMake(100, 100, 200, 40))
view.addSubview(textField!)
}
}
UserInputs.swift
import UIKit
class UserInputs: UITextField, UITextFieldDelegate {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
delegate = self
createBorder()
}
required override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
createBorder()
}
func createBorder(){
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
border.frame = CGRect(x: 0, y: self.frame.size.height-width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
//print("border created")
}
func textFieldDidBeginEditing(textField: UITextField) {
print("focused")
}
func textFieldDidEndEditing(textField: UITextField) {
print("lost focus")
}
}
Your UITextFieldDelegate should probably stay in your RegistrationViewController.
Instead of overriding the delegate you can do this.
In your init method, add a target to self with the appropriate function.
class UserInputs: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
self.addTarget(self, action: "formatText", for: .editingChanged)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addTarget(self, action: "formatText", for: .editingChanged)
}
func formatText() {
// Edit self.text here
}
//.......//
}
You can change border by calling delegate in UserInput
class UserInputs: UITextField, UITextFieldDelegate{
let border = CALayer()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
createBorder()
self.delegate = self
}
func createBorder(){
let width = CGFloat(2.0)
border.borderColor = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
border.frame = CGRect(x: 0, y: self.frame.size.height-width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
//print("border created")
}
func pulseBorderColor(){
border.borderColor = UIColor.greenColor().CGColor
}
func normalColor(){
border.borderColor = UIColor(red: 55/255, green: 78/255, blue: 95/255, alpha: 1.0).CGColor
}
func textFieldDidBeginEditing(textField: UITextField) {
print("focused")
pulseBorderColor()
}
func textFieldDidEndEditing(textField: UITextField) {
print("lost focus")
normalColor()
}
}

Resources