set and create paragraph style in swift - xcode

I am trying to port an objective C method to draw text in a PDF context to Swift.
I could convert most of the code, but the following lines are giving me a problem.
Some help in the conversion would be welcome.
Here the Objective C code:
-(void)drawText:(NSString *)textToDraw context:(CGContextRef)myPDFContext textcolor:(NSColor *)textcolor textfont:(NSFont*)textfont textbox:(CGRect)boxsize pagesize:(CGSize)pageSize {
// .........
// create paragraph style and assign text alignment to it
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting _settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
// set paragraph style attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
// .........
}
// The following lines are my try in Swift, but this gives errors:
func DrawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.Alignment, valueSize: sizeof(alignment), value: &alignment)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
//.......
If this is solved I could post the complete method in Swift.

I think this does it:
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
I made the following modifications:
I changed sizeof(alignment) to UInt(sizeofValue(alignment)) because sizeof in Swift only takes a type (such as sizeof(Int)). The UInt() is a constructor that turns the Int returned by sizeofValue into the UInt needed by this call.
I make alignmentSetting into an array so that it could be passed to an UnsafePointer of that type. This also better matches the original version.
I changed CTParagraphStyleSpecifier.Alignment to just .Alignment since the first part isn't needed since spec is of type CTParagraphStyleSpecifier.
The hardcoded 1 is OK in this case because you are passing just one value, but the more general solution would be to do:
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))

Here the full working method based on the corrections and help.
func drawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
let _font = NSFont(name:textfont.fontName, size:textfont.pointSize )
if (_font == nil) {
println("Font [\(textfont)] does not exist")
return
}
let myBoxWidth = boxsize.size.width
let myBoxHeight = boxsize.size.height
let myBoxxpos = boxsize.origin.x
let myBoxypos = boxsize.origin.y
let frameRect = CGRectMake(myBoxxpos, myBoxypos,myBoxWidth,myBoxHeight);
let framePath = CGPathCreateMutable()
CGPathAddRect(framePath, nil, frameRect);
// create attributed string
let attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrStr, CFRangeMake(0, 0), textToDraw);
// create font
let font = CTFontCreateWithName(textfont.fontName, textfont.pointSize, nil);
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
//let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
// set font attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
// set color attribute
CFAttributedStringSetAttribute(attrStr,CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTForegroundColorAttributeName,textcolor.CGColor);
// Prepare the text using a Core Text Framesetter.
let framesetter = CTFramesetterCreateWithAttributedString(attrStr);
// Get the frame that will do the rendering.
let currentRange = CFRangeMake(0, 0);
let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil);
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(myPDFContext, CGAffineTransformIdentity);
// Draw the frame.
CTFrameDraw(frameRef, myPDFContext);
}

Related

How to use an array to fill the title text

I've created a customized keyboard using UIView. I'm trying to auto-fill the letters using a for loop but with no success.
func initializeSubviews() {
let xibFileName = "keyboardView" // xib extension not included b
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView
self.addSubview(view)
view.frame = self.bounds
setKeyboardText()
}
#IBOutlet var keyboardLetters: [myKeyboardBtn]!
func setKeyboardText() {
let str = "abcdefghijklmnopqrstuvwxyz"
let characterArray = Array(str)
for (Index, key) in keyboardLetters.enumerated() {
key.titleLabel?.text = String(characterArray[Index])
}
// [a,b,c,d,...]
}
what am I doing wrong?
According to Apple
"To set the actual text of the label, use setTitle(_:for:)
(button.titleLabel.text does not let you set the text)."

Cocoa NSAttributedString Pasting Changes Font

