I've seen a lot of APP listing articles in NSCollectionView. When there is no item, there are some hints in the middle of view, such as "no articles", "waiting to add", and so forth.
Do you have any relevant cases to show me how this is accomplished?.
Thank you
Try this code:
func showMessage(_ text: String) {
let messageLabel = UILabel()
messageLabel.text = text
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
self.collectionView.backgroundView = messageLabel
}
func hideMessage() {
self.collectionView.backgroundView = nil
}
Related
Essentially I would like to create a double line large title navigation bar for iOS 14 - I cannot seem to find a conclusive answer anywhere.
The following is what I currently have:
title = "Good Morning \nTim"
for navItem in(self.navigationController?.navigationBar.subviews)! {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.text = self.title
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
}
}
}
The issue with this is does display the second line, so the text is cutting off at Good Morning.
Any suggestions?
Your ViewController needs this modification in viewDidLoad for example:
let label = UILabel()
label.numberOfLines = 2
label.textAlignment = .center
label.text = "Good Morning \nTim"
navigationItem.titleView = label
I have found lots of similar questions about not receiving touch events and I understand that in some cases, writing a custom hitTest function may be required - but I also read that the responder chain will traverse views and viewControllers that are in the hierarchy - and I don't understand why a custom hitTest would be required for my implementation.
I'm looking for an explanation and/or a link to a document that explains how to test the responder chain. This problem is occurring in Xcode 10.2.1.
My scenario (I am not using Storyboard):
I have a mainViewController, that provides a full screen view with an ImageView and a few Labels. I have attached TapGestureRecognizers to the ImageView and one of the labels - and they both work properly.
When I tap the label, I add a child viewController and it's view as a subview to the mainViewController. The view is constrained to cover only the right-half of the screen.
The child viewController contains a vertical stack view that contains 3 arrangedSubviews.
Each arrangedSubview contains a Label and a horizontal StackView.
The horizontal stackView's each contain a View with a Label as a subview.
The Label in the subview sets it's isUserInteractionEnabled flag to True and adds a TapGestureRecognizer.
These are the only objects in the child ViewController that have 'isUserInteractionEnabled' set.
The Label's are nested fairly deep, but since this is otherwise a direct parent/child hierarchy (as opposed to the 2 views belonging to a NavigationController), I would expect the Label's to be in the normal responder chain and function properly. Do the Stack View's change that behavior? Do I need to explicitly set the 'isUserInteractionEnabled' value to False on some of the views? Is there way I can add logging to the ResponderChain so I can see which views it checked and find out where it is being blocked?
After reading this StackOverflow post I tried adding my gesture recognizers in viewDidLayoutSubviews() instead of what's shown below - but they still do not receive tap events.
Thank you in advance to any who can offer advice or help.
Here is the code for the label that is not responding to my tap events and the tap event it should call:
func makeColorItem(colorName:String, bgColor:UIColor, fgColor:UIColor) -> UIView {
let colorNumber:Int = colorLabelDict.count
let colorView:UIView = {
let v = UIView()
v.tag = 700 + colorNumber
v.backgroundColor = .clear
v.contentMode = .center
return v
}()
self.view.addSubview(colorView)
let tapColorGR:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapColor))
let colorChoice: UILabel = {
let l = UILabel()
l.tag = 700 + colorNumber
l.isUserInteractionEnabled = true
l.addGestureRecognizer(tapColorGR)
l.text = colorName
l.textAlignment = .center
l.textColor = fgColor
l.backgroundColor = bgColor
l.font = UIFont.systemFont(ofSize: 24, weight: .bold)
l.layer.borderColor = fgColor.cgColor
l.layer.borderWidth = 1
l.layer.cornerRadius = 20
l.layer.masksToBounds = true
l.adjustsFontSizeToFitWidth = true
l.translatesAutoresizingMaskIntoConstraints = false
l.widthAnchor.constraint(equalToConstant: 100)
return l
}()
colorView.addSubview(colorChoice)
colorChoice.centerXAnchor.constraint(equalTo: colorView.centerXAnchor).isActive = true
colorChoice.centerYAnchor.constraint(equalTo: colorView.centerYAnchor).isActive = true
colorChoice.heightAnchor.constraint(equalToConstant: 50).isActive = true
colorChoice.widthAnchor.constraint(equalToConstant: 100).isActive = true
colorLabelDict[colorNumber] = colorChoice
return colorView
}
#objc func tapColor(sender:UITapGestureRecognizer) {
print("A Color was tapped...with tag:\(sender.view?.tag ?? -1)")
if let cn = sender.view?.tag {
colorNumber = cn
let v = colorLabelDict[cn]
if let l = (v?.subviews.first as? UILabel) {
print("The \(l.text) label was tapped.")
}
}
}
It looks like the main reason you're not getting a tap recognized is because you are adding a UILabel as a subview of a UIView, but you're not giving that UIView any constraints. So the view ends up with a width and height of Zero, and the label exists outside the bounds of the view.
Without seeing all of your code, it doesn't look like you need the extra view holding the label.
Take a look at this... it will add a vertical stack view to the main view - centered X and Y - and add "colorChoice" labels to the stack view:
class TestViewController: UIViewController {
let stack: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.spacing = 4
return v
}()
var colorLabelDict: [Int: UIView] = [:]
override func viewDidLoad() {
super.viewDidLoad()
let v1 = makeColorLabel(colorName: "red", bgColor: .red, fgColor: .white)
let v2 = makeColorLabel(colorName: "green", bgColor: .green, fgColor: .black)
let v3 = makeColorLabel(colorName: "blue", bgColor: .blue, fgColor: .white)
[v1, v2, v3].forEach {
stack.addArrangedSubview($0)
}
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
func makeColorLabel(colorName:String, bgColor:UIColor, fgColor:UIColor) -> UILabel {
let colorNumber:Int = colorLabelDict.count
// create tap gesture recognizer
let tapColorGR:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapColor))
let colorChoice: UILabel = {
let l = UILabel()
l.tag = 700 + colorNumber
l.addGestureRecognizer(tapColorGR)
l.text = colorName
l.textAlignment = .center
l.textColor = fgColor
l.backgroundColor = bgColor
l.font = UIFont.systemFont(ofSize: 24, weight: .bold)
l.layer.borderColor = fgColor.cgColor
l.layer.borderWidth = 1
l.layer.cornerRadius = 20
l.layer.masksToBounds = true
l.adjustsFontSizeToFitWidth = true
l.translatesAutoresizingMaskIntoConstraints = false
// default .isUserInteractionEnabled for UILabel is false, so enable it
l.isUserInteractionEnabled = true
return l
}()
NSLayoutConstraint.activate([
// label height: 50, width: 100
colorChoice.heightAnchor.constraint(equalToConstant: 50),
colorChoice.widthAnchor.constraint(equalToConstant: 100),
])
// assign reference to this label in colorLabelDict dictionary
colorLabelDict[colorNumber] = colorChoice
// return newly created label
return colorChoice
}
#objc func tapColor(sender:UITapGestureRecognizer) {
print("A Color was tapped...with tag:\(sender.view?.tag ?? -1)")
// unwrap the view that was tapped, make sure it's a UILabel
guard let tappedView = sender.view as? UILabel else {
return
}
let cn = tappedView.tag
let colorNumber = cn
print("The \(tappedView.text ?? "No text") label was tapped.")
}
}
Result of running that:
Those are 3 UILabels, and tapping each will trigger the tapColor() func, printing this to the debug console:
A Color was tapped...with tag:700
The red label was tapped.
A Color was tapped...with tag:701
The green label was tapped.
A Color was tapped...with tag:702
The blue label was tapped.
My code below has a button, pressing which should increase the number of credits my user has by 3. I have used get/set property observers for my variable to be saved to userDefaults. While the credits label reflects the change (increases by 3), the number isn't saved to userDefaults, and consequently resets back to the base number when I kill and restart the app.
var livesLeft: Int {
get {
return UserDefaults.standard.integer(forKey: "livesLeftSaved")
}
set {
UserDefaults.standard.set(newValue, forKey: "livesLeftSaved")
}
}
lazy var livesLeftLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
livesLeft = 0
label.text = String(livesLeft)
label.font = UIFont(name: "GillSans-BoldItalic", size: 25)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
let addLivesButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "icon"), for: .normal)
button.addTarget(self, action: #selector(pressedButton), for: .touchUpInside)
return button
}()
#objc func pressedButton() {
livesLeft += 3
//I think the mistake I'm making is in the next line of code//
livesLeftLabel.text = String(livesLeft)
}
While the label gets updated from 0 to 3 on pressing the button, it fails to save the increased number to userDefaults and consequently the user loses his credits once I kill the app.
I know I'm missing out on something very trivial here, I can't figure out what I'm doing wrong
Your issue is here:
lazy var livesLeftLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
livesLeft = 0 *** HERE ***
You are resetting the livesLeft value when you load the label.
Just change this line to
livesLeftLabel.text = String(livesLeft)
I have a [[TextField need max available width----]offset(10.0)][Label]]. I want to setup TextFeild pin to left and shrink all available space without label trimming and setup label to pin right and get minimal fit size.
lazy var textField: UITextField = {
var textField = UITextField()
textField.placeholder = "Placeholder"
textField.delegate = self
textField.borderStyle = UITextField.BorderStyle.none
textField.keyboardType = UIKeyboardType.numberPad
textField.returnKeyType = UIReturnKeyType.done
textField.setContentHuggingPriority(.defaultHigh, for: .horizontal)
return textField
}()
lazy var measureLabel: UILabel = {
var label = UILabel()
label.numberOfLines = 1
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}()
measureLabel.snp.makeConstraints { (make) in
make.right.equalTo(self.snp.right)
make.centerY.equalToSuperview()
}
textField.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalTo(self.measureLabel.snp.left).offset(-10.0)
make.centerY.equalToSuperview()
}
You need
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentCompressionResistancePriority(.required, for: .horizontal)
Also you can completely remove these 2 lines as by default textfield's ContentHuggingPriority && ContentCompressionResistancePriority is lower than the default for label , plus the textfield has no intrinsic size
Implement the a demo below.
label can automatically increase height with attributes below. (swift 5)
label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
It can make the height of super view synchronize at the same time, when you set
the height of super view the same as label.
label = UILabel()
viewContainer.addSubview(label)
label.backgroundColor = UIColor.white
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.text = "hello world, today is a new day. Have a nice day. hello world, today is a new day. Have a nice day. hello world, today is a new day. Have a nice day. hello world, today is a new day. Have a nice day. hello world, today is a new day. Have a nice day. hello world, today is a new day. Have a nice day."
self.addSubview(label)
label.snp.makeConstraints { (make) in
let superView = viewContainer!
make.left.equalTo(superView).offset(10)
make.right.equalTo(superView).offset(-10)
make.centerY.equalTo(superView)
}
viewContainer.snp.makeConstraints { (make) in
make.centerY.equalTo(self)
make.centerX.equalTo(self)
make.left.equalTo(self).offset(10)
make.right.equalTo(self).offset(-10)
make.height.equalTo(label).offset(100)
}
download code: https://github.com/zgpeace/SnapkitDemo/tree/dynamicHeightLabel
Im trying to implement a String list of messages to display on the screen for my login page. I need these messages to be able to scrolled through if the user decides to. I am using a label for my text. If the user does not scroll, then the messages will automatically scroll through. I tried using iCarousel but it does not achieve the effect I want.
You can use Libray https://github.com/cbpowell/MarqueeLabel
or easily you can also make auto scrollable label your own self like
func startAnimation()
{
//Animating the label automatically change as per your requirement
DispatchQueue.main.async(execute: {
UIView.animate(withDuration: 10.0, delay: 1, options: ([.curveLinear, .repeat]), animations: {
() -> Void in
self.demoLabel.center = CGPoint(x: 0 - self.demoLabel.bounds.size.width / 2, y: self.demoLabel.center.y)
}, completion: nil)
})
}
Usage
class ViewController: UIViewController {
let demoLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 14)
label.textColor = .green
label.text = "This is the demo label for testing automatically scrolling of uilabel when user not clicked on label if user click on label the scrolling is stoped."
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(demoLabel)
startAnimation()
}