Trouble With UIViewAnimation - ios

I'm getting in trouble on UIView Animation. When I click into one of UITextFields basically everything works fine until I select another UITextField after being selected the other one, the fields move up and down then return to the same place after I select the other UITextField.
What am I doing wrong? What I expected is to move the whole UIStackView containing my fields into up to avoid the keyboard to cover it all. Also, to keep the animation static when I click into the other UITextField, just returning to the default position when the keyboard got dismissed.
class LoginViewController: UIViewController {
var coordinator: MainCoordinator?
override func viewDidLoad() {
super.viewDidLoad()
viewTapped()
setupScreen()
setupViews()
setConstraints()
}
private func setupScreen() {
self.view.backgroundColor = .systemPink
}
private func setConstraints() {
self.textFieldLogin.heightAnchor.constraint(equalToConstant: 50).isActive = true
self.textFieldLogin.widthAnchor.constraint(equalToConstant: 190).isActive = true
//
self.textFieldSenha.heightAnchor.constraint(equalToConstant: 50).isActive = true
self.textFieldSenha.widthAnchor.constraint(equalToConstant: 190).isActive = true
//
self.loginButton.widthAnchor.constraint(equalToConstant: 145).isActive = true
self.loginButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
//
self.stackView.heightAnchor.constraint(equalToConstant: 200).isActive = true
self.stackView.widthAnchor.constraint(equalToConstant: 200).isActive = true
self.stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
self.stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
private func setupViews() {
self.view.addSubview(self.stackView)
self.viewTapped()
}
private lazy var textFieldLogin: UITextField = {
let textFieldLogin = UITextField()
textFieldLogin.tag = 1
textFieldLogin.translatesAutoresizingMaskIntoConstraints = false
textFieldLogin.layer.cornerRadius = 3.7
textFieldLogin.textAlignment = .center
textFieldLogin.placeholder = "Usuário"
textFieldLogin.backgroundColor = .white
textFieldLogin.delegate = self
return textFieldLogin
}()
private lazy var textFieldSenha: UITextField = {
let textFieldSenha = UITextField()
textFieldSenha.tag = 2
textFieldSenha.translatesAutoresizingMaskIntoConstraints = false
textFieldSenha.layer.cornerRadius = 3.7
textFieldSenha.textAlignment = .center
textFieldSenha.placeholder = "Senha"
textFieldSenha.backgroundColor = .white
textFieldSenha.delegate = self
return textFieldSenha
}()
private lazy var loginButton: UIButton = {
let loginButton = UIButton()
loginButton.translatesAutoresizingMaskIntoConstraints = false
loginButton.layer.cornerRadius = 3.8
loginButton.titleLabel?.font = UIFont(name: "Arial", size: 19)
loginButton.setTitle("Entrar", for: .normal)
loginButton.setTitleColor(.systemGreen, for: .normal)
loginButton.backgroundColor = .white
return loginButton
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [self.textFieldLogin, self.textFieldSenha, self.loginButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.backgroundColor = .systemBlue
stackView.axis = .vertical
stackView.distribution = .equalSpacing
return stackView
}()
private func viewTapped() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
self.view.addGestureRecognizer(tapGesture)
}
#objc func handleTap(sender: UITapGestureRecognizer) {
UIView.animate(withDuration: 1.1, delay: 0, usingSpringWithDamping: 5.1, initialSpringVelocity: 5.0, options: .curveEaseIn, animations: {
self.stackView.frame.origin.y = self.stackView.frame.origin.y + 130
self.textFieldLogin.resignFirstResponder()
self.textFieldSenha.resignFirstResponder()
}, completion: nil)
}
}
extension LoginViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
UIView.animate(withDuration: 1.1, delay: 0, usingSpringWithDamping: 5.1, initialSpringVelocity: 5.0, options: .curveEaseIn, animations: {
self.stackView.frame.origin.y = self.stackView.frame.origin.y - 130
}, completion: nil)
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.animate(withDuration: 1.1, delay: 0, usingSpringWithDamping: 5.1, initialSpringVelocity: 5.0, options: .curveEaseIn, animations: {
self.stackView.frame.origin.y = self.stackView.frame.origin.y + 130
}, completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

I used the following code into textFieldDidBeginEditing and everything works fine! Thank you all.
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded() //Think I forget this method
self.stackView.frame.origin.y = self.stackView.frame.origin.y - 130
}