I have an NSAttributedString which has an NSAttachment and some text. I have copied it to the pasteboard. I have also made sure that the string has no font attributes. When I paste it, it always changes the font to Helvetica. Is there any way to prevent this behavior?
let wrapper = NSFileWrapper()
wrapper.preferredFilename = "image"
let attachment = NSTextAttachment(fileWrapper: wrapper)
if let im = NSImage(data: data) { attachment.image = im }
let image = NSAttributedString(attachment: attachment)
let str = NSMutableAttributedString(attributedString: image)
str.appendAttributedString(NSAttributedString(string: "hello world"))
let range = NSMakeRange(0, str.length)
//remove font attributes
str.removeAttribute(NSFontAttributeName, range: range)
str.removeAttribute(NSFontSizeAttribute, range: range)
Swift.print(str.attributes) //prints an empty array
At this point I copy the data to the clipboard
if let d = str.RTFDFromRange(range, documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType]) {
let pboard = NSPasteboard.generalPasteboard()
pboard.clearContents()
pboard.declareTypes([NSPasteboardTypeRTFD], owner: self)
pboard.setData(d, forType: NSPasteboardTypeRTFD)
}

iOS CVPixelBufferCreate leaking memory in swift 2

I'm trying to convert an image into a video, and the right way seems to use a AVAssetWriter with a AVAssetWriterInputPixelBufferAdaptor, and it works well, but it leaks memory.
When I convert the CGImage to a CVPixelBuffer, I call CVPixelBufferCreate, which never frees it's memory.
func CGImageToPixelBuffer(image: CGImageRef, frameSize: CGSize) -> CVPixelBuffer {
// stupid CFDictionary stuff
let keys: [CFStringRef] = [kCVPixelBufferCGImageCompatibilityKey, kCVPixelBufferCGBitmapContextCompatibilityKey]
let values: [CFTypeRef] = [kCFBooleanTrue, kCFBooleanTrue]
let keysPointer = UnsafeMutablePointer<UnsafePointer<Void>>.alloc(1)
let valuesPointer = UnsafeMutablePointer<UnsafePointer<Void>>.alloc(1)
keysPointer.initialize(keys)
valuesPointer.initialize(values)
let options = CFDictionaryCreate(kCFAllocatorDefault, keysPointer, valuesPointer, keys.count,
UnsafePointer<CFDictionaryKeyCallBacks>(), UnsafePointer<CFDictionaryValueCallBacks>())
let buffer = UnsafeMutablePointer<CVPixelBuffer?>.alloc(1)
// here's the leak >:[
let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(frameSize.width), Int(frameSize.height),
kCVPixelFormatType_32ARGB, options, buffer)
CVPixelBufferLockBaseAddress(pixelBuffer.memory!, 0);
let bufferData = CVPixelBufferGetBaseAddress(buffer.memory!);
let rgbColorSpace = CGColorSpaceCreateDeviceRGB();
let context = CGBitmapContextCreate(bufferData, Int(frameSize.width),
Int(frameSize.height), 8, 4*Int(frameSize.width), rgbColorSpace,
CGImageAlphaInfo.NoneSkipFirst.rawValue);
CGContextDrawImage(context, CGRectMake(0, 0,
CGFloat(CGImageGetWidth(image)),
CGFloat(CGImageGetHeight(image))), image);
CVPixelBufferUnlockBaseAddress(pixelBuffer.memory!, 0);
return buffer.memory!
}
And here's the code which calls CGImageToPixelBuffer
func saveImageAsVideoFile(path: NSURL, image: UIImage, duration: Double) {
let writer = try! AVAssetWriter(URL: path, fileType: AVFileTypeQuickTimeMovie)
let videoSettings: NSDictionary = [
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : NSNumber(integer: Int(image.size.width)),
AVVideoHeightKey : NSNumber(integer: Int(image.size.height))
]
let input = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings as? [String : AnyObject])
let inputAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: input, sourcePixelBufferAttributes: nil)
input.expectsMediaDataInRealTime = false
writer.addInput(input)
writer.startWriting()
writer.startSessionAtSourceTime(kCMTimeZero)
// leak starts here
let buffer = CGImageToPixelBuffer(image.CGImage!, frameSize: image.size)
// append 30 frames to AVAssetWriter thing
for i in 0..<30
if input.readyForMoreMediaData {
inputAdaptor.appendPixelBuffer(buffer, withPresentationTime: CMTimeMake(i, 30))
}
}
//CVPixelBufferRelease(buffer) ??
input.markAsFinished()
writer.finishWritingWithCompletionHandler({ () -> Void in
print("yay")
})
}
CVPixelBufferRelease is not available in swift 2
CVPixelBufferCreate doesn't return a unmanaged pointer in swift 2, so I can't use this guys code
I've tried manually calling destroy and dealloc on the unsafepointer, to no avail.
every time it's called it increases the memory usage, and will crash the device if called enough
Any help or advice would be appreciated.
Your let buffer = UnsafeMutablePointer<CVPixelBuffer?>.alloc(1) isn't balanced by a dealloc, however adding that doesn't seem to fix the leak.
Simply using a CVPixelBuffer? does though, and seems simpler:
var buffer: CVPixelBuffer?
// TODO: handle status != noErr
let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(frameSize.width), Int(frameSize.height),
kCVPixelFormatType_32ARGB, options, &buffer)
CVPixelBufferLockBaseAddress(buffer!, 0);
let bufferData = CVPixelBufferGetBaseAddress(buffer!);
let rgbColorSpace = CGColorSpaceCreateDeviceRGB();
let context = CGBitmapContextCreate(bufferData, Int(frameSize.width),
Int(frameSize.height), 8, 4*Int(frameSize.width), rgbColorSpace,
CGImageAlphaInfo.NoneSkipFirst.rawValue);
CGContextDrawImage(context, CGRectMake(0, 0,
CGFloat(CGImageGetWidth(image)),
CGFloat(CGImageGetHeight(image))), image);
CVPixelBufferUnlockBaseAddress(buffer!, 0);
You should also handle status != noErr and unwrap the optional buffer sooner, to avoid all those buffer!s.
I also have similar leak. I am not sure but I think the following can be used:
let bufferPointer = UnsafeMutablePointer<CVPixelBuffer?>.alloc(1)
...
let pixelBuffer = bufferPointer.memory!
bufferPointer.destroy()
return pixelBuffer

