How to add a note on UIwebview - objective-c

I would like to implement a function like adding a note of a selected text on UIWebview as iBooks and Amazon Kindle does.
I already created UIMenuItem. But, I dont know how to implement the method for this. Could anybody help in this regard?
I dont know what functionality is used to implement in this method. Thanks.
- (void)Note:(id)sender {
NSString *selection = [webView stringByEvaluatingJavaScriptFromString:#"window.getSelection().toString()"];
}

The JavaScript in question is designed to give you the text that is highlighted within your selection.
I'm not sure that running toString on an element will return the correct information though. The following method will return the highlighted text and save it into a variable.
function getHighlightedString() {
var text = window.getSelection();
myAnchorOffset = text.anchorOffset;
myFocusOffset = text.focusOffset;
myHighlightLength=myFocusOffset-myAnchorOffset;
if(myHighlightLength<0)
{
myHighlightLength*=-1;
temp = myAnchorOffset;
myAnchorOffset = myFocusOffset;
myFocusOffset = temp;
}
selectedText = text.anchorNode.textContent.substr(myAnchorOffset, myFocusOffset - myAnchorOffset);
}
when you have this method loaded into the webview
NSString myHighlightedText = [webView stringByEvaluatingJavaScriptFromString:#"getHighlightedString()"];

Related

How to print contents of an HTML element in MacGap?

I'm creating a WebView app with MacGap2 and I'd like to be able to print the contents of an HTML element (with a print preview like you would see in Safari/Preview/etc).
I've been looking at WebUIDelegate's webView:printFrameView as well as Printing Entire Contents of WebView in Cocoa, Not Just Displayed - but I'm having a hard time putting it all together as I'm new to Objective-C/Cocoa.
If I wanted a method like this (options not necessary if print preview works):
MacGap.print([HTMLelement], [options]);
// Example usage
var el = document.getElementById('view');
// Or if not element, send as HTML string? (with inline CSS)
// el = el.innerHTML;
MacGap.print(el, { printBackgrounds: true, landscape: false });
What would I need to add to my MacGap Classes/Commands?
App.h:
- (void) print:(NSString*)printString;
App.m:
- (void) print:(NSString*)printString {
// ???
}
I dont have experience with MacGap2 and I tested this on a uiwebview not a webview, but I think it should still work to get the html string from the webview.
- (void) print:(NSString*)printString
{
NSString * pstrViewHTML= [myWebView stringByEvaluatingJavaScriptFromString:#"function getView(){return document.getElementById('view');} getView();"];
// Do something with html string.
}

How to hide the iOS 8 Today Widget when there is nothing to display?

I'm using the today widget for iOS8 to display information relevant to the current day. The problem is I don't want to display the widget/section at all if there are no relevant messages to show.
I know it must be possible as the BA app does it (it only shows the widget when there is a flight, the rest of the time its not visible at all). I just cant figure out a way to achieve this behaviour.
Does anyone know how this can be done?
I found the way to do this is using the NCWidgetController. This allows you to easily specify when the today widget should be displayed based on whatever criteria you see fit.
Simply add the following into your viewDidLoad method (or anywhere that will be called when the widget reloads) in the today widget view controller and it will work:
BOOL hasDataToDisplay = NO;
NCWidgetController *widgetController = [NCWidgetController widgetController];
[widgetController setHasContent:hasDataToDisplay forWidgetWithBundleIdentifier:#"com.my-company.my-app.my-widget"];
Apple Docs: https://developer.apple.com/library/ios/documentation/NotificationCenter/Reference/NCWidgetController_Class/index.html#//apple_ref/occ/cl/NCWidgetController
WARNING: The NCWidgetController cannot be reset from the widget itself once you have set there is no content to display. In other words once you set it to NO then there is no going back unless you trigger it from the parent/containing app.
In the widget's ViewController's viewDidLoad method add the following:
BOOL DisplayWidget = NO;
[[NCWidgetController widgetController] setHasContent:DisplayWidget
forWidgetWithBundleIdentifier:#"<widget's bunder identifier>"];
This will disable the widget from showing.
To enable it again, you must do that from the containing app using the same line passing YES to setHasContent parameter. Make sure to add the necessary imports to the containing app in the ViewController which will re-enable the widget:
#import <NotificationCenter/NotificationCenter.h>
#interface ViewController () <NCWidgetProviding> {...}
[Check out page 41 of the documentations for widgets
https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensibilityPG.pdf ]
The approach which I used, though not perfect and has a small remnant in Notification Center, but worked for me:
In viewDidLoad() set preferred content size height to 1:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
preferredContentSize = CGSizeMake(0, 1)
view.setNeedsLayout()
}
then when widget updates, gets real height and set it:
var data: NSData?
func updateData() {
// fetch data
if let data = data {
let viewHeight: CGFloat
// UI preperation and initialize viewHeight var
preferredContentSize = CGSizeMake(0, viewHeight);
} else {
preferredContentSize = CGSizeMake(0, 1);
}
}
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
updateData()
completionHandler(data != nil ? NCUpdateResult.NewData : NCUpdateResult.NoData)
}
It is better use
+ (instancetype)widgetController
then call
- (void)setHasContent:(BOOL)flag forWidgetWithBundleIdentifier:(NSString *)bundleID

How can I make an SLTextField tappable?

I have been writing a test with "Subliminal's" "SLTextField" and have run into some really annoying issues lately.
I have written a test which should update a text field, clear it, and update it again.
For some reason when I use "SLTextField" to change the element text I consistently receive errors stating that "SLTextFields" aren't tappable elements. Is this true? If so, what is the point of having an "SLTextField" class at all?
I can rewrite the same test code to find the element as an "SLElement", at which point I can tap the element, open up a keyboard object and type the necessary text, but it seems like I'm circumventing the entire functionality of Subliminal in doing this.
What's wrong w/"SLTextField"?
Code:
SLTextField *textField = [SLTextField elementWithAccessibilityLabel:fieldName];
SLWaitUntilTrue([textField isTappable], DEFAULT_TIMEOUT);
textField.text = newValue;
The above code throws an error, stating that "textField" never becomes tappable. Alternatively, the code below works perfectly, though it's unnecessarily verbose and seems to make "SLTextField" superfluous.
Code:
SLElement *field = [SLElement elementWithAccessibilityLabel:fieldName];
[field tapAtActivationPoint];
//fill with text
SLKeyboard *kb = [SLKeyboard keyboard];
SLKeyboardKey *deleteKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Delete"];
while(![field.value isEqualToString:#""]){
[deleteKey touchAndHoldWithDuration:1.2];
}
[kb typeString:newValue];
SLKeyboardKey *doneKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Next"];
if(![doneKey isValid]){
doneKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Done"];
}
[doneKey tap];
[kb hide];
Is your TextField within a TableViewCell? And are you seeing this on iOS 7?
If so, it might have been fixed by https://github.com/inkling/Subliminal/pull/202 (merged 6/6/2014).

NSPredicateEditor is clearing the value of its NSTextField

I'm using NSPredicateEditor in my project. One of my rows has 2 popup buttons and a text field on the right. If I have something typed in the text field, when I select a different menu item from either of the popup buttons then the text in the text field is deleted. That seems to be the default behavior and I don't want that text deleted. I've tried everything I can think of and can't seem to handle this. Any ideas how to change this behavior?
You could save the data by using NSUserDefault it will save and load the data (It won't delete your data unless the user does).
Access the text fields via the row template's templateViews property.
This allows you to get the custom values that have been entered into the textfield. Save the value when it changes.
class YourCustomRowTemplate : NSPredicateEditorRowTemplate {
func printTextFieldValues() {
let templateViews = super.templateViews
for view in templateViews {
if let textField = view as? NSTextField {
let text = textField.stringValue
print("Text in the texfield is: \(text)")
}
}
}
}
You can set the values the same way, by subclassing NSPredicateEditorRowTemplate and overriding the templateViews method.

JSFL: selecting items returned by fl.findObjectInDocByType()

I can't seem to use the info returned by fl.findObjectInDocByType() with fl.getDocumentDOM().selection.
I want to use document.setTextRectangle to re-size some text fields from an array generated using fl.findObjectInDocByType().
I can easily access all the textObject properties but since document.setTextRectangle requires a current selection, I am at a loss.
The example in the documentaion for setting selection is:
fl.getDocumentDOM().selection = fl.getDocumentDOM().getTimeline().layers[0].frames[0].elements[0];
fl.findObjectInDocByType() returns an array of objects with the attributes: (object.timeline, object.layer, object.frame, object.parent)
But these are objects, and don't have a property for array index numbers required by fl.getDocumentDOM().selection=...
var doc = fl.getDocumentDOM();
var textFieldArray = fl.findObjectInDocByType("text", doc);
for (var i=0; i < textFieldArray.length; i ++){
fnResizeTheTextField(textFieldArray[i]);
}
function fnResizeTheTextField(theTextField){
//force current selection to be theTextField
//doc.selection MUST be an array, so assign theTextField to an array...
var selectArray = new Array();
selectArray[0] = theTextField.obj;
var theTimeline =theTextField.timeline;
var theLayer =theTextField.layer;
var theFrame =theTextField.frame;
doc.currentTimeline =theTextField.timeline;
doc.selection = doc.getTimeline().theLayer.theFrame.selectArray;//error
//resize the text rectangle
doc.setTextRectangle({left:0, top:0, right:1000, bottom:1000});
}
}
Result: Error:doc.getTimeline().theLayer has no properties
It turns out, the ObjectFindAndSelect.jsfl script already contains a function specifically for this: fl.selectElement(). Much more elegant:
var doc = fl.getDocumentDOM();
// generate an array of elements of type "text"
var textFieldArray = fl.findObjectInDocByType("text", doc);
for (var i=0; i < textFieldArray.length; i ++){
fnResizeTheTextField(textFieldArray[i]);
}
function fnResizeTheTextField(theTextField){
//force current selection to be theTextField
fl.selectElement(theTextField,false);//enter 'edit mode' =false...
//resize the text rectangle
doc.setTextRectangle({left:0, top:0, right:1000, bottom:1000});
}
}
I found the answer. In order to select anything for a document level operation, you have to also make flash focus on the keyframe of that object.
so, if I loop through an array of objects created by fl.findObjectInDocByType(), I use this code to make flash focus on the object correctly:
function fnMakeFlashLookAt(theObject){
doc.currentTimeline =theObject.timeline;
doc.getTimeline().currentLayer =theObject.layer;
doc.getTimeline().currentFrame =theObject.frame;
}
this may not work on objects nested inside a symbol however.
I had a similar issue recently, and apparently all google results about setTextRectangle() direct us here. It's unbelievable how poorly documented jsfl is :)
If you need to use setTextRectangle() inside an library item that is not on stage, you need to open for edit the item first.
Here's the code that solved my problem:
library.selectItem(libraryItemName);
doc.selection = [tf];//where tf is the reference to textfield we need to edit
doc.library.editItem(libraryItemName);
doc.setTextRectangle({left:l, top:t, right:r, bottom:b});
doc.selectNone();
If you have a better working solution, please post. I hope it saves somebody's time. Good luck!