Auto resize custom text field for multiple lines label - ios

I've a custom text field in which I'm adding an error label below text field. I want to resize this custom text field so that it expands with multiple line error label and doesn't overlap with other fields below it. In IB I've pinned view properly so that is not an issue.
How to fix it?
class LoginViewController: UIViewController {
#IBOutlet weak var emailTextField: CustomTextField!
#IBOutlet weak var passwordTextField: CustomTextField!
override func viewDidLoad() {
super.viewDidLoad()
emailTextField.setError("Multiple line error. Multiple line error. Multiple line error. Multiple line error.")
}
}
class CustomTextField: UITextField {
var bottomBorder = UIView()
var errorLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.initialize()
// Setup Bottom-Border
// ....
errorLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(errorLabel)
errorLabel.topAnchor.constraint(equalTo: self.bottomBorder.bottomAnchor, constant: 4).isActive = true
errorLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
errorLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
errorLabel.numberOfLines = 0
errorLabel.lineBreakMode = .byWordWrapping
errorLabel.sizeToFit()
}
func initialize() {
self.text = ""
self.clearError()
// ...
}
func setError(error: String) {
self.errorLabel.text = error
self.errorLabel.isHidden = false
self.setNeedsLayout()
self.layoutIfNeeded()
}
func clearError() {
self.errorLabel.text = ""
self.errorLabel.isHidden = true
}
}

UITextField is only 1 line you need to use UITextView or better do
class CustomView: UIView {
let textfield = UITextField()
let bottomBorder = UIView()
let errorLabel = UILabel()
.....
}
So the view will expand according to sum of textfield height , border height and label text height

Related

Tap Gesture not working for custom view in swift

I have a circleButton, and adding a gesture recognizer to it is not working. circleButton is a UIView.
#IBOutlet private weak var circleButton: UIView!
Here is my code:
class CustomBottomBar: UIView {
#IBOutlet private weak var circleButton: UIView!
#IBOutlet private weak var bottomBarView: UIView!
#IBOutlet private weak var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
circleButton.layer.borderColor = UIColor.black.cgColor
circleButton.layer.borderWidth = 2
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
#objc func onAddButtonClicked() {
print("add")
}
private func commonInit() {
if let customBar = Bundle.main.loadNibNamed("CustomBottomBar", owner: self, options: nil)?[0] as? UIView {
customBar.frame = bounds
addSubview(customBar)
circleButton.layer.cornerRadius = circleButton.frame.height/2
circleButton.clipsToBounds = true
circleButton.isUserInteractionEnabled = true
print(self.circleButton.frame.size)
print(self.contentView.frame.size)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onAddButtonClicked))
circleButton.addGestureRecognizer(tapGesture)
}
}
}
The frame size is also coming properly for circleButton. Not sure what is the problem. I am using customBottomBar in a storyboard. Here is the full code :
https://github.com/hirakjyotiborah/AwesomeTaskManag3r
Inside your Main.storyboard give a height constraint to your bottom Bar as you only set leading , trailing and bottom or do this inside commonInit
// 70 (Bottom bar) + 80/2 (half of circle button) = 110
heightAnchor.constraint(equalToConstant: 110).isActive = true
As the base for responding to any click is the frame and the main parent view of the button has zero height

NSLayout Constraint not working on Custom View

