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()
}
}
Related
I want to perform animation(full view height to 88px height, bottom to top animation) when viewController is loaded. So I have added a UIView(animationView)on the storyboard, added gradient programmatically in viewDidLoad() and performed animation in viewDidAppear() as follows:
override func viewDidLoad() {
super.viewDidLoad()
self.animationView.applyGradient(with: [UIColor(red: 0, green: 91/255, blue: 200/255, alpha: 1), UIColor(red: 0, green: 131/255, blue: 232/255, alpha: 1)])
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//Perform animation
self.animationVWBottomContraint.constant = self.view.bounds.height - 88.0
UIView.animate(withDuration: 0.75,delay: 10, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: {(_) in
DispatchQueue.main.asyncAfter(deadline: .now()+6) {
self.animationView.isHidden = true
}
}
extension UIView {
open func applyGradient(with colours: [UIColor]) {
//Create a gradient and apply it to the sublayer.
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.name = "grad1"
gradient.colors = colours.map { $0.cgColor }
gradient.startPoint = CGPoint(x: 0.0,y: 0.0)
gradient.endPoint = CGPoint(x: 0.0,y: 1.0)
self.layer.insertSublayer(gradient, at: 0)
}
}
If we add one image as subview of animationView to check if animation has effect, then we can see that animation happens. But CAGradient layer does not have any effect.
Instead of gradient if we add backgroundColor, Animation happens as expected.
override func viewDidLoad() {
super.viewDidLoad()
//self.animationView.applyGradient(with: [UIColor(red: 0, green: 91/255, blue: 200/255, alpha: 1), UIColor(red: 0, green: 131/255, blue: 232/255, alpha: 1)])
self.animationView.backgroundColor = UIColor.blue
}
This means the newly added sublayer doesn't get resized on layoutIfNeeded(). Hence I have tried to force invoke layout or creating subclass and adding gradient as part of custom init.
self.animationView.layer.setNeedsLayout()
or
self.animationView.layoutIfNeeded()
or
class GradientView : UIView {
required init?(coder: NSCoder) {
super.init(coder:coder)
self.customInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.customInit()
}
func animate(){
UIView.animate(withDuration: 0.25, delay: 2, options: .curveLinear, animations: {
self.frame.size.height = 80.0
self.layoutIfNeeded()
}, completion: nil)
}
func customInit(){
let containerView = UIView()
self.addSubview(containerView)
containerView.frame = self.bounds
self.autoresizingMask = [.flexibleHeight, .flexibleWidth]
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.name = "grad1"
gradient.colors = [UIColor(red: 0, green: 91/255, blue: 200/255, alpha: 1), UIColor(red: 0, green: 131/255, blue: 232/255, alpha: 1)].map { $0.cgColor }
gradient.startPoint = CGPoint(x: 0.0,y: 0.0)
gradient.endPoint = CGPoint(x: 0.0,y: 1.0)
containerView.layer.insertSublayer(gradient, at: 0)
}
}
But it didn't help.
How can I resolve the issue?
Thanks in advance.
This means the newly added sublayer doesn't get resized on
layoutIfNeeded()
Layers won't resize themselves when the parent View's bound/frame change, For example: if you apply shadow or border to view and then change the frame of the view border/shadow will not update themselves automatically. You have to resize the layer in layoutSubviews so change your code to
class GradientView : UIView {
private var gradient: CAGradientLayer?
override func layoutSubviews() {
super.layoutSubviews()
self.gradient?.frame = self.bounds
}
required init?(coder: NSCoder) {
super.init(coder:coder)
self.customInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.customInit()
}
func animate(){
UIView.animate(withDuration: 0.25, delay: 2, options: .curveLinear, animations: {
self.frame.size.height = 80.0
self.layoutIfNeeded()
}, completion: nil)
}
func customInit(){
let containerView = UIView()
self.addSubview(containerView)
containerView.frame = self.bounds
self.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.gradient = CAGradientLayer()
gradient?.frame = self.bounds
gradient?.name = "grad1"
gradient?.colors = [UIColor(red: 0, green: 91/255, blue: 200/255, alpha: 1), UIColor(red: 0, green: 131/255, blue: 232/255, alpha: 1)].map { $0.cgColor }
gradient?.startPoint = CGPoint(x: 0.0,y: 0.0)
gradient?.endPoint = CGPoint(x: 0.0,y: 1.0)
if let gradient = self.gradient {
containerView.layer.insertSublayer(gradient, at: 0)
}
}
}
Hope this helps
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
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
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()
}
}
}
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
}