Related

Why does my UITapGestureRecognizer does not get called when animating the UIView?

I made a toast view with a UIImageView in it, what I want to do is everytime the user taps the image view, the toast dismisses itself. Naturally I configured a UITapGestureRecognizer to my image view, but the selector function is not getting called. Here's what I did:
class ToastView: UIView {
private var icon: UIImageView!
init() {
super.init(frame: .zero)
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
override func layoutSubviews() {
super.layoutSubviews()
setupConstraints()
}
private func setupViews() {
translatesAutoresizingMaskIntoConstraints = false
layer.cornerRadius = 8
isUserInteractionEnabled = true
icon = UIImageView()
icon.translatesAutoresizingMaskIntoConstraints = false
icon.image = UIImage(named: "someImage")
icon.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(iconHandler))
icon.addGestureRecognizer(tapGesture)
addSubview(icon)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: superview!.topAnchor, constant: 55),
leadingAnchor.constraint(equalTo: superview!.leadingAnchor, constant: 16),
trailingAnchor.constraint(equalTo: superview!.trailingAnchor, constant: -16),
icon.heightAnchor.constraint(equalToConstant: 16),
icon.widthAnchor.constraint(equalToConstant: 16),
icon.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
icon.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
#objc private func iconHandler(_ sender: UITapGestureRecognizer) {
// This function is not called
print("handle icon")
}
}
After some research, I tried to give the ToastView a gesture recognizer instead of the image view. So I did give the tap gesture recognizer when showing the toast in my custom UIViewController class like this:
class CustomViewController: UIViewController {
private var isShowingToast: Bool = false
private lazy var toast: ToastView = {
let toast = ToastView()
toast.isUserInteractionEnabled = true
toast.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissToast)))
return toast
}()
func showToastWithMessage() {
if !isShowingToast {
view.addSubview(toast)
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 1
self?.toast.frame.origin.y += 10
self?.isShowingToast = true
}, completion: { _ in
UIView.animate(withDuration: 0.5, delay: 5.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 0
self?.toast.frame.origin.y -= 10
}, completion: { [weak self] _ in
self?.isShowingToast = false
self?.toast.removeFromSuperview()
})
})
}
}
#objc private func dismissToast() {
// This functions does not get called as well
print("dismiss")
}
}
Unfortunately the dismiss function does not print to the console. Is there anyway to resolve this?
Looks like this occurs because of your animation. View is all the time in animation status and block tap gesture. U can try call it with delay instead of adding delay for your animation.
func showToastWithMessage() {
if !isShowingToast {
view.addSubview(toast)
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 1
self?.toast.frame.origin.y += 10
self?.isShowingToast = true
}, completion: { _ in
print("Completion")
})
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 0
self?.toast.frame.origin.y -= 10
}, completion: { [weak self] _ in
self?.isShowingToast = false
self?.toast.removeFromSuperview()
})
}
}
}
This way view going to animate status after 5 sec not with 5 sec delay.
This is how your controller look like:
class CustomViewController: UIViewController {
private var isShowingToast: Bool = false
lazy var toast: ToastView = {
let toast = ToastView()
toast.isUserInteractionEnabled = true
toast.backgroundColor = .red
toast.translatesAutoresizingMaskIntoConstraints = false
toast.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissToast)))
return toast
}()
override func viewDidLoad() {
super.viewDidLoad()
// Add Toast constraints
view.addSubview(toast)
toast.heightAnchor.constraint(equalToConstant: 200).isActive = true
toast.widthAnchor.constraint(equalTo: toast.heightAnchor).isActive = true
toast.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
toast.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
func showToastWithMessage() {
if !isShowingToast {
view.addSubview(toast)
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 1
self?.toast.frame.origin.y += 10
self?.isShowingToast = true
}, completion: { _ in
UIView.animate(withDuration: 0.5, delay: 5.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { [weak self] in
self?.toast.alpha = 0
self?.toast.frame.origin.y -= 10
}, completion: { [weak self] _ in
self?.isShowingToast = false
self?.toast.removeFromSuperview()
})
})
}
}
#objc private func dismissToast() {
// This functions does not get called as well
print("dismiss")
}
}
And this is your class:
class ToastView: UIView {
private var icon = UIImageView()
init() {
super.init(frame: .zero)
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
override func layoutSubviews() {
super.layoutSubviews()
setupConstraints()
}
private func setupViews() {
translatesAutoresizingMaskIntoConstraints = false
layer.cornerRadius = 8
isUserInteractionEnabled = true
icon.translatesAutoresizingMaskIntoConstraints = false
icon.image = UIImage(named: "profilo")?.withRenderingMode(.alwaysOriginal) //put your image here
icon.isUserInteractionEnabled = true
icon.layer.cornerRadius = 8
icon.layer.masksToBounds = true //set image masked round corner
icon.clipsToBounds = true
icon.contentMode = .scaleAspectFill //set image content mode
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(iconHandler))
icon.addGestureRecognizer(tapGesture)
}
private func setupConstraints() {
// Setup the constraints for the subviews
addSubview(icon)
icon.topAnchor.constraint(equalTo: topAnchor).isActive = true
icon.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
icon.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
icon.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
#objc private func iconHandler(_ sender: UITapGestureRecognizer) {
// This function is not called
print("handle icon")
}
}
this is the result:

