How to show all TextField in a view that stored in a array / Swift 5 - swift

I have a tapGesture, that every time when you click on it a new TextField displays and will saved in a array.
I can create multiple TextField and can store them in a Array, but I have no idea, how I can display them on a view.
At the Moment only the first one will be shown in the view.
I want to build something similar like snapchat with the text.
var myTextField: UITextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
func addTapGestureToTextImageView() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTextImage))
textImageView.isUserInteractionEnabled = true
textImageView.addGestureRecognizer(tapGesture)
}
#objc func handleTextImage() {
textFields.append(myTextField)
let myTextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
for i in 0..<textFields.count {
myTextField.tag = i
view.addSubview(textFields[i])
print(textFields[i])
}
print(textFields.count)
}

If you want to pile them vertically you have to increment the y with the height for each textField and hence change it's frame.
for i in 0..<textFields.count {
var field = textFields[i]
field.frame = CGRect(x: 0, y: 30*i, width: 300, height: 30)
view.addSubview(field)
print(textFields[i])
}
It will look something like this. But you will need to tweak it to properly align it.
My suggestion would be to put a UIStackView in the baseView and then use it's addArrangedSubview() and it will do the stacking for you.

Use stackView for this:
lazy var textFieldsView: UIStackView = {
let stackView = UIStackView()
stackView.spacing = 4
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.frame.size.width = 300
view.addSubview(stackView)
return stackView
}()
#objc func handleTextImage() {
let myTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300.0, height: 30.0))
for pair in textFields.enumerated() {
pair.element.tag = pair.offset
textFieldsView.addArrangedSubview(pair.element)
}
// Update stackView frame if needed
}

Related

Issue reusing the same UI Image code across multiple view controllers | Swift 5

I have the following function I use to customize the navigation bar across almost all the apps view controllers and table view controllers - instead of replicating the code numerous times I am looking for way to easily call the function on those view controllers needing it.
I have tried wrapping in extension UIViewController { } but run into a selector issue saying the following:
Argument of '#selector' cannot refer to local function
'Tapped(tapGestureRecognizer:)'
Code:
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
print("Profile Tapped")
}
How can this UIImage be seen in the navigation bar across various view controller without needing to rewrite the same code across all.
Lot a way to do it. I'll usually istance and personalize an UIViewController and use it around the whole app.
class baseController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//call your navBar here
navbar()
}
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
}
Now instance this class whenever you want and your controller will get your navBar() every time with this
class mineController:baseController {
//your code here...
}

Add drop shadow to xib view in Swift

I have added a card view from a Xib file which when tapped it slides from bottom-up to display the view.
I have am trying to customised the view and a drop shadow to the view which is not working.
The cornerRadius works fine otherwise.
func setupCard() {
menuCardVC = MenuCardVC(nibName:"MenuCardVC", bundle:nil)
menuCardVC.view.layer.shadowOpacity = 0.50
menuCardVC.view.layer.shadowRadius = 12
menuCardVC.view.layer.shadowColor = UIColor.black.cgColor
menuCardVC.view.layer.shadowOffset = CGSize.zero
menuCardVC.view.layer.cornerRadius = 25
self.addChild(menuCardVC)
self.view.addSubview(menuCardVC.view)
menuCardVC.view.frame = CGRect(x: 0, y: self.view.frame.height - cardHandleAreaHeight, width: self.view.bounds.width, height: cardHeight)
menuCardVC.view.clipsToBounds = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardTap(recognzier:)))
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardPan(recognizer:)))
menuCardVC.handleArea.addGestureRecognizer(tapGestureRecognizer)
menuCardVC.handleArea.addGestureRecognizer(panGestureRecognizer)
}//end setupCard
Remove the below line of code,
menuCardVC.view.clipsToBounds = true
Since your view hierarchy is simple you could use the following technique. Also, you don't need to use clipsToBounds.
let superview = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
superview.backgroundColor = .white
let subview = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
subview.backgroundColor = .red
// First provide the corner radius.
subview.layer.cornerRadius = 8
// Then provide the shadow parameters.
subview.layer.shadowOpacity = 1
// ...
// Do not clip to bounds since it will clip the shadow.
// ...
superview.addSubview(subview)

Views misplaced when setting frames

