How to get a value from NSValue in Swift? - ios

Here's what I've tried so far
func onKeyboardRaise(notification: NSNotification) {
var notificationData = notification.userInfo
var duration = notificationData[UIKeyboardAnimationDurationUserInfoKey] as NSNumber
var frame = notificationData[UIKeyboardFrameBeginUserInfoKey]! as NSValue
var frameValue :UnsafePointer<(CGRect)> = nil;
frame.getValue(frameValue)
}
But I always seem to crash at frame.getValue(frameValue).
It's a little bit confusing because the documentation for UIKeyboardFrameBeginUserInfoKey says it returns a CGRect object, but when I log frame in the console, it states something like NSRect {{x, y}, {w, h}}.

getValue() must be called with a pointer to an (initialized) variable
of the appropriate size:
var frameValue = CGRect(x: 0, y: 0, width: 0, height: 0)
frame.getValue(&frameValue)
But it is simpler to use the convenience method:
let frameValue = frame.CGRectValue() // Swift 1, 2
let frameValue = frame.cgRectValue() // Swift 3

Related

Swift iOS - Overlay text onto PDF with PDFKit and UI

I'm working in Swift 5 and on iOS. I'm trying to overlay text onto a current PDF I have. I'm essentially porting code I made from an app for macOS. This is the code from the Mac version:
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = CGRect(x: 0, y: 0, width: 792, height: 612)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
let graphicsContext = NSGraphicsContext(cgContext: context!, flipped: false)
NSGraphicsContext.current = graphicsContext
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
// Parse and Draw Text on the context
//drawText()
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
context!.saveGState()
context!.restoreGState()
context!.endPDFPage()
NSGraphicsContext.current = nil
context?.closePDF()
}
}
The drawText() function did most of the text overlaying that was needed, but I put another "draw "method below it to test it out.
I'm understandably getting an error Cannot find 'NSGraphicsContext' in scope since NSGraphicsContext doesn't exist on iOS. I've tried to find an equivalent translation with UIGraphicsPDFRenderer or UIGraphicsBeginPDFContextToData, and using some code from a Ray Wenderlich tutorial, I was able to create a new PDF and place text on it with the below code:
func createDocument(url: URL) -> Data {
//let pdfData = try? Data.init(contentsOf: url)
// 1
let pdfMetaData = [
kCGPDFContextCreator: "Timecard App",
kCGPDFContextAuthor: "Timecard App"
]
let format = UIGraphicsPDFRendererFormat()
format.documentInfo = pdfMetaData as [String: Any]
// 2
let pageWidth = 8.5 * 72.0
let pageHeight = 11 * 72.0
let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
// 3
let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
// 4
let data = renderer.pdfData { (context) in
// 5
context.beginPage()
// 6
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
}
return data
}
...but I couldn't find a way to load in current PDF "data" to the renderer and then draw from there. Does anyone have any suggestions on the proper way to do this?
Here is possible solution - actually you just need to operate with CoreGraphics context directly, set current, flip transform, etc. (style and conventions of original code preserved).
Tested with Xcode 12 / iOS 14.
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = page.bounds(for: .mediaBox)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
UIGraphicsPushContext(context!)
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: mediaBox.size.height)
context!.concatenate(flipVertical)
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
context!.endPDFPage()
context?.closePDF()
UIGraphicsPopContext()
}
}
Edit additional pages using the following function
// add a new page
func addPage(number: Int){
// index is one less than document page number
let index = number - 1
context.endPDFPage()
if let page = document.page(at: index) {
context.beginPDFPage(nil)
page.draw(with: .mediaBox, to: context)
context.concatenate(flipVertical)
}
}
Where document is the PDF document you wish to edit.
Then start editing that new page. The X and Y coordinates reset to 0,0 again for the new page.

Optional error while converting from Swift 2 to 3 in keyboardWillShow notification

