unable to find view with tag - swift

In my viewWillAppear() I create a label and give it a tag. When another condition is met, I try to remove the label, but for some reason, that is not working and the label is still in the view. I must be doing something wrong...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var label: UILabel?
// Add label if there are no recipes
if (recipeBook.recipesArr.count == 0) {
label = self.view.viewWithTag(123) as? UILabel
//label?.tag = 123 // arbitrary num
label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height/3, width: self.view.frame.width, height: 100))
label?.text = "Add A Recipe"
label?.textColor = UIColor(red:0.93, green:0.92, blue:0.92, alpha:1.0)
label?.font = label?.font.withSize(36)
label?.textAlignment = .center
self.view.addSubview(label!)
}
else {
// remove it
if let foundLabel = self.view.viewWithTag(123) {
foundLabel.removeFromSuperview()
} else {
print("Couldn't find label with tag in view")
}
}
}
I didn't realize in this line label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height/3, width: self.view.frame.width, height: 100)) I was creating a new label which has a default tag of 0. Changed it to label?.frame = CGRect(x: 0, y: self.view.frame.height/3, width: self.view.frame.width, height: 100) so that I'm not creating a new label and everything is working fine. Silly mistake.

Your code is not doing what you think it is. If your recipesArr is empty (or more accurately the count is zero) you are trying to find a label/view with the tag 123. That is then ignored and you create a new label but don't give it a tag.
What you need to do is assign the label you create the tag 123 after you create it like this:
label?.tag = 123
Then you will have created the label and set it's tag so it can then subsequently be found.

you are recreating the label after the line label = UILabel(frame: CGRect
you can create the label programmatically like so without optionals:
lazy var recipeLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height/3, width: self.view.frame.width, height: 100))
label.tag = 123
label.text = "Add A Recipe"
label.font = UIFont.systemFont(ofSize: 17.0)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
then add it to your subview with iOS 9+ constraints if needed:
self.view.addSubview(recipeLabel)
then you can access the view anywhere by simply referencing the view recipeLabel or by the .tag property if you desire.

Related

Simple function for a customised label

I'm trying to write a most basic function that will set the parameters of a label.
First I set the position where I want to put a label:
var xCircularPos = UIScrollView.frame.width / 2
var yCircularPos = UIScrollView.frame.height * 0.15
And than write a function:
func textParameters(labelName: UILabel, text: String, xPosition: CGFloat, yPosition: CGFloat) {
labelName.textAlignment = .center
labelName.text = text
labelName.sizeToFit()
labelName.layer.position.x = CGFloat(0)
labelName.layer.position.y = CGFloat(0)
UIScrollView.addSubview(labelName)
}
Than I create a label and use a function:
let scoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 21))
textParameters(labelName: scoreLabel, text: "Счет", xPosition: xCIrcularPos, yPosition: yCIrcularPos)
But nothing happens. When I set the same parameters for the label without a function - everything works fine. In both cases everything happens in viewDidLoad.
Can you please give a tip what I am missing?

How do I Fade Label out at end instead of replacing end with "..." if it's too long / How to use GoogleToolboxForMac