XCode 7: Copy Layoutattributes

I'm getting the warning "This is likely occurring because the flow layout subclass MyApp.MenuFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them". How can I copy this in Swift?
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
if let array = super.layoutAttributesForElementsInRect(rect) as [UICollectionViewLayoutAttributes]? {
var visibleRect = CGRectZero
visibleRect.origin = (self.collectionView?.contentOffset)!
visibleRect.size = (self.collectionView?.bounds.size)!
let activeDisatance : CGFloat = visibleRect.size.width / 2
let zoomFactor : CGFloat = 0.15
for attributes in array{
if CGRectIntersectsRect(attributes.frame, visibleRect){
let distance = CGRectGetMidX(visibleRect) - attributes.center.x
let normalizedDistance = distance / activeDisatance
if abs(distance) < activeDisatance{
let zoom = 1 + zoomFactor * (1 - abs(normalizedDistance))
attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0)
attributes.zIndex = 1
}
}
}
return array
}
return super.layoutAttributesForElementsInRect(rect)
}
Collections in Swift are value types, that's the reason why they don't have a copy() method. So when you are calling
array = super.layoutAttributesForElementsInRect(rect) as [UICollectionViewLayoutAttributes]?
it already creates a copy of the super layout attributes array and stores it in array
The problem is not that the array has not been copied, the problem is, that you are changing the UICollectionViewLayoutAttributes inside the array without copying them first.
To avoid this error message you can do something like this:
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElementsInRect(rect)
var attributesCopy = [UICollectionViewLayoutAttributes]()
for itemAttributes in attributes! {
let itemAttributesCopy = itemAttributes.copy() as! UICollectionViewLayoutAttributes
// add the changes to the itemAttributesCopy
attributesCopy.append(itemAttributesCopy)
}
return attributesCopy
}

sometimes sprites will not bounce off the physics boundary

