Add CATextLayer on Top of NSImageView - swift

I'm trying to display text on top of an NSImage.I use the following code
let myTextLayer = CATextLayer()
myTextLayer.string = "My text"
myTextLayer.foregroundColor = NSColor.cyan.cgColor
myTextLayer.frame = self.img_view.bounds
self.img_view.layer=myTextLayer;
This keeps producing empty NSImageView

You needs explicitly say NSImageView wantsLayer before use layers, like below
self.img_view.wantsLayer = true
let myTextLayer = CATextLayer()
myTextLayer.string = "My text"
myTextLayer.foregroundColor = NSColor.cyan.cgColor
myTextLayer.frame = self.img_view.bounds
self.img_view.layer.addSublayer(myTextLayer)

Related

AdjustsFontSizeToFitWidth equivalent for iOS15 UIButton

I'm using UIKit, not SwiftUI. Previously, I used setAttributedTitle and I also set AdjustsFontSizeToFitWidth to true, and the font size shrinks to match the button width. I can't seem to match this behavior with the new button configurations and swift's AttributedString.
Here's what I have:
var config = UIButton.Configuration.filled()
config.imagePadding = 5
config.imagePlacement = .trailing
config.image = UIImage(systemName: "ellipsis.circle",
withConfiguration: UIImage.SymbolConfiguration(scale: .medium))
config.titlePadding = 10
config.cornerStyle = .capsule
let handler: UIButton.ConfigurationUpdateHandler = { button in
button.configuration?.background.backgroundColor = Colors.specLabel
button.configuration?.baseForegroundColor = Colors.greyText
}
myButton.configuration = config
myButton.configurationUpdateHandler = handler
And then
guard let attributedString = try? AttributedString(markdown: "hello **hello** hello") else { return }
myButton.configuration?.attributedTitle = attributedString
Every time, I get a multi-line button. Setting numberOfLines, lineBreakMode and adjustsFontSizeToFitWidth is all ignored. Any ideas?

How to make `attributedReadMoreText` in `ReadMoreTextView` library not selectable - swift

