I'm trying to code an easy example to modify the color of a NSTextfield playing with green and red colors as good answer or wrong.
I cannot achieve that cause i always obtained an error message when using this apple guide page for NSTextField, AppKit framework ref.
I'm trying to use this code :
#IBOutlet weak var mensajeResultado: NSTextField!
when trying to colorize it without success
let rango = NSRange(location: 0,length: 0)
mensajeResultado.superclass.setTextColor(NSColor.redColor(), range: rango)
Shouldn't it just be:
mensajeResultado.textColor = NSColor.redColor()
?
Ken Thomases' answer is right, you just have to assign the color to the textColor property of your text field.
Just for info, it's very convenient to test in a Playground when you're not sure, for example:
import Cocoa
import XCPlayground
let tf = NSTextField(frame: NSMakeRect(50, 50, 100, 100))
tf.stringValue = "test"
tf.font = NSFont(name: "Helvetica", size: 16)
tf.backgroundColor = NSColor.blueColor()
tf.textColor = NSColor.whiteColor()
let v = NSView(frame: NSMakeRect(0, 0, 200, 200))
v.addSubview(tf)
XCPShowView("My View", v)
Related
Recently I came across an interesting(may be naive) problem regarding UIColor in Swift..
import UIKit
let view = UIView(frame: CGRect(x: 0,
y: 0,
width: 50,
height: 50))
view.backgroundColor = .systemBlue
let a = UIColor.systemBlue
switch a {
case .red:
print("red")
case .systemBlue:
print("blue")
default:
print("unknown")
}
Following code prints "blue" on playground perfectly fine, but changing
let a = UIColor.systemBlue
to
let a = view.backgroundColor ?? .red
prints "unknown" in playground, May someone help what is happening here? I could not resolve it.. Is it something related to value type or reference type at some point?? Please help!!
Printing the two values gives you the explanation:
print(UIColor.systemBlue)
<UIDynamicSystemColor: 0x600000b47880;
name = systemBlueColor
>
print(view.backgroundColor!)
<UIDynamicModifiedColor: 0x60000058bed0;
contrast = normal,
baseColor = <UIDynamicSystemColor: 0x600000b47880;
name = systemBlueColor
>
>
When setting the backgroundColor property, UIKit wraps the color in a private class UIDynamicModifiedColor.
If you compare the resolved colors using the view's traits, you'll get true:
UIColor.systemBlue.resolvedColor(with: view.traitCollection) ==
view.backgroundColor!.resolvedColor(with: view.traitCollection)
The resolved color is an absolute color:
print(UIColor.systemBlue.resolvedColor(with: view.traitCollection))
UIExtendedSRGBColorSpace 0 0.478431 1 1
A color such as UIColor.systemBlue is a dynamic color that might result in different colors, depending on the view's traits, which include factors such as high-contrast mode and dark/light mode.
From the docs of UIColor.systemBlue:
A blue color that automatically adapts to the current trait environment.
This issue came up in relation to a problem I had yesterday for which I should be able to create a workaround. As I investigated further, I found that it occurs more broadly than I originally thought. I had previously only noticed it in displayed text that included at least one newline character, but that's not the case below.
The problem seems to result from using the NSLayoutManager's boundingRect method to obtain (among other things) individual character widths and then using those widths to set characters' UITextView frame width properties. Doing so apparently causes the setting of the text view's backgroundColor to UIColor.clear to be ignored (i.e., the background becomes opaque). The Playground code below reproduces the problem, shown in red text, and shows the workaround of using a constant for widths, in black. The tighter the kerning, the more pronounced the effect.
Is this a bug? Or is it a quirk due to something else?
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.bounds = CGRect(x: -100, y: -100, width: 200, height: 200)
view.backgroundColor = .white
let str = "..T.V.W.Y.."
let strStorage = NSTextStorage(string: str)
let layoutManager = NSLayoutManager()
strStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: view.bounds.size)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
let strArray = Array(str)
struct CharInfo {
var char: Character
var origin: CGPoint?
var size: CGSize?
}
var charInfoArray = [CharInfo]()
for index in 0..<str.count {
charInfoArray.append(CharInfo.init(char: strArray[index], origin: nil, size: nil))
let charRange = NSMakeRange(index, 1)
let charRect = layoutManager.boundingRect(forGlyphRange: charRange, in: textContainer)
charInfoArray[index].origin = charRect.origin
charInfoArray[index].size = charRect.size
}
for charInfo in charInfoArray {
let textView0 = UITextView()
textView0.backgroundColor = UIColor.clear // Ignored in this case!!
textView0.text = String(charInfo.char)
textView0.textContainerInset = UIEdgeInsets.zero
let size0 = charInfo.size!
textView0.frame = CGRect(origin: charInfo.origin!, size: size0)
textView0.textContainer.lineFragmentPadding = CGFloat(0.0)
textView0.textColor = UIColor.red
view.addSubview(textView0)
let textView1 = UITextView()
textView1.backgroundColor = UIColor.clear // Required
textView1.text = String(charInfo.char)
textView1.textContainerInset = UIEdgeInsets.zero
var size1 = charInfo.size!
size1.width = 20 // But changing .height has no effect on opacity
textView1.frame = CGRect(origin: charInfo.origin!, size: size1)
textView1.frame = textView1.frame.offsetBy(dx: 0, dy: 20)
textView1.textContainer.lineFragmentPadding = CGFloat(0.0)
textView1.textColor = UIColor.black
view.addSubview(textView1)
}
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
This does seem to be a bug, but it's with NSLayoutManager's instance method boundingRect(forGlyphRange:in:). It only looks like it could be a transparency change.
According to Apple's documentation, boundingRect(forGlyphRange:in:) is supposed to "[return] a single bounding rectangle (in container coordinates) enclosing all glyphs and other marks drawn in the given text container for the given glyph range, including glyphs that draw outside their line fragment rectangles and text attributes such as underlining." But that's not what it's doing.
In this case, the width of each boundingRect gets reduced by the amount that the next glyph was shifted to the left, due to kerning. You can test this, for example, using str = "ToT" and adding print(size0.width) right after it is set. You'll get this:
6.0 // "T"; should have been 7.330078125
6.673828125 // "o"
7.330078125 // "T"
Until this bug is fixed, a workaround would be to calculate glyph size for each character in isolation.
I recently started coding Swift. So I am working on a macOS app to get more used to Swift.
I want that an NSTextField is created when the user presses a button. I got the code below but the NSTextField doesn't display. While research I found that it somehow must be added to view but not how this is done.
#IBAction func buttonPressed(_ sender: Any) {
let label = NSTextField()
label.stringValue = ("Hello World")
}
How do I make the NSTextField display? Any help would be highly appreciated.
Just turn the label into a layer-backed view. Then add it into a visible view:
let rect = NSMakeRect(20, 20, 80, 44)
let label = NSTextField(frame: rect)
label.wantsLayer = true
label.layer?.backgroundColor = NSColor.greenColor().CGColor
window?.contentView?.addSubview(label)
let rect = NSMakeRect(20, 20, 80, 44)
let label = NSTextField(frame: rect)
view.addSubview(label)
Worked for me. In Xcode 10 with swift 4.
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!
this is a very simple question, but I can't find an answer anywhere. I am trying to display a high score within a CGRect, but cannot figure out exactly how to do it. I am currently trying to do this on a playground so that might have something to do with it. Thanks for the help.
You can make a UILabel in a Swift playground. Here's how it might look:
Here's the code from the playground:
import UIKit
let score = 200
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 20))
label.backgroundColor = UIColor.purpleColor()
label.textColor = UIColor.orangeColor()
label.text = "High Score: \(score)"
A CGRect is the frame or dimensions assigned to a UIView, not the UIView itself. I would suggest creating a UILabel and add that to your view. You can set the text on the UILabel to, in your case, the high score variable as a string. Im not sure if it is possible to do this in a playground, but I may be mistaken. A XCode project would work perfect for this, but again, im not sure about the playground.
To do this in a playground, it would look like this:
import UIKit
import XCPlayground
let view = UIView(frame: CGRectMake(0,0,500,500)) //create a view and give it a proper size
let label = UILabel(frame: CGRectMake(0,0,100,23)) //create a label with a size
label.text = "My Label" //give the label some text
view.backgroundColor = UIColor.lightGrayColor() //default view in Playground as black background
view.addSubview(label) //add the label to the view
XCPlaygroundPage.currentPage.liveView = view //tell playgound to display your view as the root view in the assistant editor
You need to open the assistant editor in the playground to see the result (the two circles icon in the upper right corner)