How to adjust the size of UITextField.leftView? - swift

I am trying to change adjust the size of the a UIImageView that is the leftView of my UITextField without any success. The image is always huge. I would like it to have the same height as my UITextField. Here is what i have tried:
Result:
let topContainer: UIView = {
let view = UIView()
view.layer.borderWidth = 1.0
view.layer.borderColor = UIColor.appGrayExtraLightGray.cgColor
view.layer.cornerRadius = 5
view.clipsToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let emailField: UITextField = {
let view = UITextField()
view.backgroundColor = UIColor.orange
view.text = "myEmailAddress#gmail.com"
view.font = UIFont.systemFont(ofSize: 14)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let s = view.safeAreaLayoutGuide
topContainer.addSubview(emailField)
emailField.topAnchor.constraint(equalTo: topContainer.topAnchor, constant: spacing).isActive = true
emailField.leadingAnchor.constraint(equalTo: topContainer.leadingAnchor, constant: spacing).isActive = true
emailField.trailingAnchor.constraint(equalTo: topContainer.trailingAnchor, constant: -spacing).isActive = true
emailField.bottomAnchor.constraint(equalTo: topContainer.bottomAnchor, constant: -spacing).isActive = true
/**Mail and clear button on eitherisde of textField*/
let mailView = UIImageView.init(frame: CGRect.init(x: 0, y: 0, width: 15, height: 15)) // Chaning the GREct has no effect on the size!!
mailView.image = UIImage.init(named: "mailGrayFilled")
emailField.leftView = mailView
emailField.leftViewMode = .always
emailField.clearButtonMode = .always
view.addSubview(topContainer)
topContainer.topAnchor.constraint(equalTo: s.topAnchor, constant: spacing).isActive = true
topContainer.leadingAnchor.constraint(equalTo: s.leadingAnchor, constant: spacing).isActive = true
topContainer.trailingAnchor.constraint(equalTo: s.trailingAnchor, constant: -spacing).isActive = true

Putting the UIImageView into a Container UIView seems to fix the issue when I test your code. This may also allow you to adjust the padding to suit your needs.
let iconContainer = UIView(frame: CGRect(x: 0, y: 0, width: 25, height: 15))
let mailView = UIImageView(frame: CGRect(x: 0, y: 0, width: 15, height: 15))
mailView.image = UIImage(named: "mailGrayFilled")
mailView.contentMode = .scaleAspectFit
iconContainer.addSubview(mailView)
emailField.leftViewMode = .always
emailField.leftView = iconContainer
emailField.clearButtonMode = .always

Related

How to add an image in navigation bar which is partially outside of navigation bar?

I am trying to achieve below behaviour. I tried with below code
let imageView = UIImageView(image: #imageLiteral(resourceName: "Tab-2"))
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
imageView.translatesAutoresizingMaskIntoConstraints = false
navigationController?.navigationBar.addSubview(imageView)
/*
navigationController?.navigationBar.bringSubviewToFront(imageView)
navigationController?.navigationBar.subviews.forEach { $0.clipsToBounds = false }
navigationController?.navigationBar.layer.zPosition = -1
navigationItem.titleView?.clipsToBounds = false
navigationController?.view.subviews.forEach { $0.clipsToBounds = false }
navigationController?.view.clipsToBounds = false
navigationController?.navigationItem.titleView?.clipsToBounds = false
navigationController?.navigationItem.titleView?.subviews.forEach { $0.clipsToBounds = false }
*/
navigationController?.navigationBar.addConstraints([navigationController!.navigationBar.leftAnchor.constraint(equalTo: imageView.leftAnchor, constant: 0),
navigationController!.navigationBar.rightAnchor.constraint(equalTo: imageView.rightAnchor, constant: 0),
navigationController!.navigationBar.topAnchor.constraint(equalTo: imageView.topAnchor, constant: 5),
navigationController!.navigationBar.heightAnchor.constraint(equalToConstant: 40)])
I tried different ways which is inside asterisk sign.

How to find out which view in a stackview underwent an animation

I have a view that contains a stackView. The view is triggered on long gesture recognizer. I then use hittest to animate the contents of the stackView.
My view with the stackview is defined as follows:
lazy var starContainer : UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 182, height: 36))
view.layer.cornerRadius = 8
view.backgroundColor = .white
view.addSubview(starStack)
starStack.translatesAutoresizingMaskIntoConstraints = false
starStack.topAnchor.constraint(equalTo: view.topAnchor, constant: 1).isActive = true
starStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 1).isActive = true
starStack.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -1).isActive = true
starStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -1).isActive = true
view.layer.cornerRadius = view.frame.height / 2
// Set padding
let padding : CGFloat = 2
starStack.spacing = padding
starStack.layoutMargins = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)
starStack.isLayoutMarginsRelativeArrangement = true
// Generate shadow
view.layer.shadowColor = UIColor(white: 0.4, alpha: 0.4).cgColor
view.layer.shadowRadius = 8
view.layer.shadowOpacity = 0.5
view.layer.shadowOffset = CGSize(width: 0, height: 4)
for index in 0...4 {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
if index == 0 {
imageView.tag = 5009
}
imageView.tag = index
imageView.image = UIImage(named: "icon_starred")
imageView.tintColor = UIColor(hex: Constants.Colors.secondary)
starStack.addArrangedSubview(imageView)
arrayStar.append(imageView)
}
return view
}()
I have then added a long gesture recognizer and whenever it detects a change then I move the views within the stackview individually:
#objc func showStars(gesture : UILongPressGestureRecognizer) {
if gesture.state == .changed {
let pressedLocation = gesture.location(in: self.starContainer)
let fixedYLocation = CGPoint(x: pressedLocation.x, y: starContainer.frame.height / 2)
let hitTestView = starContainer.hitTest(fixedYLocation, with: nil)
if hitTestView is UIImageView {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
let stackView = self.starContainer.subviews.first
stackView?.subviews.forEach({ (imageView) in
imageView.transform = .identity
})
hitTestView?.transform = CGAffineTransform(translationX: 0, y: -50)
})
}
}
My question is how do I get the imageView tag that's been populated in the stackview?
Note: This animation is similar to the emoticons for a Facebook post.
You're thinking about this the wrong way. Your showStars method uses the call starContainer.hitTest() to figure out which view the user long-pressed. At that point, you know which view you are about to animate. Just remember that view.

