textSize property of SCNText is missing - swift

I want to add a SCNPlane around the SCNText in ARSCNView. therefore I need the textSize of a text which I have created with SCNText like:
as you can see from the image, there is an error which indicate that the
SCNText has no member 'textSize'
but I can see the related documentation in apple website for both SCNText and textSize.
does anybody know what is the problem and how can I access the textSize property?
I'm using xCode 10.0 beta 4, swift 4.2, macOS Mojave 10.14 Beta.

The textSize property is only available on macOS 10.8+ which is why you can't see it (assuming of course you are building for IOS). By this, I mean that if you are building a MacOS app then the variable is accessible. Whereas if you are building for IOS it is not.
If you want to get the size of your SCNText you can use it's boundingBox property to get its width and height for example e.g:
//1. Get The Bounding Box Of The Text Node
let boundingBox = textNode.boundingBox
//2. Get The Width & Height Of Our Node
let width = boundingBox.max.x - boundingBox.min.x
let height = boundingBox.max.y - boundingBox.min.y
If you just want to set the fontSize you can do something like so:
//1. Create An SCNNode With An SCNText Geometry
let textNode = SCNNode()
let textGeometry = SCNText(string: "StackOverflow", extrusionDepth: 1)
textGeometry.font = UIFont(name: "Skia-Regular_Black", size: 20)
Remembering of course that when you specify the font size in ARKit, this in meters.
Hope it helps...

Related

SCNText ignores NSAttributedString attributes

Although the docs claim that SceneKit's SCNText works with NSAttributedString, the following NSAttributedString attributes seem to be ignored completely (and perhaps others, too):
shadow
strokeColor
strokeWidth
foregroundColor
SCNText does obey NSAttributedString's font attribute, however -- and it does display on screen.
What I'm trying to do is add a stroke to my SCNText. I do not want to use a SpriteKit overlay for such a simple use case.
From the docs:
When you create a text geometry from an attributed string, SceneKit styles the text according to the attributes in the string, and the properties of the SCNText object determine the default style for portions of the string that have no style attributes.
Here's what I'm doing in code. This code results in a white-filled text in the correct font, but without shadow or stroke of any kind:
let labelNode = SCNNode()
let label = SCNText(string: "\(targetLevel)", extrusionDepth: 0.0)
let font = UIFont(name: "Whatever", size: 4.0)
let shadow = NSShadow()
shadow.shadowBlurRadius = 1.0
shadow.shadowColor = UIColor.black
let attStr = NSAttributedString(string: "\(targetLevel)", attributes: [.font: font!, .shadow: shadow, .strokeColor: UIColor.black, .strokeWidth: 0.5])
label.string = attStr
label.alignmentMode = "kCAAlignmentCenter"
label.flatness = 0.01
//Using a big font and then scaling the node down to make it look less jagged:
labelNode.scale = SCNVector3(0.25,0.25,0.25)
labelNode.geometry = label
labelNode.castsShadow = false
parent.addChildNode(labelNode)
Questions: Am I doing something wrong -- or is this a limitation of SceneKit? Is there any other way to stroke an SCNText? I saw this question, but it's about fill color, not stroke, and didn't fix my problem.
As you've found (and others have noted), you don't automatically get all of the properties of the attributed string rendered in SceneKit. Think of them in terms of what you are drawing (e.g. the shape of the text), rather than how you draw them (e.g. color, shadow etc). All SceneKit uses is the geometry.
If you want outline-stroked 3D text then you may have to get the path from the font and use that for your geometry instead of SCNText. I wrote this category but the code is pretty ancient now, I have no idea if it still works, but it should be enough to get you there.
You could also perhaps render the text as an image and put it as a texture on a billboard, unless you actually want it to be part of the 3D scene. But by this point, a sprite kit overlay isn't looking that complicated, is it?

NSMenuItem with attributedTitle containing an NSFont object draws the title with baseline shift

I'm tying to create an NSPopUpButton with the list of fonts available in the system. Seemed pretty obvious task but I've failed. I guess, I'm missing something so obvious that I've completely forgot about it.
The code is pretty straight:
let button = NSPopUpButton()
button.menu = NSMenu()
NSFontManager.shared.availableFonts.forEach { fontNameString in
let item = NSMenuItem()
let font = NSFont(name: fontNameString, size: 14)!
let attrs: [NSAttributedString.Key: Any] = [.font: font]
item.attributedTitle = NSAttributedString(string: fontNameString, attributes: attrs)
button.menu?.addItem(item)
}
But that just creates the NSMenu with items having shifted baselines. I've tried to calculate the baseline offset and add it as an attribute but I've failed. Haven't found an algorhythm to satisfy all the font available in the system.
Besides, adding the baseline offset resizes the corresponding NSMenuItem which does not have a fixed size, by the way - the height of an item is different on every font.
To analyse the situation I've added the .backgroundColor attribute and set it to red NSColor. And that brought even more confusing. It appears that some font somehow not drawing in bounds.
How can I center the attributed title vertically? Please, help!
Probably, that is NSAttributedString's issue.
To workaround, I created a custom view and drew a string in it with a trick.
Then set it to NSMenuItem.view.
Get more details, see my codes below.
https://github.com/bluedome/FontSelectionView/blob/main/FontSelectionView.swift
Hope it help, if you're still having the trouble...