I've found a solution with GTMFadeTruncatingLabelTest from GoogleToolboxForMac but don't really understand how to use it and I don't find anything about it
but if you have another solution besides of this one
If you can't help me with GoogleToolboxForMac feel free to suggest other solution
Should kinda look like this at the end:
I am not sure about the GTMFadeTruncatingLabelTest but I can offer an alternative solution.
Steps
Check if the label's text is going to be truncated
If 1 is true, Create a CAGradientLayer that goes from Opaque to Transparent
Apply the gradient layer as a mask to the UILabel
Implementation
If you don't want to read the rest, just grab the code from this repo
I wrapped step 1, 2 and 3 from above in a custom UILabel subclass. The reasoning is explained in the comments.
class FadingLabel: UILabel
{
// Add a property observer for text changes
// as we might not need to fade anymore
override var text: String?
{
didSet
{
// Check if the text needs to be faded
fadeTailIfRequired()
}
}
// Add a property observer for numberOfLines changes
// as only 1 line labels are supported for now
override var numberOfLines: Int
{
didSet
{
// Reset the number of lines to 1
if numberOfLines != 1
{
numberOfLines = 1
}
}
}
override func layoutSubviews()
{
super.layoutSubviews()
// The label's frame might have changed so check
// if the text needs to be faded or not
fadeTailIfRequired()
}
/// The function that handles fading the tail end of the text if the text goes
/// beyond the bounds of the label's width
private func fadeTailIfRequired()
{
// Reset the numberOfLines to 1
numberOfLines = 1
// Prevent processing fading when the library is in the middle of
// processing the string to truncate the ellipsis
if !isTruncatingEllipsis
{
// Check if the label's has it's width set and if the text goes
// beyond it's width plus a margin of safety
if bounds.width > CGFloat.zero && intrinsicContentSize.width > bounds.width + 5
{
// Fade label works better with this setting
allowsDefaultTighteningForTruncation = true
// Initialize and configure a gradient to start at the end of
// the label
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
gradient.startPoint = CGPoint(x: 0.8, y: 0.5)
gradient.endPoint = CGPoint(x: 0.99, y: 0.5)
// Apply the gradient as a mask to the UILabel
layer.mask = gradient
// Remove ellipsis added as the default UILabel truncation character
removeEllipsis()
// We do not need to go beyond this point
return
}
// If the text has not been truncated, remove the gradient mask
if originalText == text
{
// Remove the layer mask
layer.mask = nil
}
}
}
/// Keep removing 1 character from the label till it no longer needs to truncate
private func removeEllipsis()
{
isTruncatingEllipsis = true
// Keep looping till we do not have the perfect string length
// to fit into the label
while intrinsicContentSize.width > bounds.width
{
// Drop the last character
text = String(text!.dropLast())
}
isTruncatingEllipsis = false
}
}
Then you can use it like a regular UILabel, for example:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let fadingLabelWithLongText = FadingLabel()
view.addSubview(fadingLabelWithLongText)
fadingLabelWithLongText.text = "Fading label with text more than it's bounds can handle"
fadingLabelWithLongText.textColor = .white
fadingLabelWithLongText.frame = CGRect(x: 20, y: 90, width: 250, height: 50)
let regularLabelWithLongText = UILabel()
view.addSubview(regularLabelWithLongText)
regularLabelWithLongText.text = "Regular label with text more than it's bounds can handle"
regularLabelWithLongText.textColor = .white
regularLabelWithLongText.frame = CGRect(x: 20, y: 160, width: 250, height: 50)
let fadingLabelWithShortText = UILabel()
view.addSubview(fadingLabelWithShortText)
fadingLabelWithShortText.text = "Fading label with text that fits"
fadingLabelWithShortText.textColor = .white
fadingLabelWithShortText.frame = CGRect(x: 20, y: 230, width: 250, height: 50)
let regularLabelWithShortText = UILabel()
view.addSubview(regularLabelWithShortText)
regularLabelWithShortText.text = "Regular label with text that fits"
regularLabelWithShortText.textColor = .white
regularLabelWithShortText.frame = CGRect(x: 20, y: 300, width: 250, height: 50)
}
Output
Limitation
This way only supports single line UILabels
Update
Added a function to remove the default truncation method of using ellipsis (three dots) by UILabel with this function.
/// Keep removing 1 character from the label till it no longer needs to truncate
private func removeEllipsis()
{
isTruncatingEllipsis = true
// Keep looping till we do not have the perfect string length
// to fit into the label
while intrinsicContentSize.width > bounds.width
{
// Drop the last character
text = String(text!.dropLast())
}
isTruncatingEllipsis = false
}
This function has been updated in the original code and repo mentioned above.
I Think the easiest way is to use the following code:
titleLabel.adjustsFontSizeToFitWidth = false
titleLabel.lineBreakMode = .byClipping
It automatically fades the last words if applicable and text size has more width than UILabel!
thanks to thi

How can I insert image to end of the string and insert more than one image in UITextView with swift?

I created an UITextView and I can add image with image picker into text view. But I have some problem about replacement image. I want add this image end of the text. And I want to add images more than one. (like: text + image + text...). How can I solve this problem ? Can anyone help me ?
let pickImage = UIImageView() // this is for imagepickercontroller
lazy var writePost: UITextView = {
let wpost = UITextView()
let images = pickImage
let attachment = NSTextAttachment()
let attString = NSAttributedString(attachment: attachment)
attachment.image = images.image
images.frame = CGRect(x: 0, y: 0, width: 220, height: 220)
images.contentMode = .scaleAspectFill
wpost.textStorage.insert(attString, at: wpost.selectedRange.location)
wpost.addSubview(images)
wpost.textAlignment = .center
wpost.textColor = UIColor.lightGray
wpost.font = UIFont(name: "AvenirNext-DemiBoldItalic", size: 16)
wpost.isEditable = true
wpost.isScrollEnabled = true
wpost.layer.borderWidth = 1.5
wpost.layer.cornerRadius = 7.0
wpost.layer.borderColor = UIColor.orange.cgColor
wpost.delegate = self
return wpost
}()
What you should do is use UITextView's textContainer's exclusionPaths property. The exclusionPaths property lets you assign an array of UIBezierPaths to your textContainer. When exclusionPaths are set, none of the UITextView's text will appear within these paths. You could then add a UIImageView as a subview of the UITextView's super view placed above the UITextView that has a frame equal to said exclusion path.
The end result will be a UITextView with a UIImageView placed above it. None of the UITextView's text will be blocked by the UIImageView as the UITextView's textContainer's exclusionPaths have instructed the text not to populate there.
Here is an example of some code I've done to do something similar, with variable names changed to match your code a bit:
let imageView: UIImageView!
func addImageView() {
imageView = UIImageView(frame: CGRect(x: textView.frame.maxX - 200, y: textView.frame.maxY - 150, width: 200, height: 150))
textView.superView.addSubview(imageView)
}
func setExclusionPath(for imageView: UIImageView) {
let imageViewPath = UIBezierPath(rect: CGRect(x: textView.frame.maxX - imageView.frame.width, y: textView.frame.maxY - imageView.frame.height, width: imageView.frame.width, height: imageView.frame.height))
textView.textContainer.exclusionPaths.append(imageViewPath)
}
func someMethod() {
addImageView()
setExclusionPath(for: self.imageView)
}
Resources:
exclusionPaths reference from Apple