UIView is not visible when inside subview

Inside my project I have Custom-DropDownView. The problem is the actual DropView is not appearing when the DropDownButton is being pressed if the DropDownButton is inside a Subview.
My structure: ViewController -> UIView -> UIStackView -> arrangedSubview ->DropdownButton
If adding the DropDownButton inside the ViewController it works just fine. However if I add it inside the StackView (where I actually need it to be) it won't show the dropView that should appear when the user taps the DropDownButton.
DropDownButton:
class DropDownBtn: UIButton, DropDownProtocol {
let label: UILabel = {
let v = UILabel()
v.font = UIFont(name: "AvenirNext-Regular", size: 15)
v.textColor = .white
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let listImage: UIImageView = {
let v = UIImageView()
v.backgroundColor = .clear
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
func dropDownPressed(string: String, image: UIImage) {
self.setTitle("", for: .normal)
self.label.text = string
self.listImage.image = image
self.dismissDropDown()
}
var dropView = DropDownView()
var height = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
self.layer.borderColor = UIColor.white.cgColor
self.layer.borderWidth = 1.0
self.layer.cornerRadius = 3
// add image
self.addSubview(listImage)
self.listImage.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10).isActive = true
self.listImage.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
self.listImage.widthAnchor.constraint(equalToConstant: 22).isActive = true
self.listImage.heightAnchor.constraint(equalToConstant: 22).isActive = true
// add label
self.addSubview(label)
self.label.leadingAnchor.constraint(equalTo: self.listImage.leadingAnchor, constant: 32).isActive = true
self.label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
dropView = DropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.layer.borderColor = UIColor.white.cgColor
dropView.layer.borderWidth = 1.0
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubviewToFront(dropView)
dropView.bottomAnchor.constraint(equalTo: self.topAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
override func removeFromSuperview() {
dropView.bottomAnchor.constraint(equalTo: self.topAnchor).isActive = false
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = false
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = false
}
var isOpen = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 150
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y -= self.dropView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y += self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseIn, animations: {
self.dropView.center.y += self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
Usage at the moment:
Inside UIView:
let dropDownButton: DropDownBtn = {
let v = DropDownBtn()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
and I add it like this (+ using autolayout, the button is displayed perfectly fine and not behind any other view):
theStackView.addArrangedSubview(self.itemView)
itemView.addSubview(dropDownButton)
I tried calling self.view.bringSubViewToFront(self.view.theUIView.dropDownButton.dropView) but that didn't do anything. actual DropDownButton seems alright. I checked it in the View-Hierarchy and I also tested that touchesBegan is actually called and it is.
So I guess the issue is how i constrain the dropView but I am completely stuck here. If anyone can help me out here I am very grateful!
If anything is unclear or you need more code, just let me know!
Update:
The reason the tableView was not visible is because I forgot to call reloadData. However that does not fix the main problem !
The tableView is not clickable when adding it inside the arrangedSubview. Only if I add it to the UIView directly it becomes clickable. In both times the view-hierarchy looks the same.. It is always at the front.
If you want to have a look yourself just let me know and I give you the link to the project.

Fold and unfold animation of UIImageView using constraints

As you can see in my code bellow, I'm trying to make a fold/unfold animation using constraints. Certainly the gray background has the fold/unfold animation but the image itself doesn't.
How can I get same fold/unfold effect of the image itself?
class ViewController2: UIViewController {
var folded: Bool = false
var imagen: UIImageView!
private var foldConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let imagen = UIImageView(contentMode: .scaleAspectFill, image: #imageLiteral(resourceName: "gpointbutton"))
imagen.translatesAutoresizingMaskIntoConstraints = false
imagen.backgroundColor = .gray
view.addSubview(imagen)
self.imagen = imagen
imagen.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imagen.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 0)
createAnimationButton()
}
private func createAnimationButton() {
let button = UIButton(title: "Animate", titleColor: .blue)
button.translatesAutoresizingMaskIntoConstraints = false
button.addAction(for: .touchUpInside) { [weak self] (_) in
guard let self = self else { return }
self.folded = !self.folded
if self.folded {
self.foldConstraint.isActive = true
UIView.animate(withDuration: 0.5) {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}
} else {
self.foldConstraint.isActive = false
UIView.animate(withDuration: 0.5) {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}
}
}
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
One thing to note here is that the width or height constraint is set to 0 (accurately also includes 0.1), and the same is hidden.
Then you need to set the height constraint to be greater than 0.1
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 0)
Replace with this, temporarily set to 1
foldConstraint = imagen.heightAnchor.constraint(equalToConstant: 1)
Hide it at the end of the animation
self.folded = !self.folded
if self.folded {
self.foldConstraint.isActive = true
UIView.animate(withDuration: 1, animations: {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
}) { (completion) in
self.imagen.isHidden = true
}
} else {
self.imagen.isHidden = false
self.foldConstraint.isActive = false
UIView.animate(withDuration: 1, animations: {
self.imagen.setNeedsLayout()
self.imagen.superview?.layoutIfNeeded()
})
}
Update:
scaleAspectFill is not suitable for animation, it should be set to scaleAspectFit
let imagen = UIImageView(contentMode: .scaleAspectFit, image: #imageLiteral(resourceName: "gpointbutton"))

I want customised UITextfield which has placeholder behaviour same as android also i want error label within UITextfield

I want customized UITextfield which has placeholder behavior same as android also I want error label within UITextfield.
Recently I have seen in android. They have default text area where placeholder does not get hidden when a user starts writing, Instead of hiding they keep placeholder upside of textarea.
Recently i have created my CustomTextfield class which is same as your requirements.
Here is the Github link to download class and its usage https://github.com/indrajitv/CustomTextField
Below is the code,
//
// File.swift
// customTextField
//
// Created by indrajit on 17/08/18.
// Copyright © 2018 indrajit. All rights reserved.
//
import UIKit
#IBDesignable
open class CustomTextField:UITextField{
private var labelPlaceholderTitleTop:NSLayoutConstraint!
private var labelPlaceholderTitleCenterY:NSLayoutConstraint!
private var labelPlaceholderTitleLeft:NSLayoutConstraint!
#IBInspectable var allowToShrinkPlaceholderSizeOnEditing = true
#IBInspectable var shrinkSizeOfPlaceholder:CGFloat = 0
#IBInspectable var placeHolderColor:UIColor = .lightGray{
didSet{
labelPlaceholderTitle.textColor = placeHolderColor
}
}
open override var font: UIFont?{
didSet{
labelPlaceholderTitle.font = font
}
}
#IBInspectable var heightOfBottomLine:CGFloat = 1{
didSet{
heightAnchorOfBottomLine.constant = heightOfBottomLine
}
}
open override var leftView: UIView?{
didSet{
if let lv = leftView{
labelPlaceholderTitleLeft.constant = lv.frame.width+leftPadding
}
}
}
#IBInspectable var leftPadding:CGFloat = 0{
didSet{
labelPlaceholderTitleLeft.constant = leftPadding
}
}
#IBInspectable var errorText:String = ""{
didSet{
self.labelError.text = errorText
}
}
#IBInspectable var errorColor:UIColor = .red{
didSet{
labelError.textColor = errorColor
}
}
#IBInspectable var errorFont:UIFont = UIFont.systemFont(ofSize: 10){
didSet{
self.labelError.font = errorFont
}
}
#IBInspectable var shakeIntensity:CGFloat = 5
private var heightAnchorOfBottomLine:NSLayoutConstraint!
lazy var labelPlaceholderTitle:UILabel={
let label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.font = self.font
label.adjustsFontSizeToFitWidth = true
return label
}()
lazy var labelError:UILabel={
let label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.adjustsFontSizeToFitWidth = true
label.text = self.errorText
label.textAlignment = .right
label.font = self.errorFont
label.textColor = errorColor
return label
}()
let bottonLineView:UIView={
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.initalSetup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initalSetup()
}
override open func prepareForInterfaceBuilder() {
self.initalSetup()
}
open override func awakeFromNib() {
self.labelError.isHidden = true
}
func initalSetup(){
self.labelPlaceholderTitle.text = placeholder
placeholder = nil
borderStyle = .none
bottonLineView.removeFromSuperview()
addSubview(bottonLineView)
bottonLineView.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true
bottonLineView.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true
bottonLineView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
heightAnchorOfBottomLine = bottonLineView.heightAnchor.constraint(equalToConstant: heightOfBottomLine)
heightAnchorOfBottomLine.isActive = true
addSubview(labelPlaceholderTitle)
labelPlaceholderTitleLeft = labelPlaceholderTitle.leftAnchor.constraint(equalTo: leftAnchor, constant: leftPadding)
labelPlaceholderTitleLeft.isActive = true
labelPlaceholderTitle.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true
labelPlaceholderTitleTop = labelPlaceholderTitle.topAnchor.constraint(equalTo: topAnchor, constant: 0)
labelPlaceholderTitleTop.isActive = false
labelPlaceholderTitleCenterY = labelPlaceholderTitle.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0)
labelPlaceholderTitleCenterY.isActive = true
addSubview(labelError)
labelError.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive = true
labelError.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive = true
labelError.topAnchor.constraint(equalTo: bottonLineView.bottomAnchor, constant: 2).isActive = true
addTarget(self, action: #selector(self.textFieldDidChange), for: .editingChanged)
}
#objc func textFieldDidChange(){
func animateLabel(){
UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
}
if let enteredText = text,enteredText != ""{
if labelPlaceholderTitleCenterY.isActive{
labelPlaceholderTitleCenterY.isActive = false
labelPlaceholderTitleTop.isActive = true
labelPlaceholderTitleTop.constant = -5
if allowToShrinkPlaceholderSizeOnEditing{
let currentFont = font == nil ? UIFont.systemFont(ofSize: 16) : font!
let shrinkSize = shrinkSizeOfPlaceholder == 0 ? currentFont.pointSize-3 : shrinkSizeOfPlaceholder
labelPlaceholderTitle.font = UIFont.init(descriptor: currentFont.fontDescriptor, size:shrinkSize)
}
animateLabel()
}
}else{
labelPlaceholderTitleCenterY.isActive = true
labelPlaceholderTitleTop.isActive = false
labelPlaceholderTitleTop.constant = 0
labelPlaceholderTitle.font = font
animateLabel()
}
}
#objc public func showError(){
self.labelError.isHidden = false
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: center.x - shakeIntensity, y: center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: center.x + shakeIntensity, y: center.y))
layer.add(animation, forKey: "position")
}
#objc public func hideError(){
self.labelError.isHidden = true
}
}
**Usage :
//
// ViewController.swift
// customTextField
//
// Created by indrajit on 16/08/18.
// Copyright © 2018 indrajit. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var text: CustomTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func okClicked(_ sender: UIButton) {
if text.text! == ""{
text.showError()
}else{
text.hideError()
}
}
}
Output:

How to sort UIView when keyboard appears?

I can't find a good solution of how to move UIView up when the keyboard appears. My solution now is this:
import UIKit
class LoginViewControllerbybys: UIViewController, UITextFieldDelegate {
// Create username and password text boxes
let emailTextField: UITextField = {
let field = UITextField()
field.placeholder = "EMAIL"
field.font = UIFont(name: "Helvetica Neue", size: 14)
field.background = UIImage(named: "divider")
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.spellCheckingType = .no
field.translatesAutoresizingMaskIntoConstraints = false
return field
}()
let passwordTextField: UITextField = {
let field = UITextField()
field.placeholder = "PASSWORD"
field.font = UIFont(name: "Helvetica Neue", size: 14)
field.background = UIImage(named: "divider")
field.autocapitalizationType = .none
field.autocorrectionType = .no
field.spellCheckingType = .no
field.isSecureTextEntry = true
field.translatesAutoresizingMaskIntoConstraints = false
return field
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white // sets background colour to white
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
// Adding Logo in the centre-top of the screen
let logo: UIImageView = {
let image = UIImage(named: "logo")
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
func logoView() {
logo.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
logo.heightAnchor.constraint(equalToConstant: 75).isActive = true
logo.widthAnchor.constraint(equalToConstant: 75).isActive = true
logo.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
}
func emailTextFieldView() {
emailTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
emailTextField.heightAnchor.constraint(equalToConstant: 35).isActive = true
emailTextField.widthAnchor.constraint(equalToConstant: 310).isActive = true
emailTextField.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 50).isActive = true
}
func passwordTextFieldView() {
passwordTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
passwordTextField.heightAnchor.constraint(equalToConstant: 35).isActive = true
passwordTextField.widthAnchor.constraint(equalToConstant: 310).isActive = true
passwordTextField.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 30).isActive = true
}
// Create Sign In button
let signInButton: UIButton = {
let button = UIButton()
button.setImage(#imageLiteral(resourceName: "goButton"), for: .normal)
button.frame = CGRect(x: 0, y: 597, width: 375, height: 70)
button.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside) // when tapped on Sign In button, execute function SignInTapped
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
func signInButtonView() {
signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
signInButton.heightAnchor.constraint(equalToConstant: 70).isActive = true
signInButton.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
signInButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
// Show all Subviews
self.view.addSubview(logo)
self.view.addSubview(signInButton)
self.view.addSubview(emailTextField)
self.view.addSubview(passwordTextField)
// Constraints
logoView()
signInButtonView()
emailTextFieldView()
passwordTextFieldView()
}
func keyboardWillShow(notification: NSNotification) {
let keyboardSize: CGSize = ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size)!
let offset: CGSize = ((notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size)!
if keyboardSize.height == offset.height {
if self.view.frame.origin.y == 0 {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
}
} else {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height - offset.height
})
}
}
func keyboardWillHide(notification: NSNotification) {
let keyboardSize: CGSize = ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size)!
self.view.frame.origin.y += keyboardSize.height
}
}
However, the problem with this is that it hides some of my boxes and a label and I can't see them. I thik I would be able to avoid it with UIScrollView but I have no idea how to do it... Would appreciate any help!
You can use UITableView, then you can focus on the selected row using:
scrollToRow(at:at:animated:)
https://developer.apple.com/reference/uikit/uitableview/1614997-scrolltorow

Resources