I'm beginning a new game and have a weird problem right off the bat. In my didMoveToView function I have the following to put a bounds on my sprites within the frame(whole screen)
self.physicsBody=SKPhysicsBody(edgeLoopFromRect: self.frame)
The following code adds an SKSpriteNode at the touch point and adds a rotatebyangle action with a repeat forever
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(balloonWorld)
let nodeAtPoint = self.nodeAtPoint(location)
if(nodeAtPoint.name == nil) {
let location = touch.locationInNode(self)
let randNum:Int = Int.random(min: 0, max: 7)
var stringColor:String = (balloonColors.objectAtIndex(randNum) as String)
stringColor = stringColor.stringByReplacingOccurrencesOfString("face",
withString :"")
let sprite = Balloon(theColor:stringColor)
//let spriteFileName:String = balloonColors.objectAtIndex(randNum) as String
//let sprite = SKSpriteNode(imageNamed:spriteFileName)
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
sprite.zPosition = SceneLevel.hero.rawValue
balloonWorld!.addChild(sprite)
let action = SKAction.rotateByAngle(CGFloat(-M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
} else {
nodeAtPoint.removeFromParent()
println(nodeAtPoint.name)
}
}
}
I've setup balloonWorld as follows:
balloonWorld = SKNode()
self.addChild(balloonWorld!)
My problem is that sometimes the balloon sprites will not bounce off the edge, but just keep going thru the edge never to be seen again.
Any suggestions?
Thanks,
Ken
per request here's the code for setting up the physics bodies
balloon:
class Balloon: SKNode {
// properties
var myColor:String?
var objectSprite:SKSpriteNode?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(theColor:String){
super.init()
self.myColor=theColor
let imageFileName = "\(theColor)face"
let objectSprite:SKSpriteNode = SKSpriteNode(imageNamed:imageFileName)
objectSprite.physicsBody = SKPhysicsBody(circleOfRadius: objectSprite.size.height / 2.0)
objectSprite.physicsBody?.affectedByGravity = true
objectSprite.name = theColor + " balloon"
addChild(objectSprite)
}
}
I didn't set up a physics body on the boundary as I though all I needed was the line self.physicsBody = SKPhysicsBody(edgeLoopFrom Rect : self:frame) since the boundary has the same frame.
I tried adding the following, but that did not change the behavior:
let borderShape=SKShapeNode(rect: CGRectMake(self.frame.origin.x+2, self.frame.origin.y+2, self.frame.size.width-4, self.frame.size.height-4))
borderShape.fillColor=SKColor.clearColor()
borderShape.strokeColor=SKColor.blackColor()
borderShape.lineWidth=1
borderShape.physicsBody?.categoryBitMask=BodyType.boundary.rawValue
borderShape.zPosition=SceneLevel.border.rawValue
borderShape.physicsBody=SKPhysicsBody(edgeLoopFromRect: borderShape.frame)
balloonWorld!.addChild(borderShape)
Create an enum to differentiate the two types of sprites:
enum ColliderType {
case sprite1 = 1
case sprite2 = 2
}
You need to add a contact and collision bit mask like so:
borderShape.physicsBody?.contactTestBitMask = ColliderType.sprite2.rawValue
Also add a collisionBitMask:
borderShape.physicsBody?.collisionBitMask = ColliderType.sprite2.rawValue
Do the same for sprite2 and set it's collisionBitMask and it's contactTestBitMask to be the rawValue of sprite1. Make sure you set this class to be the contactDelegate so you receive a warning though a function that will notify you when two objects have collided:
self.physicsWorld?.contactDelegate = self
func didBeginContact(contact: SKPhysicsContact) {
[handle the contact however you would like to here]
}
I hope this helps!
LearnCocos2D(Steffen Itterheim) pointed me in the right direction in his comment. I was using moveTo/moveBy and the physics engine would not properly handle that. Once I simply made the moves based on impulses everything worked fine.
Thanks Steffen

Resources