Reposition in y-direction a custom navigationBar titleView

Using Swift5.1.3, XCode11.3, iOS13.3,
I try to reposition a custom navigationBar titleView.
Creating the custom view and adding it to my navigationBar works fine. (see code below)
Here an example: Please only consider the DarkGray NavigationBar on top with a Name-Label and a yellow round Image. The label and image shall be moved in y-direction!
The example on the left, I have successfully running. The example on the right I try to achieve. But without luck so far.
There is one missing thing I am struggling with since 4 hours.
How do I adjust the y-position (or .topAnchor constant offset) of a custom navigationBar titleView ???
The crash-message says:
'Unable to activate constraint with anchors
<NSLayoutYAxisAnchor:0x6000033ac900 "UIStackView:0x7fdcced39ea0.top"> and
<NSLayoutYAxisAnchor:0x6000033644c0 "UIView:0x7fdcd412ba20.top"> because they
have no common ancestor. Does the constraint or its anchors reference items
in different view hierarchies? That's illegal.'
Here is my code (please note the comment with the many exclamation marks - that is the y-offset trial and crash position of my code):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ...
// set up navigationItem and navigationController look and feeel
navigationController?.set_iOS12_lookAndFeel()
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.largeTitleDisplayMode = .always
// create NavigationBar.titleView StackView (consisting of a label and a button)
let titleStackView = UIStackView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
titleStackView.isUserInteractionEnabled = true
titleStackView.axis = .horizontal
titleStackView.alignment = .center
titleStackView.spacing = 10.0
// stackView label
let labelWidth: CGFloat = UIScreen.main.bounds.width - 16.0 - 10.0 - 36.0 - 16.0 // FullScreenWidth minus (Leading + Spacing + ButtonWidth + Trailing)
let label = UILabel()
label.font = AppConstants.Font.NavBar_TitleFont
label.text = self.profileName
label.textColor = .white
label.tintColor = .white
// position label
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
// stackView button
let buttonWidth: CGFloat = 36.0
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: buttonWidth, height: buttonWidth)))
button.setImage(self.profileImageView.image, for: .normal)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(self.callProfileBtnMethod), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
button.layer.cornerRadius = button.frame.size.width / 2
button.layer.masksToBounds = false
button.clipsToBounds = true
// position button
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
button.heightAnchor.constraint(equalToConstant: buttonWidth).isActive = true
// add label and button to stackView
titleStackView.addArrangedSubview(label)
titleStackView.addArrangedSubview(button)
// position titleStackView
titleStackView.translatesAutoresizingMaskIntoConstraints = false
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Here the code crashes !!!!!!!
titleStackView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100.0).isActive = true
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// position cockpitHeaderView (equal in size and position to titleStackView)
let cockpitHeaderView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
cockpitHeaderView.backgroundColor = .green
cockpitHeaderView.isUserInteractionEnabled = true
cockpitHeaderView.addSubview(titleStackView)
cockpitHeaderView.leadingAnchor.constraint(equalTo: titleStackView.leadingAnchor, constant: 0.0).isActive = true
cockpitHeaderView.topAnchor.constraint(equalTo: titleStackView.topAnchor, constant: 0.0).isActive = true
cockpitHeaderView.trailingAnchor.constraint(equalTo: titleStackView.trailingAnchor, constant: 0.0).isActive = true
cockpitHeaderView.bottomAnchor.constraint(equalTo: titleStackView.bottomAnchor, constant: 0.0).isActive = true
// finally replace NavBar title by custom cockpitHeaderView
self.title = ""
self.navigationItem.titleView = cockpitHeaderView
}
How can I move the titleView correctly ???

UIStackView stretching out sub view aligning it