I am creating a custom UIView in swift, here is the code for the custom View,
class FormField: UIView {
#IBOutlet var viewLabel : UILabel!
#IBOutlet var textField: UITextField!
#IBOutlet var separator: UIView!
private var verticallyCenteredAnchor : NSLayoutConstraint!
private var topPositionedAnchor : NSLayoutConstraint!
private var isViewHighlighted = false
private var view: UIView?
let nibName = "FormField"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
self.view = view
view.frame = self.bounds
self.addSubview(view)
verticallyCenteredAnchor = viewLabel.centerYAnchor.constraint(equalTo: self.view!.centerYAnchor)
verticallyCenteredAnchor.isActive = false
topPositionedAnchor = viewLabel.topAnchor.constraint(equalTo: self.view!.topAnchor,constant: 0)
topPositionedAnchor.isActive = true
let userNameTap = UITapGestureRecognizer(target: self, action: #selector(animateLabel))
userNameTap.numberOfTapsRequired = 1
view.addGestureRecognizer(userNameTap)
}
#objc func animateLabel() {
if(self.isViewHighlighted) {
verticallyCenteredAnchor.isActive = true
topPositionedAnchor.isActive = false
} else {
verticallyCenteredAnchor.isActive = false
topPositionedAnchor.isActive = true
}
self.isViewHighlighted = !self.isViewHighlighted
UIView.animate(withDuration: 0.5) {
self.view!.layoutIfNeeded()
}
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
I am using a xib file for the view and constraints are set in that. Now when someone taps on the view I am trying to change the constraints with animation. But that's not working, no layout change is happening when the user taps on the view. I can confirm that the animateLabel() method is called when the user taps.
Now if is add in the line,
self.view!.translatesAutoresizingMaskIntoConstraints = false
all constraints are messed up, it's not honouring the constraints I already set in the xib.
What's going wrong here?
I am using a xib file for the view and constraints are set in that.
You have conflicts in your constraints who are in xib + in code , you need to hook the constraints in xib as outlets and play with their .isActive property and completely remove the code constraints

create instance of custom xib class make gesture recognizer don't work

first I create a .xib file and named it RadioBtnView and connect it to a custom UIView class :
class CheckBtnView: UIView {
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var CheckBtn: UIButton!
#IBOutlet weak var checkDescription: UILabel!
var status = false {
didSet {
if status {
CheckBtn.backgroundColor = .orange
} else {
CheckBtn.backgroundColor = .clear
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
private func setupViews(){
Bundle.main.loadNibNamed("CheckBtnView", owner: self, options: nil)
addSubview(mainView)
mainView.frame = self.bounds
mainView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
}
}
then try to add an instance of RadioBtnView programmatically in viewController and add gesture recognizer to it :
let radio = RadioBtnView()
scrollContainerView.addSubview(radio)
radio.translatesAutoresizingMaskIntoConstraints = false
radio.topAnchor.constraint(equalTo: entry.bottomAnchor, constant: 15).isActive = true
radio.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 38).isActive = true
radio.isUserInteractionEnabled = true
let radioGesture = UITapGestureRecognizer(target: self, action: #selector(change(_:)))
radio.addGestureRecognizer(radioGesture)
but the change(_:) function doesn't respond seems like it didn't call
is this for creating the xib view programmatically or something else?
I finally know the reason ... I just delete radio.translatesAutoresizingMaskIntoConstraints = false and add the radio.frame = CGRect(x:50,y:100,width:120,height:20) and every think work
this seems like we can not add xib view programmatically

Custom UIView without Storyboard

Now I'm practicing build IOS app without using storyboard , but I have a problem want to solve , I created a custom UIView called BannerView and added a background(UIView) and a title(UILabel) , and called this BannerView in the MainVC , but run this app , it crashes at the function setupSubviews() and I don't know why.
import UIKit
import SnapKit
class BannerView: UIView {
var background: UIView!
var title: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupSubviews()
}
func setupSubviews() {
background.backgroundColor = .gray
title.text = "INHEART"
self.addSubview(background)
self.addSubview(title)
}
override func layoutSubviews() {
background.snp.makeConstraints { make in
make.width.equalTo(ScreenWidth)
make.height.equalTo(BannerHeight)
make.left.top.right.equalTo(0)
}
title.snp.makeConstraints { make in
make.width.equalTo(100)
make.center.equalTo(background.snp.center)
}
}
}
class MainVC: UIViewController {
var bannerView:BannerView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView = BannerView(frame: CGRect.zero)
view.addSubview(bannerView)
}
}
Your properties do not appear to be initialised
var background: UIView!
var title: UILabel!
You should initialize these in your init method
self.background = UIView()
self.title = UILabel()
If you use force unwrapping on a class property you must initialize in the init method. XCode should be complaining to you about this and your error message should show a similar error
You are not initialised the background view please initialised them
self.background = UIView()
self.title = UILabel()
and if you want to create custom view by use of xib the follow them Custum View
You must have to initialised the self.background = UIView() and self.title = UILabel() first.
You can initalised them in setupSubviews() function before the set/assign values to them.

Using scroll view programmatically

I am trying to develop an app with the UI created only programmatically.
I want to create a simple view which is a UIScrollView (to be able to scroll the view when the keyboard is appearing) with a containerView (UIView) where we can find a button.
I am using PureLayout to make easier the set up of constraints, swift 4, Xcode 9.2 beta
Below the class of this view
class SimpleView: UIScrollView {
var containerView: UIView!
var signInButton: UIButton!
var signInLabel: UILabel!
var screenSize: CGSize = CGSize.zero
var shouldSetupConstraints = true
override init(frame: CGRect) {
super.init(frame: frame)
self.screenSize = frame.size
self.containerView = UIView(frame: CGRect.zero)
self.signInButton = UIButton(frame: CGRect.zero)
self.signInLabel = UILabel(frame: CGRect.zero)
self.addSubview(self.containerView)
self.containerView.addSubview(self.signInButton)
self.signInButton.addSubview(self.signInLabel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
self.layoutSignInButton()
self.layoutSignInLabel()
shouldSetupConstraints = false
}
super.updateConstraints()
}
private func layoutContainerView() {
self.containerView.autoPinEdgesToSuperviewEdges()
self.containerView.backgroundColor = UIColor.yellow
}
private func layoutSignInButton() {
self.signInButton.autoPinEdge(toSuperviewEdge: .right)
self.signInButton.autoPinEdge(toSuperviewEdge: .left)
self.signInButton.autoPinEdge(toSuperviewEdge: .top)
self.signInButton.autoSetDimension(.height, toSize: 55.0)
self.signInButton.backgroundColor = UIColor(hex: "#FD9FA2")
}
private func layoutSignInLabel() {
self.signInLabel.autoPinEdgesToSuperviewEdges()
self.signInLabel.shadowColor = UIColor(hex: "#9A615E")
self.signInLabel.shadowOffset = CGSize(width: 0.0, height: 2)
self.signInLabel.text = NSLocalizedString("SIGN IN", comment: "")
self.signInLabel.textAlignment = .center
self.signInLabel.textColor = UIColor.white
self.signInLabel.font = UIFont.boldSystemFont(ofSize: 15.0)
self.signInLabel.backgroundColor = UIColor.clear
}
}
Below the code of the UIViewController subclass embedding the previous view
class SignInViewController: UIViewController {
var simpleView: SimpleView!
override func viewDidLoad() {
super.viewDidLoad()
self.simpleView = SimpleView(frame: self.view.bounds) // with SimpleView(frame: self.view.frame) has the same behaviour
self.view.addSubview(self.simpleView)
self.simpleView.autoPinEdgesToSuperviewEdges()
self.navigationController?.navigationBar.isHidden = true
}
}
Unfortunatly the result is not the one expected : see below
What am I missing ?
Different points are missing:
- The position of the button is weird (space between the button and the top / left side of the button partly hidden outside of the screen)
- the container view is invisible (backgroundColor = UIColor.yellow has no effect)
Thank you by advance !
//////////////////////////// EDIT ////////////////////////////////
Below a screenshot of the exact same code using a UIView instead of UIScrollView
Class SimpleView: UIView {
enter image description here
The content of a UIScrollView must also define the .contentSize` of the scroll view.
I don't use PureLayout, so I don't know what the syntax is, but in your layoutContainerView() func, you need to also do:
self.containerView ... set width dimension to SuperviewWdith
That will set the content of containerView to the width of the scroll view, and that should fix the width part.
I assume you will be adding elements to containerView and set their constraints to control the height of it.

Resources