I am converting my app to Swift 3 at the moment and I have problems with this function I used to show the keyboard before.
Initializer for conditional binding must have Optional type, not 'CGFloat'
The error appears in the third line.
It's been a while since I've programmed the last time and so I am not sure how to solve this.
func keyboardWillShow(_ sender: Notification) {
if let userInfo = sender.userInfo {
if let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height {
let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
let edgeInsets = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
UIView.animate(withDuration: duration!, animations: { () -> Void in
self.tableView.contentInset = edgeInsets
self.tableView.scrollIndicatorInsets = edgeInsets
self.view.layoutIfNeeded()
})
}
}
}
This is one of those rare situations where I recommend force-unwrapping. You know the userInfo contains this information, and you are hosed if it doesn't. Moreover, there is no need to pass through AnyObject or to call cgRectValue; you can cast all the way down to a CGRect in a single move. So I would write:
let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! CGRect).size.height
(Note that there is no if, because we are not doing a conditional binding; we simply cast, kaboom.)
[Note too that there is no need now to fetch the duration or to call animate or layoutIfNeeded; you can throw all of that away. We are already in an animation, and your changes to the contentInset and scrollIndicatorInsets will be animated in time to the keyboard.]
change
if let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height {
to
if let foo = userInfo[UIKeyboardFrameEndUserInfoKey] {
let keyboardHeight = (foo as as AnyObject).cgRectValue.size.height
--- UPDATE ---
or
if let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject?).cgRectValue.size.height {
AnyObject? works because AnyObject can contain Optional itself. So you have to casting to Optional explicitly. Either AnyObject? or Optional<AnyObject> will work.

How can I call an Objective C method from a Swift 3 app?

I'm trying to use wit-ai SDK, written in Objective C in a Swift 3 app.
The documentation in the GitHub repo explains how to use a particular Objective C method to send text string to Wit, but I'm not sure how to use that method in my app, and could use some guidance.
InterpretString
Sends an NSString to wit.ai for interpretation. Same as sending a voice input, but with text.
- (void) interpretString: (NSString *) string customData:(id)customData;
This is what I have so far:
import UIKit
class ViewController: UIViewController, WitDelegate {
/**
* Called when the Wit request is completed.
* param outcomes a NSDictionary of outcomes returned by the Wit API. Outcomes are ordered by confidence, highest first. Each outcome contains (at least) the following keys:
* intent, entities[], confidence, _text. For more information please refer to our online documentation: https://wit.ai/docs/http/20141022#get-intent-via-text-link
*
* param messageId the message id returned by the api
* param customData any data attached when starting the request. See [Wit sharedInstance toggleCaptureVoiceIntent:... (id)customData] and [[Wit sharedInstance] start:... (id)customData];
* param error Nil if no error occurred during processing
*/
public func witDidGraspIntent(_ outcomes: [Any]!, messageId: String!, customData: Any!, error e: Error!) {
if ((e) != nil) {
print("\(e.localizedDescription)")
return
}
let outcomes : NSArray = outcomes! as NSArray
let firstOutcome : NSDictionary = outcomes.object(at: 0) as! NSDictionary
let intent : String = firstOutcome.object(forKey: "intent") as! String
labelView!.text = intent
labelView!.sizeToFit()
}
var labelView : UILabel?
var witButton : WITMicButton?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// set the WitDelegate object
Wit.sharedInstance().delegate = self
// create the button
let screen : CGRect = UIScreen.main.bounds
let w : CGFloat = 100
let rect : CGRect = CGRect(x: screen.size.width/2 - w/2, y: 60, width: w, height: 100)
witButton = WITMicButton(frame: rect)
self.view.addSubview(witButton!)
// create the label
labelView = UILabel(frame: CGRect(x: 0, y: 200, width: screen.size.width, height: 50))
labelView!.textAlignment = .center
labelView!.text = "intent"
labelView!.textColor = UIColor.black
labelView!.sizeToFit()
self.view.addSubview(labelView!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Extracting string values from this json in IOS swift

I am using swiftyjson to extract the json but i cant able to extract specification json that is label and value inside specification. I need it quickly.
((
{
code = "NPR 1515";
description = "With its enhanced power and performance the NPR1515 provides a genuine \"workhorse\" that will take almost anything in its stride.
\nThe full 1500 Watt motor unit packs almost 50% more power than normally required and this power is transmitted to the floor through our long established 150rpm, oil filled, low load, planetary gearbox .
\nThe big advantage of excess power is to provide scope for many additional tasks where the excess power is both advantageous and needed.";
id = 16;
name = "Scrubbers & Polisher - NPR 1515";
specification = "[{\"label\":\"Model No\",\"value\":\"PR 1515\\t\\t\\r\"},{\"label\":\"\\nMotor\",\"value\":\"500W\\t\\r\"},{\"label\":\"\\nPad\",\"value\":\"00mm\\t\\r\"},{\"label\":\"\\nPower\",\"value\":\"30V AC 50Hz\\r\"},{\"label\":\"\\nBrush\",\"value\":\"50mm\\t\\r\"},{\"label\":\"\\nSpeed\",\"value\":\"50 rpm\\r\"},{\"label\":\"\\nVacuum\",\"value\":\"T130\\t\\r\"},{\"label\":\"\\nRange\",\"value\":\"2m\\t\\t\\r\"},{\"label\":\"\\nWeight\",\"value\":\"0 Kgs\\t\\r\"},{\"label\":\"\\nSize\",\"value\":\"185 x 580x 450mm\"}]";
"video_url" = "<null>";
}
))
let json2 = JSON(data3!)
for (index, object) in json2 {
let name = object["name"].stringValue
let code = object["code"].stringValue
let description = object["description"].stringValue
let specification = object["specification"].stringValue
does not run this part.
let json3 = JSON(specification)
for (index, object3) in json3 {
println("in this loop")
if let specification2 = object3["label"].string {
println(specification2)
}
else {
println(object3["label"].error)
}
let specification3 = object3["value"].stringValue
println(specification3)
}
ok now it is working but label are not able to print in label they are working in println but if i do this value works but not label.
let str = self.labelArray[i]
let label8 = UILabel(frame: CGRectMake(2, 0, 0, 0))
label8.backgroundColor = UIColor.whiteColor()
label8.textColor = UIColor.blackColor().colorWithAlphaComponent(0.7)
label8.frame = CGRect(x: 10, y: setheight , width: screenWidth/2, height: 25)
label8.textAlignment = NSTextAlignment.Left
label8.text = str
self.scrollview_add.addSubview(label8)
var label7 = UILabel(frame: CGRectMake(2, 0, 0, 0))
label7.backgroundColor = UIColor.whiteColor()
label7.textColor = UIColor.blackColor().colorWithAlphaComponent(0.7)
label7.frame = CGRect(x: screenWidth/2, y: setheight , width: screenWidth/2, height: 25)
label7.textAlignment = NSTextAlignment.Right
label7.text = self.valueArray[i]
self.scrollview_add.addSubview(label7)
setheight += 25
As posted in your question value of specification is a string so first you have to convert it into NSData then convert it into json:
if let jsonData = specification.dataUsingEncoding(NSUTF8StringEncoding){
let json = JSON(data:jsonData)
for (index, object3) in json {
println("in this loop")
if let specification2 = object3["label"].string {
println(specification2)
}
else {
println(object3["label"].error)
}
let specification3 = object3["value"].stringValue
println(specification3)
}
}
According to the data structure you posted, the value inside "specification" is a string, not a JSON object. There're two ways to do it properly:
1, format the JSON data correctly, i.e, instead of a string, the "specification" should also be a JSON object (array with dictionaries), depending on where you get this JSON data, you need to modify the generation logic of this data to accomplish this.
2, you can manually parse the specification using JSON parser, for example:
let error: NSErrorPointer = nil;
let specificationString = "[{\"label\":\"Model No\",\"value\":\"PR 1515\\t\\t\\r\"},{\"label\":\"\\nMotor\",\"value\":\"500W\\t\\r\"},{\"label\":\"\\nPad\",\"value\":\"00mm\\t\\r\"},{\"label\":\"\\nPower\",\"value\":\"30V AC 50Hz\\r\"},{\"label\":\"\\nBrush\",\"value\":\"50mm\\t\\r\"},{\"label\":\"\\nSpeed\",\"value\":\"50 rpm\\r\"},{\"label\":\"\\nVacuum\",\"value\":\"T130\\t\\r\"},{\"label\":\"\\nRange\",\"value\":\"2m\\t\\t\\r\"},{\"label\":\"\\nWeight\",\"value\":\"0 Kgs\\t\\r\"},{\"label\":\"\\nSize\",\"value\":\"185 x 580x 450mm\"}]"
if let specificationData = specificationString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) {
let specificationObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(specificationData, options: .MutableContainers, error: error)
//Now you have a correct Array, do whatever you want with it.
}

Swift custom Keyboard Error

I keep getting an Cannot convert expression's type '[NSObject : AnyObject]?' to 'NSDictionary' error and I don't know what to do. I tried everything, looked everywhere. Can you please help? I am creating a custom keyboard in SWIFT and I am totally new at this so i could definitely use the help.
// Called when `UIKeyboardWillShowNotification` is sent.
func keyboardWillShow(aNotification: NSNotification) {
let info = aNotification.userInfo as NSDictionary **<<<<<<<<<ERROR HERE>>>>>>>**
let sizeBegin = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size
let sizeEnd = info.objectForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue().size
let duration = info.objectForKey(UIKeyboardAnimationDurationUserInfoKey).doubleValue
let curve = info.objectForKey(UIKeyboardAnimationCurveUserInfoKey).integerValue
var animationCurve: UIViewAnimationCurve
if let value = UIViewAnimationCurve.fromRaw(curve) {
animationCurve = value
} else {
animationCurve = UIViewAnimationCurve.EaseInOut
}
let insets = UIEdgeInsets(top: 44, left: 0, bottom: sizeEnd.height, right: 0)
UIView.animateWithDuration(duration, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.textView.contentInset = insets
self.textView.scrollIndicatorInsets = insets
}, completion: nil)
}
// Called when `UIKeyboardWillHideNotification` is sent.
func keyboardWillHide(aNotification: NSNotification) {
let info = aNotification.userInfo as NSDictionary **<<<<<<<<<<ERROR HERE>>>>>>**
let sizeBegin = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size
let sizeEnd = info.objectForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue().size
let duration = info.objectForKey(UIKeyboardAnimationDurationUserInfoKey).doubleValue
let curve = info.objectForKey(UIKeyboardAnimationCurveUserInfoKey).integerValue
var animationCurve: UIViewAnimationCurve
if let value = UIViewAnimationCurve.fromRaw(curve) {
animationCurve = value
} else {
animationCurve = UIViewAnimationCurve.EaseInOut
}
let insets = UIEdgeInsets(top: 44, left: 0, bottom: sizeEnd.height, right: 0)
UIView.animateWithDuration(duration, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.textView.contentInset = insets
self.textView.scrollIndicatorInsets = insets
}, completion: nil)
}
Your dictionary is of type [NSObject:AnyObject]? which is an Optional type that must be unwrapped to be used. Since it is an Optional, it could be nil. One safe way of dealing with this is to use the nil coalescing operator ?? to unwrap it:
let info = (aNotification.userInfo ?? [:]) as NSDictionary
That will either unwrap your dictionary if it is not nil or give you a new empty dictionary if it is nil.

Resources