I am a new iOS programming. Now i am creating a sample app which display text using ReadMoreTextView library. My content may contain many lines but by using this library i can maximumNumberOfLines to display how many lines of content should be displayed. I implement those content in cell of UITableView and i have problem is that, when i use label.attributedReadMoreText = NSAttributedString(string: "...") then end of content will display ... and when i click on it and then whole content will be display so, my question is that: How to not letting user click on that ... because i want user to click on cell then i will show another view and display whole content there?
How can i achieve something like this? Thank in advance.
This is how i set UITextView
lazy var categoryShortDetailLabel: ReadMoreTextView = {
let label = ReadMoreTextView()
label.font = UIFont(name: "SFCompactText-Regular", size: 16)
label.textColor = .black
label.isEditable = false
label.isSelectable = false
label.maximumNumberOfLines = 3
label.shouldTrim = true
label.attributedReadMoreText = NSAttributedString(string: "...")
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
Looking at the code here, I found that, the ReadMoreTextView is meant to provide you the feature like, ReadMore and ReadLess for the larger texts in textView.
However your requirement is to stop that functionality. Now, if you take a look at the code here, you will get the idea, that the function shoreMoreText and it's a private function so, can't override it. and this function is expanding the texts and setting the numberOfLines to zero. so, what you can do is, comment the code within and return from function to stop doing the action. Also as the ReadMoreTextView is Licensed as MIT(Read licence here) so, it's okay to modify the code.
private func showMoreText() {
return
/*if let readLessText = readLessText, text.hasSuffix(readLessText) { return }
shouldTrim = false
textContainer.maximumNumberOfLines = 0
if let originalAttributedText = _originalAttributedText?.mutableCopy() as? NSMutableAttributedString {
attributedText = _originalAttributedText
let range = NSRange(location: 0, length: text.count)
if let attributedReadLessText = attributedReadLessText {
originalAttributedText.append(attributedReadLessText)
}
textStorage.replaceCharacters(in: range, with: originalAttributedText)
}
invalidateIntrinsicContentSize()
invokeOnSizeChangeIfNeeded()*/
}
Try and share your results.
Hope it helps!

total of label click on two buttons

I have two UIButton if I click onto button1 the value of label1 is 250, if I click again onto button1 the value of label1 should be 0. The same logic is applied to my button2 and label2. I want to store the addition of label1 and label2 into label3. . My code is:
func PickUpCarCost() {
if (!isclick){
imgCheck.image = UIImage(named: "check.png")
isclick=true
//Pickup fare conver into integer
let PickUp = String(self.PickUpPrice)
PickUpFare.text = PickUp
self.pickCost = Int( PickUpFare.text!)
self.PAyAmount = ((self.PayFareWithSecurity)+(self.pickCost))
print("PaybleAmount: \(self.PAyAmount)")
self.AmountPay1 = ((self.PAyAmount)+(self.DeliverCost))
PaybleAmount.text=String(self.AmountPay1!)
}
else
{
imgCheck.image = UIImage(named: "uncheck.png")
PickUpFare.text = "0"
self.PickUpElse=Int(PickUpFare.text!)
print("PickUpElse: \(self.PickUpElse)")
self.PAyAmount = (self.PayFareWithSecurity)+(self.PickUpElse)
PaybleAmount.text=String(self.PAyAmount!)
isclick=false
}
}
func CarDeliverCost() {
if (!isclick){
imgUnCheck.image = UIImage(named: "check.png")
isclick=true
let DeliverPrice = String(self.deliveryPrice)
DeliverFare.text = DeliverPrice
self.DeliverCost = Int(DeliverFare.text!)
self.PAyAmount = ((self.PayFareWithSecurity)+(self.DeliverCost))
print("PaybleAmount: \(self.PAyAmount)")
PaybleAmount.text=String(self.PAyAmount!)
}
else
{
imgUnCheck.image = UIImage(named: "uncheck.png")
let deliveryelse = String(0)
DeliverFare.text = deliveryelse
self.deliver = Int(DeliverFare.text!)
PaybleAmount.text=String(self.PAyAmount!)
isclick=false
}
}
The first issue that you're facing is that you are using the same boolean isClick for two different scenarios. Your logic is that the user could decide to click onto both buttons or a single one. So if I click onto the first button then your flag is turn on, since the second button also uses the same boolean then you'll get that uncheck behavior automatically.
Thus you should use two different booleans such as hasPressOntoFirstButton and hasPressOntoSecondButton, where both set to false at the beginning, and each variable is used in their respective scene.
You mentioned that you want to add the label1 to label2, the easier way would be add the variable that set these labels. i.e
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
let data1: Double = 83.0
let data2: Double = 82.0
var total: Double { // have a computed variable to adds automatically
return data1 + data2
}
label1.text = "\(data1)"
label2.text = "\(data2)"
label3.text = "\(data1 + data2)" // or "\(total)"
Remarks:
(1) your naming convention are really misleading, you should separate UI object naming from data. i.e you can have deliveryFareLabel.text instead of DeliverFare.text
(2) avoid using !, unwrapped all optional using either nil coalescing ??, or if let or guard statements

tvOS: Anyway to display a subtitle outside of the AVPlayer?

So the scenario is that there is a view where the user can enable/disable subtitles in an app I'm helping to develop.
On that view there is a sample text saying "This is what captions look like", and at the moment it's just a basic, unstyled UILabel. Ideally I would like it to be styled in a similar manner to how the user has customized their captions in the System Settings.
Is this possible in any way? I've envisioned two possible method:
Create an AVPlayer instance and a .vtt file with the text, load it into the view and pause the player. I'm not sure this is possible with a sample video (and it would somehow have to be transparent as there is an image behind the sample sub text).
Somehow get all the styling (font, size, background color, etc) the user has set for their subtitle and create an attributed string to match that
Method 2 seems like the most feasible way, but I don't know if we have access to those settings in code.
So I figured it out! It basically makes use a combination of the Media Accessibility API, which allows you to get the values the user has chosen for their captions/subtitle settings, Attributed Strings, and a subclass UILabel (although this could maybe be substituted with a UITextView as that will allow you to set it's UIEdgeInsets natively)
So, first, the subclass is to allow the UILabel to be inset. This is because captions can have a background color AND a text highlight color and without the inset, the text highlight is all you see. So the function the subclass is simple:
class InsetUILabel: UILabel {
override func drawTextInRect(rect: CGRect) {
let inset: CGFloat = 15
let insets: UIEdgeInsets = UIEdgeInsets(top: inset, left: inset/2, bottom: inset, right: inset/2)
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}
}
And for generating the actual label. This uses a label called textSample, but you can obviously make it a little more general.
import MediaAccessibility
func styleLabel(sampleText: String) {
let domain = MACaptionAppearanceDomain.User
// Background styling
let backgroundColor = UIColor(CGColor: MACaptionAppearanceCopyWindowColor(domain, nil).takeRetainedValue())
let backgroundOpacity = MACaptionAppearanceGetWindowOpacity(domain, nil)
textSample.layer.backgroundColor = backgroundColor.colorWithAlphaComponent(backgroundOpacity).CGColor
textSample.layer.cornerRadius = MACaptionAppearanceGetWindowRoundedCornerRadius(domain, nil)
// Text styling
var textAttributes = [String:AnyObject]()
let fontDescriptor = MACaptionAppearanceCopyFontDescriptorForStyle(domain, nil, MACaptionAppearanceFontStyle.Default).takeRetainedValue()
let fontName = CTFontDescriptorCopyAttribute(fontDescriptor, "NSFontNameAttribute") as! String
let fontColor = UIColor(CGColor: MACaptionAppearanceCopyForegroundColor(domain, nil).takeRetainedValue())
let fontOpacity = MACaptionAppearanceGetForegroundOpacity(domain, nil)
let textEdgeStyle = MACaptionAppearanceGetTextEdgeStyle(domain, nil)
let textHighlightColor = UIColor(CGColor: MACaptionAppearanceCopyBackgroundColor(domain, nil).takeRetainedValue())
let textHighlightOpacity = MACaptionAppearanceGetBackgroundOpacity(domain, nil)
let textEdgeShadow = NSShadow()
textEdgeShadow.shadowColor = UIColor.blackColor()
let shortShadowOffset: CGFloat = 1.5
let shadowOffset: CGFloat = 3.5
switch(textEdgeStyle) {
case .None:
textEdgeShadow.shadowColor = UIColor.clearColor()
case .DropShadow:
textEdgeShadow.shadowOffset = CGSize(width: -shortShadowOffset, height: shortShadowOffset)
textEdgeShadow.shadowBlurRadius = 6
case .Raised:
textEdgeShadow.shadowOffset = CGSize(width: 0, height: shadowOffset)
textEdgeShadow.shadowBlurRadius = 5
case .Depressed:
textEdgeShadow.shadowOffset = CGSize(width: 0, height: -shadowOffset)
textEdgeShadow.shadowBlurRadius = 5
case .Uniform:
textEdgeShadow.shadowColor = UIColor.clearColor()
textAttributes[NSStrokeColorAttributeName] = UIColor.blackColor()
textAttributes[NSStrokeWidthAttributeName] = -2.0
default:
break
}
textAttributes[NSFontAttributeName] = UIFont(name: fontName, size: (textSample.font?.pointSize)!)
textAttributes[NSForegroundColorAttributeName] = fontColor.colorWithAlphaComponent(fontOpacity)
textAttributes[NSShadowAttributeName] = textEdgeShadow
textAttributes[NSBackgroundColorAttributeName] = textHighlightColor.colorWithAlphaComponent(textHighlightOpacity)
textSample.attributedText = NSAttributedString(string: sampleText, attributes: textAttributes)
}
Now the text highlight section makes use of shadows, with values I think look pretty good, but you might want to tweak them a tiny bit. Hope this helps!

Adding min/max images to NSSlider using Swift

I created a NSSlider programmatically and implemented it in a NSMenuItem:
let menuVolumeSliderItem = NSMenuItem()
let menuVolumeSlider = NSSlider()
menuVolumeSlider.sliderType = NSSliderType.linear
menuVolumeSlider.isEnabled = true
menuVolumeSlider.isContinuous = true
menuVolumeSlider.action = #selector(AppDelegate.doSomething)
menuVolumeSlider.minValue = 0.0
menuVolumeSlider.maxValue = 1.0
menuVolumeSlider.floatValue = 0.5
menuVolumeSlider.frame.size.width = menu.size.width
menuVolumeSlider.frame.size.height = 20.0
menuVolumeSliderItem.view = menuVolumeSlider
menu.addItem(menuVolumeSliderItem)
And essentially, it work’s like a charm:
But the result I want to achieve is a Slider with a min/max image on the left/right edge of the Slider like this one:
How can I achieve a NSMenuItem like that?
EDIT:
I’ve created a custom NSView:
let view = NSView()
view.frame.size.width = menu.size.width
view.frame.size.height = 25.0
... then created a NSImage ...
let img = NSImageView()
img.frame.size.width = 16.0
img.frame.size.height = 16.0
img.image = NSImage(named: "NSStatusAvailable")!
... and added menuVolumeSlider and img to my custom view:
view.addSubview(img)
view.addSubview(menuVolumeSlider)
menuVolumeSliderItem.view = view
menu.addItem(menuVolumeSliderItem)
... and it worked! But can anyone please give me a hint how to change the positions of the subViews?
Create a NSView that will include the slider and the two images. Then set menuVolumeSliderItem.view to be that NSView.