I am trying to make a view programatically, making a card with an image and some text and overlaying it with some color. I have turned off clip to bounds and added some colors to make it more visual.
So when I set
overlay.frame = self.frame
or
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
the overlay should cover the entire view but it does not, why is this?
This is what is showing
This is what I want to see, but with each card having a blue layer on top
private var leftImage: UIImageView = {
let i = UIImageView(frame: .zero)
i.contentMode = .scaleAspectFill
i.image = UIImage()
return i
}()
private var topLabel: UILabel = {
let label = UILabel(frame: .zero)
label.isUserInteractionEnabled = false
label.font = UIFont.boldSystemFont(ofSize: 16.0)
label.lineBreakMode = .byWordWrapping
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 2
return label
}()
var bottomLabel: UILabel = {
let label = UILabel(frame: .zero)
label.isUserInteractionEnabled = false
label.lineBreakMode = .byWordWrapping
label.font = UIFont.systemFont(ofSize: 12.0)
label.numberOfLines = 1
label.text = "60 seconds"
return label
}()
private var stackView: UIStackView = {
let stack = UIStackView(frame: .zero)
stack.axis = .vertical
stack.isUserInteractionEnabled = false
stack.distribution = .fillEqually
stack.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
stack.isLayoutMarginsRelativeArrangement = true
return stack
}()
private var overlay: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = .blue
view.alpha = 0.8
//view.isHidden = true
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
create()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
create()
}
private func create() {
self.translatesAutoresizingMaskIntoConstraints = false
//leftview
leftImage.frame = CGRect(x: 0, y: 0, width: frame.width / 2, height: frame.height)
addSubview(leftImage)
//rightVIew
stackView.frame = CGRect(x: frame.midX, y: 0, width: frame.width / 2, height: frame.height)
clipsToBounds = false
//add views to stack
stackView.addArrangedSubview(topLabel)
stackView.addArrangedSubview(bottomLabel)
addSubview(stackView)
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(overlay)
//card styling
layer.cornerRadius = 10
}
overlay.frame = self.frame
That can never be right, except by accident, because the frame of overlay and the frame of self are in two different coordinate systems. At the very least you want to say
overlay.frame = self.bounds // bounds, not frame
However, that isn't going to work either unless you say it in the right place. The right place is when layout occurs:
override func layoutSubviews() {
super.layoutSubviews()
self.overlay.frame = self.bounds
}
Simply adding that to your existing code should solve the problem.
The issue is that you're setting your overlay's dimensions like so:
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
before the superview has determined its final width and height. You'll need to do one of the following:
Call create() after the superview class has determined its final size
Continuously set the overlay's frame as the superview's width and height changes (see https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews)
Use AutoLayout constraints via a xib/storyboard file

Remove a nested subview inside custom UITextField when user interaction did end?

This is my first post on StackOverflow! Seems to be an amazing community.
I have created a custom UITextfield with a bottom line (bottomLine) programmatically and also a shadowed line (shadowLine) that I wish should be removed when the user moves away from the textfield. How do I do that?
lazy var textField: UITextField = {
let textField = UITextField()
textField.anchor(width: 150, height: 22)
textField.backgroundColor = UIColor(white: 100, alpha: 1)
var bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: 22, width: 150, height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
textField.borderStyle = UITextField.BorderStyle.none
textField.layer.addSublayer(bottomLine)
let shadowLine = UIView()
shadowLine.frame = CGRect(x: 0, y: 22, width: 150, height: 3)
shadowLine.backgroundColor = UIColor.black
shadowLine.layer.shadowColor = UIColor.darkGray.cgColor
shadowLine.layer.shadowOffset = CGSize(width: 3, height: 3)
shadowLine.layer.shadowRadius = 3
shadowLine.layer.shadowOpacity = 1
shadowLine.tag = 1
textField.addSubview(shadowLine)
return textField
}()
Then I implemented the UITextFieldDelegate protocol and one of its optional functions in order to remove shadowLine subview but it doesn't get removed as I want to:
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == textField {
textField.viewWithTag(1)?.removeFromSuperview()
}
}
Can you help a rookie out? :)
It looks like you forgot to set the delegate:
textField.delagate = self

Swift: SegmentedControl in NavBar with Small TitleView

I am attempting to include a segmentedControl on my navBar that looks like this:
The idea here is that the text "fetching..." is a small titleView. However, my current implementation would result in the text "fetching..." on the lower side like so:
I implement large titles so that I can get two "rows" on the navBar, else the word "fetching..." will be hidden behind the segmentedControl.
Code:
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.backBarButtonItem?.title = "Back"
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = segmentedControl
}
Does anyone have any advice?
You can create a customView that holds all the views you want to show in the navigation bar and set that view as titleView like below,
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
let fetchingLabel: UILabel = {
let label = UILabel(frame: .zero)
label.text = "Fetching..."
return label
}()
In viewDidLoad
let customView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 250))
customView.addSubview(segmentedControl)
customView.addSubview(fetchingLabel)
fetchingLabel.frame = CGRect(x: 150, y: 0, width: self.view.frame.width, height: 60)
segmentedControl.frame = CGRect(x: 60, y: 50, width: self.view.frame.width * 0.75, height: 30)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = customView
This should give you below result. You can play with the values to do what you want.