trying to add a textfield into an nsmenuitem but it wont show up

here is the code i use to try to add a textfield into an nsmenuitem
class menuitemtest1: NSTextField {
var menuitemtest1 = NSTextField()
override func viewDidChangeBackingProperties() {
menuitemtest1.frame = CGRect(x: 220, y: 8, width: 103, height: 17)
menuitemtest1.stringValue = "Maximum Lenght"
menuitemtest1.isEditable = false
menuitemtest1.textColor = .gray
menuitemtest1.isSelectable = false
menuitemtest1.drawsBackground = false
}
}
thats the class
and how i add it
var textFieldInMenutest = NSMenuItem()
menuBarMenu.addItem(textFieldInMenutest)
textFieldInMenutest.view = menuitemtest1()
You created an NSTextField subclass which has as a property, a separate and direct NSTextField instance. This makes no sense. What you intended to do, was this:
class menuitemtest1: NSTextField {
override func viewDidChangeBackingProperties() {
self.frame = CGRect(x: 220, y: 8, width: 103, height: 17)
self.stringValue = "Maximum Lenght"
self.isEditable = false
self.textColor = .gray
self.isSelectable = false
self.drawsBackground = false
}
}
As for why it "doesn't show up" — the text field you did add as the menu item's view has a zero-sized (default) frame, so it's simply invisible.
Further, viewDidChangeBackingProperties is not the correct place to set up basic properties of the field. In such a subclass, you should use the initializer, init(frame:... or init(coder: ...

How to add a UIButton to Swift Playground?

So I opened up playground, I just want to add a simple UIButton (or a simple UIView) for testing purposes. I can't get it to display though. This is what I have so far:
import UIKit
var uiButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
uiButton.frame = CGRectMake(0, 0, 100, 100)
uiButton.setTitle("Test", forState: UIControlState.Normal);
//self.view.addSubview(uiButton) <-- doesn't work
Do you guys know what I'm doing wrong? Thanks
I think you can add button into playground and your code is correct you can see your button here when you click on Quick Look:
or you can see that button with clicking on Value History:
you dont need to add that into view.
If you import XCPlayground in your playground you can show views using:
let view=UIView()
//other setup
XCPShowView("View Title",view)
This will show the view in the assistant editor
To show the assistant editor goto View > Assistant Editor > Show Assistant Editor
import XCPlayground has been deprecated and is now import PlaygroundSupport.
You need to create your view first and give it a size.
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.backgroundColor = UIColor.black
then add PlaygroundPage.current.liveView = view to be able to see the view in the assistant editor.
now that you have created your view, and you can see the live preview of it. You can now add other views to it. In my playground I added several views so I created simple function with a default width and height.
func addSquareView(x: Int, y: Int, width: Int = 100, height: Int = 100, color: UIColor) {
let newView = UIView(frame: CGRect(x: x, y: y, width: width, height: height))
newView.backgroundColor = color
view.addSubview(newView)
}
addSquareView(x: 100, y: 100, color: .blue)
Now there is a blue square in the center of my view.
I was just doing my sorting programming and pated the code..it will help you to add a button in playgroud with target.
import UIKit
import PlaygroundSupport
let aButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
aButton.backgroundColor = .lightGray
aButton.setTitle("Run Sorting", for: .normal)
aButton.layer.cornerRadius = 10
aButton.clipsToBounds = true
class Sorting {
#objc func callSorting() {
self.getSortedArrayUsingSelectionSort()
}
func getSortedArrayUsingSelectionSort() {
var arr = [4, 9, 10, 6, 1, 3]
let n = arr.count
for i in 0..<(n - 1) {
var minIndex = i
for j in (i+1)..<n {
if arr[j] < arr[minIndex] {
minIndex = j
}
}
if minIndex != i {
let temp = arr[minIndex]
arr[minIndex] = arr[i]
arr[i] = temp
} }
print(arr)
}
}
let target = Sorting()
aButton.addTarget(target, action: #selector(Sorting.callSorting), for: .touchUpInside)
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
containerView.backgroundColor = .white
PlaygroundPage.current.liveView = containerView
containerView.addSubview(aButton)