Is it possible to use UIDocumentPickerViewController to allow the user to select multiple files for import from another application?
Use following code snippet, this will add "Select" button beside the title, to select multiple files at once and import it.
UIDocumentPickerViewController *dvc = [[UIDocumentPickerViewController alloc]initWithDocumentTypes:arrContents inMode:UIDocumentPickerModeImport];
dvc.delegate = self;
[self presentViewController:dvc animated:true completion:^{
if (#available(iOS 11.0, *)) {
dvc.allowsMultipleSelection = true;
}
}];
and
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls
to get the list of selected files.
UIDocumentPickerViewController delegate can return only 1 file, not all:
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
So, unfortunately, it's not possible :(
Related
I have a WKWebView.
When the user right-clicks on it, I can customize a contextual menu in my objective-c method. I'd like to add a menu item only if the user has selected some text in the WKWebView. And of course I'll need to retrieve the selected text later on to process it.
How can I retrieve the selection from a WKWebView from objective-c, make sure it is only text and get that text ?
Thanks
Here is how I managed to do that. Not a perfect solution, but good enough.
General explanation
It seems that anything that happens inside the WKWebView must be managed in JavaScript. And Apple provides a framework for exchanging information between the JavaScript world and the Objective-C (or Swift) world. This framework is based on some messages being sent from the JavaScript world and caught in the Objective-C (or Swift) world via a message handler that can be installed in the WKWebView.
First step - Install the message handler
In the Objective-C (or Swift) world, define an object that will be responsible for receiving the messages from the JavaScript world. I used my view controller for that. The code below installs the view controller as a "user content controller" that will receive events named "newSelectionDetected" that can be sent from JavaScript
- (void)viewDidLoad
{
[super viewDidLoad];
// Add self as scriptMessageHandler of the webView
WKUserContentController *controller = self.webView.configuration.userContentController ;
[controller addScriptMessageHandler:self
name:#"newSelectionDetected"] ;
... the rest will come further down...
Second step - Install a JavaScript in the view
This JavaScript will detect selection change, and send the new selection through a message named "newSelectionDetected"
- (void) viewDidLoad
{
...See first part up there...
NSURL *scriptURL = .. URL to file DetectSelection.js...
NSString *scriptString = [NSString stringWithContentsOfURL:scriptURL
encoding:NSUTF8StringEncoding
error:NULL] ;
WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptString
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly:YES] ;
[controller addUserScript:script] ;
}
and the JavaScript:
function getSelectionAndSendMessage()
{
var txt = document.getSelection().toString() ;
window.webkit.messageHandlers.newSelectionDetected.postMessage(txt) ;
}
document.onmouseup = getSelectionAndSendMessage ;
document.onkeyup = getSelectionAndSendMessage ;
document.oncontextmenu = getSelectionAndSendMessage ;
Third step - receive and treat the event
Now, every time we have a mouse up or a key up in the WKWebView, the selection (possibly empty) will be caught and send to the Objective-C world through the message.
We just need a handler in the view controller to handle that message
- (void) userContentController:(WKUserContentController*)userContentController
didReceiveScriptMessage:(WKScriptMessage*)message
{
// A new selected text has been received
if ([message.body isKindOfClass:[NSString class]])
{
...Do whatever you want with message.body which is an NSString...
}
}
I made a class which inherits from WKWebView, and has a NSString property 'selectedText'. So what I do in this handler, is to store the received NSString in this property.
Fourth step - update the contextual menu
In my daughter class of WKWebView, I just override the willOpenMenu:WithEvent: method to add a menu item if selectedText is not empty.
- (void) willOpenMenu:(NSMenu*)menu withEvent:(NSEvent*)event
{
if ([self.selectedText length]>0)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:#"That works !!!"
action:#selector(myAction:)
keyEquivalent:#""] ;
item.target = self ;
[menu addItem:item] ;
}
}
- (IBAction) myAction:(id)sender
{
NSLog(#"tadaaaa !!!") ;
}
Now why isn't that ideal? Well, if your web page already sets onmouseup or onkeyup, I override that.
But as I said, good enough for me.
Edit: I added the document.oncontextmenu line in the JavaScript, that solved the strange selection behavior I sometimes had.
Swift 5 translation
webView.configuration.userContentController.add(self, name: "newSelectionDetected")
let scriptString = """
function getSelectionAndSendMessage()
{
var txt = document.getSelection().toString() ;
window.webkit.messageHandlers.newSelectionDetected.postMessage(txt);
}
document.onmouseup = getSelectionAndSendMessage;
document.onkeyup = getSelectionAndSendMessage;
document.oncontextmenu = getSelectionAndSendMessage;
"""
let script = WKUserScript(source: scriptString, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
webView.configuration.userContentController.addUserScript(script)
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// Use message.body here
}
One only needs to evaluate simple js script
NSString *script = #"window.getSelection().toString()";
using evaluateJavaScript method
[wkWebView evaluateJavaScript:script completionHandler:^(NSString *selectedString, NSError *error) {
}];
The Swift version
let script = "window.getSelection().toString()"
wkWebView.evaluateJavaScript(script) { selectedString, error in
}
I have working application for iOS7 and below
I used UISearchDisplayController for search in table.
Problem :
After search header view is not display in iOS8.
as display in below images.
Before search :
After search :
I tried using UISearchController but also have same problem
i used this code link
I add below code in TPSMastreViewController.m
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *v = [[UIView alloc] init];
v.backgroundColor = [UIColor greenColor];
return v;
}
I checked that delegate - (UIView *)tableView:(UITableView *)tableView
viewForHeaderInSection:(NSInteger)section
is not called in the case of iOS8.
Edit :
What i understand is only UITableViewDataSource delegate is called, UITableViewDelegate did not.Please not that i set both delegate in ViewDidLoad
Question :
1] Is it an UI change ?
2] Any one have patch so that delegate method will call forcefully.
I found answer so writing here it may help others facing same problem
just need to add delegate heightForHeaderInSection and it will show header view for both searchResultsController for UISearchController(iOS8) and searchResultsTableView for UISearchDisplayController(iOS7)
Add below code in TPSMastreViewController.m and it will solve problem.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 20;
}
I found my answer by reading this question :)
in iOS 8 UITableView heightForHeaderInSection is not optional
Greetings I have the following problem trying to set a datasource in an NSComboBox.
This is my custom datasource class:
#interface CComboDatasource : NSObject <NSComboBoxDataSource> {
#private
NSMutableArray* values;
}
#property (nonatomic,retain) NSMutableArray* values;
-(int)itemCount;
#end
#implementation CComboDatasource
#synthesize values;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
values=[[NSMutableArray alloc] init];
[values addObject:#"A"];
[values addObject:#"B"];
[values addObject:#"C"];
}
return self;
}
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
{
return [values count];
}
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
{
return [values objectAtIndex:index];
}
- (void)dealloc
{
[values release];
[super dealloc];
}
#end
Later in another file I connect my IBOutlet with my NSComboBox object (c_box) and I set the datasource (CComboDatasource* data_source).
[c_box setUsesDataSource:TRUE];
[c_box setDataSource:data_source];
[c_box setEditable:NO];
After the previous actions nothing is displayed in the combo box, what am I doing wrong?
What you have looks basically right to me. I can think of a few things you could try:
1) Try temporarily replacing "return [values count]" with "return 5" and replacing "return [values objectAtIndex:index]" with "return #"arbitraryString"". If "arbitraryString" then shows up in the combobox, you'll know the problem is with the "values" array.
2) Try initializing the "values" array like this:
values = [NSMutableArray array];
(It's a convenience method offered in NSArray.)
If you stick with an alloc-init method, you should make a separate temporary array that way, assign it to "values," then release it. Otherwise, since you've propertized "values" with "retain," you're retaining it twice.
3) Try adding this line at the end of your c_box calls:
[c_box reloadData];
And any time you change the data source array, call this again.
4) I don't see why separating the data source class from the class controlling the combobox should be a problem, but if it's still not working, try making the window/view controller that owns the combobox outlet the class that implements the NSComboBoxDataSource protocol (the numberOfItemsIn and objectValueFor methods), and either put "values" in this controller class or give this class access to "values."
Hope that helps.
Ok I found the problem ,in order by the custom datasource class to work u need
Create an NSObject and drag it to your editor
Change the type to your custom datasource class
Declare your Datasource as IBOutlet CustomDatasourceClass* myclass
Connect the Object with the previous outlet
Link your NScomboBox datasource (in IB designer) to the CustomDatasourceClass object
I have problem with comboBox:objectValueForItemAtIndex: because I have 10 combo box, every combo box I checking by:
if (aComboBox == _myCombo)
8 combo box works fine, but 2 not. I don't know what I'm doing wrong and why others work. I thinking about this problem about 2 weeks. I'm trying to delete and create new with different steps, but nothing help.
The solution is to reloadData before select option in awake from nib.
[_myCombo reloadData];
I have the following code to support dropping an app file into a table view. The problem is that I don't even see the green + when I drag and drop. I think it has something to do with the registerForDraggedTypes: but I'm not sure. I've tried many tutorials and none have worked for me.
- (void)awakeFromNib {
[apps registerForDraggedTypes:[NSArray arrayWithObject:#"app"]];
}
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
{
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
{
return NSDragOperationCopy;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
row:(int)row dropOperation:(NSTableViewDropOperation)operation
{
return YES;
}
Thanks in advance
registerForDraggedTypes isn't looking for an array of file extensions; it takes an array of uniform type identifiers. If you want to accept filenames, use the NSFilenamesPboardType:
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
Then, to only accept .app files, check the extension and return YES from tableView:acceptDrop:row:dropOperation:, getting the appropriate information from the NSDraggingInfo and its pasteboard.
Trying to make a simple link clicking activity work. I think I understand TTNavigator and TTStyledLabel, but can't get it to work.
Code:
#interface SomeVc : UIViewController <TTNavigatorDelegate> {
IBOutlet TTStyledTextLabel *styledTextLabel;
}
#end
#implementation SomeVc
- (void)viewDidLoad {
[super viewDidLoad];
navigator = [TTNavigator navigator];
navigator.persistenceMode = TTNavigatorPersistenceModeNone;
navigator.delegate = self;
TTURLMap* map = navigator.URLMap;
[map from:#"*" toViewController:[TTWebController class]];
styledTextLabel.text = [TTStyledText textWithURLs:someText];
[navigator openURLAction:[TTURLAction actionWithURLPath:#"http://www.cnn.com/"]];
}
- (BOOL)navigator: (TTNavigator*)navigator shouldOpenURL: (NSURL*)URL {
NSLog(#"trying to open %#", [URL absoluteString]);
return NO;
}
#end
I.e inside a viewcontroller, get the navigator and set self to be its delegate. When a link is opened, the shouldOpenURL delgate method gets called, where I will handle the URL opening myself. (I plan to let navigator handle more of it, but want to get this simple case working first.)
I have a test call at the end of viewDidLoad: which fires the delegate method fine.
Problem: I see the styledTextLabels rendered fine with URL-s, but when I tap on those, nothing happens. They don't reach the TTNavigator for some reason and I can't understand why. Feels like I'm missing some simple connection/scaffolding somewhere, but can't figure it out.
How to make it so that the links tapped in the styledtextlabel will reach the navigator delegate? Or how else should I implement this simple case with styledtextlabel? (just want to get callbacks for url taps.)
try setting the window property :
TTNavigator* navigator = [TTNavigator navigator];
navigator.window = window;
If you don't have one you can add one
navigator.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]
You might also need:
[navigator.window makeKeyAndVisible];