I'm trying to lay out a StackView programatically, the effect I would like to achieve is
but instead I am getting
I do not understand why the loadingDotView is stretching to fill up all the space?
let loadingDotView: UIView = {
let ldv = UIView()
ldv.backgroundColor = .white
ldv.alpha = 0
ldv.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
ldv.layer.cornerRadius = 10
ldv.layer.masksToBounds = true
ldv.translatesAutoresizingMaskIntoConstraints = false
return ldv
}()
let dotsStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
Setup code...
view.addSubview(dotsStackView)
NSLayoutConstraint.activate([
dotsStackView.heightAnchor.constraint(equalToConstant: 20),
dotsStackView.widthAnchor.constraint(equalToConstant: 100),
dotsStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
dotsStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
dotsStackView.addArrangedSubview(loadingDotView)
dotsStackView.addArrangedSubview(loadingDotView)
dotsStackView.addArrangedSubview(loadingDotView)
This ( closure )
let loadingDotView: UIView = {
let ldv = UIView()
ldv.backgroundColor = .white
ldv.alpha = 0
ldv.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
ldv.layer.cornerRadius = 10
ldv.layer.masksToBounds = true
ldv.translatesAutoresizingMaskIntoConstraints = false
return ldv
}()
returns same object every access so only one appears , make it ( computed property ) to create a new one every access
var loadingDotView: UIView {
let ldv = UIView()
ldv.backgroundColor = .white
ldv.alpha = 0
ldv.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
ldv.layer.cornerRadius = 10
ldv.layer.masksToBounds = true
ldv.translatesAutoresizingMaskIntoConstraints = false
return ldv
}
And add
stackView.spacing = 20
stackView.distribution = .fillEqually

Stackview in navigation bar

Is it possible to place UIStackView in NavigationBar programmaticaly using swift? I want to place there StackView with two arranged stackviews. But when i do that , it shows nothing in navigation bar. If it is possible, please provide example. Thanks
Finally I found solution
let btnSort = UIButton(type: .system)
btnSort.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
btnSort.tintColor = UIColor.white
btnSort.setImage(UIImage(named:"ic_controls_icon.png"), for: .normal)
btnSort.imageEdgeInsets = UIEdgeInsets(top: 6,left: -10,bottom: 6,right: 34)
btnSort.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: 14)
btnSort.setTitle("SORT", for: .normal)
btnSort.layer.borderWidth = 1.0
btnSort.backgroundColor = UIColor.red //--> set the background color and check
btnSort.layer.borderColor = UIColor.white.cgColor
let btnControl = UIButton(type: .system)
btnControl.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
btnControl.tintColor = UIColor.white
btnControl.setImage(UIImage(named:"ic_controls_icon.png"), for: .normal)
btnControl.imageEdgeInsets = UIEdgeInsets(top: 6,left: -10,bottom: 6,right: 34)
btnControl.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: 14)
btnControl.setTitle("SORT", for: .normal)
btnControl.layer.borderWidth = 1.0
btnControl.backgroundColor = UIColor.red //--> set the background color and check
btnControl.layer.borderColor = UIColor.white.cgColor
let view = UIStackView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
view.axis = .horizontal
view.distribution = .fillEqually
view.spacing = 5
view.addArrangedSubview(btnSort)
view.addArrangedSubview(btnControl)
let mainTitleView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
mainTitleView.addSubview(view)
navigationItem.titleView = mainTitleView
You can make any UIView subclass (of which UIStackView is one) the navigation bar's title using your view controller's navigationItem.titleView property.
You can test this out in a playground…
import UIKit
import PlaygroundSupport
let vc = UIViewController()
vc.view.backgroundColor = .white
let nav = UINavigationController(rootViewController: vc)
let topLabel = UILabel()
topLabel.font = UIFont.boldSystemFont(ofSize: 20)
topLabel.text = "Hello"
let bottomLabel = UILabel()
bottomLabel.font = UIFont.systemFont(ofSize: 16)
bottomLabel.text = "World!"
let stackView = UIStackView(arrangedSubviews: [topLabel, bottomLabel])
stackView.axis = .vertical
vc.navigationItem.titleView = stackView
PlaygroundPage.current.liveView = nav.view
PlaygroundPage.current.needsIndefiniteExecution = true
var navTitle: String? = "Preview Checklist"
var navSubTitle: String? = "Edit Checklist >"
lazy var titleStackView: UIStackView = {
let titleLabel = UILabel()
titleLabel.textAlignment = .left
titleLabel.text = navTitle
//titleLabel.font = UIFont(name: "RawlineMedium-Regular", size:CGFloat(15))
titleLabel.textColor = .white
let subtitleLabel = UILabel()
subtitleLabel.textAlignment = .left
subtitleLabel.text = navSubTitle
//subtitleLabel.font = UIFont(name: "RawlineMedium-Regular", size:CGFloat(11))
subtitleLabel.textColor = .white
let stackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel])
stackView.axis = .vertical
stackView.backgroundColor = .blue
return stackView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.titleView = titleStackView
}