PDFAnnotationSubType Ink not saved when persisting (iOS11, PDFKit)

In iOS 11, I have a PDFView controller implementation that allows to annotate a PDF, and one of those annotation if free-form drawing using PDFAnnotationSubtypeInk
let page : PDFPage = ...
let points : [CGPoint] = ...
let path = UIBezierPath()
for x in 0..<points.count {
let point = self.pdfView.convert(points[x], to: page)
if x == 0 {
path.move(to: point)
}else{
path.addLine(to: point)
}
}
let border = PDFBorder()
border.style = .solid
border.lineWidth = 2.0
let drawAnnotation = PDFAnnotation(bounds: page.bounds(for: .mediaBox), forType: .ink, withProperties: nil)
drawAnnotation.backgroundColor = .yellow
drawAnnotation.interiorColor = .yellow
drawAnnotation.fontColor = .yellow
drawAnnotation.color = .yellow
drawAnnotation.border = border
drawAnnotation.add(path)
page.addAnnotation(drawAnnotation)
When I call the persistence code
if let path = self.pdfDocumentPath, let document = self.pdfDocument {
if !document.write(toFile: path){
NSLog("Failed to save PDF")
}
}
Everything works in theory, the PDF is saved to disk... But on the next-reload from disk, my annotation is nowhere to be seen. Other types of annotations are saved correctly (i.e. Text and Highlight)
It looks like the UIBezierPaths never makes it back from Disk. No UIBezierPath is to be found in the reloaded PDFAnnotation
Am I doing something wrong? Am I missing something?
PS: I am sorry for Ruby devs that are looking for questions/answers on the Ruby Package known as PDFKit... iOS also have a package named PDFKit since iOS 11... Sorry for any confusion.
I hacked my way around the problem. The annotation get saved, but the UIBezierPath does not (and all it's points)
So when I write the PDF back to disk, I go through all the pages, and all annotations, and find those with UIBezierPath, and serialize the CGPoints into JSON into the PDFAnnotation content property.
When reloading the same PDF, I do the exact same thing, which is go through all the pages, and find .ink annotation, deserialize the points, and add the UIBezierPath back on the annotation.
Apple fixed this bug in iOS 12. PDFAnnotation.paths is filled correctly.
Starting with some version between 11.2 - 11.4 (I do not have the opportunity to verify, but it does not work exactly in 11.1 and works in 11.4+) you can use PDFAnnotation method drawWithBox:inContext: for rendering ink annotation correctly.
Attention, in iOS 12.0 Apple broke PDFAnnotation.color (alpha is always equal 1 after save-reload PDFDocument from disk). Can be repaired by drawing an annotation through the context with method above.

SKLabelNode extremely blurry [screenshot included]

I'm trying to add a label to an ARKit project, but it's rendering extremely blurry. See image below:
Here's my code:
let shapeNode = SKShapeNode(rectOf: CGSize(width: 30, height: 30))
shapeNode.name = "bar"
shapeNode.fillColor = UIColor.white
let labelNode = SKLabelNode(text: "Hello world")
labelNode.horizontalAlignmentMode = .left
labelNode.verticalAlignmentMode = .top
labelNode.fontColor = UIColor.black
labelNode.fontSize = 3
When you create a SKScene for display, you have to give it a size. This is the resolution of what will be rendered. It will then be scaled to the SKSceneView it appears in, according to how you set its scaleMode property. If the resolution of your SKScene is lower than the point size of the view it appears in, the output will be adjusted to fit using a standard scaling algorithm and will therefore be blurry.
Try increasing the size of your SKScene by a little bit and see if that helps. Note that you will likely also have to adjust the size and position of your nodes as these will appear to shrink as the scene gets larger.

Adding UILabel programmatically only by code. Sizing ? (Swift)

I try to add a UILabel by code which works, but not as intended.
With following Code I can see the label but the height of the label is not correct as you can see in the attached screen. I would like the uilabel sized so the height of the label corresponde to the fontsize.
Of course I could simply oversize the UILabels high so the text would fit. But I don't think thats best practice.
How do I size an UILabel based on a fontsize ?
var uilabel1=UILabel();
uilabel1.frame=CGRectMake(0, drawy, screenwidth, ititlesize);
uilabel1.text=sTextViewOptionsTitle;
uilabel1.font = UIFont(name: uilabel1.font.fontName, size: 28);
self.view.addSubview(uilabel1);
var lastelementheigth=uilabel1.frame.height;
UPDATE Solution with sizeToFit()
Thanks for the answer regarding sizeToFit(). This did the trick with some additional code to position the control.
updated Code:
var uilabel2=UILabel();
//uilabel2.frame=CGRectMake(0, drawy, screenwidth/2, DropdownHeigth);
uilabel2.text=sTextSizeTextViewer;
uilabel2.font = UIFont(name: uilabel2.font.fontName, size: iFontsize_Option);
uilabel2.sizeToFit();
uilabel2.frame.origin = CGPoint(x: 0, y: drawy);
self.view.addSubview(uilabel2);
lastelementheigth=uilabel2.frame.height;
You may only need to call -sizeToFit. This will change the label's size to its "preferred" size based on its contents. Then you can reposition the origin or center if desired.
It's been asked before - take a look at:
Replacement for deprecated sizeWithFont: in iOS 7?
The method you're looking for is sizeWithAttributes