I'm making super-simple one-page browser in pure Objective-C, and everything is fine except the keyboard events. I just can't enter any data in webforms using keyboard! Mouse operations like pasting in webforms and clicking on links works, but typing in webforms doesn't!
code:
#import <WebKit/WebKit.h>
#interface MyWebView : WebView
{
}
#end
#implementation MyWebView
-(void)windowWillClose:(NSNotification *)notification
{
[NSApp terminate:self];
}
-(BOOL)becomeFirstResponder
{
printf("becomeFirstResponder\n"); // this message always appears
return YES;
}
-(void)keyDown:(NSEvent *)event
{
printf("keydown\n"); // this message never appears
// should I use this code to make keyboard work?
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
#end
int main(void) {
NSRect contentRect;
NSWindow *window = nil;
WebView *webView = nil;
NSString *urlForAuthentication = nil;
NSApp = [NSApplication sharedApplication];
contentRect = NSMakeRect(100, 100, 700, 700);
window = [[NSWindow alloc] initWithContentRect:contentRect styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];
if (!window) {
printf("!window\n");
goto end;
}
webView = [[MyWebView alloc] initWithFrame:window.contentLayoutRect frameName:nil groupName:nil];
if (!webView) {
printf("!webView\n");
goto end;
}
urlForAuthentication = [[NSString alloc] initWithCString:(const char *)"https://yahoo.com" encoding:NSUTF8StringEncoding];
[[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlForAuthentication]]];
window.contentView = webView;
window.delegate = (id)webView;
[window makeFirstResponder:webView];
[window makeKeyAndOrderFront:nil];
[window makeMainWindow];
if (!window.keyWindow)
printf("not keyWindow\n"); // this message always appears
if (!window.mainWindow)
printf("not mainWindow\n"); // this message always appears
[NSApp run];
end:
[urlForAuthentication release];
[webView release];
[window release];
[NSApp release];
return 0;
}
makefile:
CC=clang
FRAMEWORKS:=/System/Library/Frameworks/WebKit.framework/WebKit /System/Library/Frameworks/AppKit.framework/AppKit
LIBRARIES:=
WARNINGS=-Wall -Werror
SOURCE=app.m
CFLAGS=-g -v $(WARNINGS) $(SOURCE)
LDFLAGS=$(LIBRARIES) $(FRAMEWORKS) $(LINK_WITH)
OUT=-o app
all:
$(CC) $(CFLAGS) $(LDFLAGS) $(OUT)
What I'm doing wrong? I've read documentation and searched on SO for similar questions, but I couldn't find the solution. I tried to capture keyDown event, but it doesn't happens. I tried to make my window key and main, but seemingly unsuccessful.
To whom it may concern: place your executable 'abc' in folder 'abc.app/Contents/MacOS/', and it will finally work properly!
Edit: my colleague found out that this simple measure affects NSApplicationActivationPolicy. With this path, app's activationPolicy is 0 (NSApplicationActivationPolicyRegular). Without this path, app's activationPolicy is 2 (NSApplicationActivationPolicyProhibited).
Binary with some name should be placed inside "<Insert Name Here>.app" folder, which also should contain "Contents" folder. This way your executable pretends to be a bundled application for macOS.
Related
I've already looked at all the other entries for this question. I have been stuck for a while on a problem I have been unable to solve, any suggestion for an avenue of approach would be sincerely appreciated.
I have a moderately large app that allocs a window controller then loads a window using an XIB containing a single window which contains an AVPlayerView that was placed in the window. The AVPlayerView is wired to an IBOutlet called playerView. The window loads when it is supposed to and then an AVPlayer is created with a URL and is to be assigned to the player of the AVPlayerView, however I always find that the IBOutlet playerView is always nil at the point of trying to make the assignment. I cannot figure out why the IBOutlet is nil. Another anomaly is that the AVPlayerWindow will not become key when so ordered even though there are no other windows on the screen. I know for a fact that I have the outlet wired up in the XIB. Any thoughts?
There are 11,000 lines of code in the app. Here is the tiny portion that attempts to attach the player to the AVPlayerView.
fileURL = [self prepareStringAndBecomeAURL:filePath isDirectory:NO needFileURL:NO];
// thePlayer = [self prepareToPlay:fileURL];
thePlayer = [AVPlayer playerWithURL:fileURL];
if (thePlayer == nil) {
[self popUpErrorMessageWithoutCode:#"Unable to create a video player."];
} else {
playerView.player = thePlayer;
if (thePlayer != nil) {
[theMainWindow orderOut:self];
[self showMoviesWindow:self];
[self performSelector:#selector(continueHandleMovieSelected) withObject:nil afterDelay:1]; // wait for window to appear
}
}
}
- (void)continueHandleMovieSelected
{
[playerView.player play];
}
No, no exceptions.
Window controller and nib are created in another module, an AppController:
- (IBAction)showMoviePlayerWindow:(id)sender
{
if (![self myMoviePlayerWindowController]) {
myMoviePlayerWindowController = [[MoviePlayerWindowController alloc] initWithWindowNibName:#"MoviePlayer"];
[self set_myMoviePlayerWindowController:myMoviePlayerWindowController];
}
[[[self myMoviePlayerWindowController] window] center];
[[[self myMoviePlayerWindowController] window] makeKeyAndOrderFront:self];
[[self myMoviePlayerWindowController] showWindow:self];
}
I'm using a Cocoa WebView which loads an HTML page containing social media buttons. When I click the Facebook like button, no popup window is opened at all, however others such as Twitter and Google+ work fine and launch the popup window as expected.
That same HTML page works without issue in Safari.
I believe I've implemented my WebUIDelegate properly and can't think of what else might be causing this issue. Any ideas?
#pragma mark WebViewUIDelegate Methods
- (WebView *)webView:(WebView *) __unused sender createWebViewWithRequest:(NSURLRequest *)request
{
NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 300) styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing:NSBackingStoreBuffered defer:YES];
[window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
WebView *webView = [[WebView alloc] init];
[webView setFrameLoadDelegate:self];
window.contentView = webView;
[webView.mainFrame loadRequest:request];
[_popupWindows addObject:window];
return webView;
}
- (WebView *)webView:(WebView *)sender createWebViewModalDialogWithRequest:(NSURLRequest *)request
{
return [self webView:sender createWebViewWithRequest:request];
}
- (void)webViewRunModal:(WebView *)sender
{
[sender.window makeKeyAndOrderFront:self];
}
- (void)webViewShow:(WebView *)sender
{
[sender.window makeKeyAndOrderFront:self];
}
- (void)webViewClose:(WebView *)sender
{
[_popupWindows removeObject:sender.window];
[sender.window close];
}
Apparently I need to call makeKeyAndOrderFront: in webView:createWebViewWithRequest: ... the example here https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DisplayWebContent/Tasks/MultipleWindows.html#//apple_ref/doc/uid/20002026-117294 implies that this is necessary (as openUntitledDocumentAndDisplay:error: will call makeKeyAndOrderFront:) while the documentation for webViewShow: seems to imply that showing the window should be its responsibility exclusively. Confusing. If anyone has a better explanation, it's welcome.
I have an Objective C application which starts with loading a window created with Interface Builder:
//in main()
[NSApplication sharedApplication];
[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
[NSApp run];
In MainMenu.xib I have a window with a button. I want to create programmatically the second window when that button is pressed.
//in MainMenu.xib Controller.h
#class SecondWindowController;
#interface Controller: NSWindowController {
#private
SecondWindowController *sw;
}
- (IBAction)onButtonPress:(id)object;
#end
//in MainMenu.xib Controller.m
#import "SecondWindowController.h"
#implementation Controller
- (IBAction)onButtonPress:(id)object {
sw = [[SecondWindowController alloc] initWithWindowNibName:#"SecondWindow"];
[sw showWindow:self];
[[self window] orderOut:self];
}
#end
Where SecondWindowController inherits from NSWindowController. In SecondWindowController.h I have:
- (id)initWithWindow:(NSWindow*)window {
self = [super initWithWindow:window];
if (self) {
NSRect window_rect = { {custom_height1, custom_width1},
{custom_height2, custom_width2} };
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
}
return self;
}
And in the SecondWindow.xib I have nothing. When the button of the first window is pressed the first window disappears and the application closes. The reason I don't want to use the Interface builder for the second window is that I want to programmatically initialize it. Is this possible and if so what is the right way to accomplish this?
OK, I was initially confused with your use of initWithWindowNibName:#"SecondWindow" which will attempt to load the window from a NIB file, which you later mention you don't want to do.
Please use this to create your window:
- (IBAction)onButtonPress:(id)object {
if (!sw)
sw = [[SecondWindowController alloc] init];
[sw showWindow:self];
[[self window] orderOut:self];
}
Which will avoid creating multiple copies of the window controller, which you don't want (if you do then you'll need to store them in an array). Note the name sw is incorrect by convention; use either _sw or create setter/getter methods and use self.sw.
Initialize SecondWindowController like this:
- (id)init {
NSRect window_rect = NSMakeRect(custom_x, custom_y,
custom_width, custom_height);
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
self = [super initWithWindow:secondWindow];
if (self) {
// other stuff
}
return self;
}
Note: your variable names for origin/size of the new window were wrong; please review them.
Usually I am making iOS app but now I am trying to make an OS X app, and I am lost at the very beginning. Say the style I make the iOS apps are totally programmatic, there's no xib files or whatsoever just because that I have a lot more control by typing than dragging. However in OS X programming, it starts with some xib files with the menu items and a default window. There are quite a lot of items in the menu items so that's probably not something I want to mess around, but I want to programmatically create my first window myself.
So I did this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSUInteger windowStyleMask = NSTitledWindowMask|NSResizableWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask;
NSWindow* appWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, 1280, 720) styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO];
appWindow.backgroundColor = [NSColor lightGrayColor];
appWindow.minSize = NSMakeSize(1280, 720);
appWindow.title = #"Sample Window";
[appWindow makeKeyAndOrderFront:self];
_appWindowController = [[AppWindowController alloc] initWithWindow:appWindow];
[_appWindowController showWindow:self];
}
So here, I have created a window first, and use that windowController to init this window. The window does show up in this way, but I can only specify the inner elements, like buttons and labels here, but not in the windowController. It makes me feel bad so I tried another way.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_appWindowController = [[AppWindowController alloc] init];
[_appWindowController showWindow:self];
}
and after this I want to set the other elements in the loadWindow: function in the windowController like this:
- (void)loadWindow
{
[self.window setFrame:NSMakeRect(200, 200, 1280, 720) display:YES];
self.window.title = #"Sample window";
self.window.backgroundColor = [NSColor lightGrayColor];
NSButton* sampleButton = [[NSButton alloc] initWithFrame:NSRectFromCGRect(CGRectMake(100, 100, 200, 23))];
sampleButton.title = #"Sample Button!";
[sampleButton setButtonType:NSMomentaryLightButton];
[sampleButton setBezelStyle:NSRoundedBezelStyle];
[self.window.contentView addSubview:sampleButton];
NSLog(#"Loaded window!");
[self.window makeKeyAndOrderFront:nil];
}
Unfortunately, this never works. the loadWindow: never gets called, nor windowDidLoad:. Where did they go?
And please don't ask why I don't use nibs. I wish to make some highly customized views inside, possibly OpenGL, so I don't think nibs can handle it. I am greatly appreciated if anyone could help. Thanks.
And also, who knows how to even start the menu items from scratch, programmatically?
I am using the latest Xcode.
I spent an entire Sunday digging into this problem myself. Like the person asking the question, I prefer coding iOS and OSX without nib files (mostly) or Interface Builder and to go bare metal. I DO use NSConstraints though. It is probably NOT WORTH avoiding IB if you're doing simpler UIs, however when you get into a more complex UI it gets harder.
It turns out to be fairly simple to do, and for the benefit of the "Community" I thought I'd post a concise up to date answer here. There ARE some older Blog Posts out there and the one I found most useful were the ones from Lap Cat Software. 'Tip O The Hat' to you sir!
This Assumes ARC. Modify your main() to look something like this:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char *argv[])
{
NSArray *tl;
NSApplication *application = [NSApplication sharedApplication];
[[NSBundle mainBundle] loadNibNamed:#"MainMenu" owner:application topLevelObjects:&tl];
AppDelegate *applicationDelegate = [[AppDelegate alloc] init]; // Instantiate App delegate
[application setDelegate:applicationDelegate]; // Assign delegate to the NSApplication
[application run]; // Call the Apps Run method
return 0; // App Never gets here.
}
You'll note that there is still a Nib (xib) in there. This is for the main menu only. As it turns out even today (2014) apparently no way to easily set the position 0 menu item. That's the one with the title = to your App name. You can set everything to the right of it using [NSApplication setMainMenu] but not that one. So I opted to keep the MainMenu Nib created by Xcode in new projects, and strip it down to just the position 0 item. I think that is a fair compromise and something I can live with. One brief plug for UI Sanity... when you're creating Menus please follow the same basic pattern as other Mac OSX Apps.
Next modify the AppDelegate to look something like this:
-(id)init
{
if(self = [super init]) {
NSRect contentSize = NSMakeRect(500.0, 500.0, 1000.0, 1000.0);
NSUInteger windowStyleMask = NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
window.backgroundColor = [NSColor whiteColor];
window.title = #"MyBareMetalApp";
// Setup Preference Menu Action/Target on MainMenu
NSMenu *mm = [NSApp mainMenu];
NSMenuItem *myBareMetalAppItem = [mm itemAtIndex:0];
NSMenu *subMenu = [myBareMetalAppItem submenu];
NSMenuItem *prefMenu = [subMenu itemWithTag:100];
prefMenu.target = self;
prefMenu.action = #selector(showPreferencesMenu:);
// Create a view
view = [[NSTabView alloc] initWithFrame:CGRectMake(0, 0, 700, 700)];
}
return self;
}
-(IBAction)showPreferencesMenu:(id)sender
{
[NSApp runModalForWindow:[[PreferencesWindow alloc] initWithAppFrame:window.frame]];
}
-(void)applicationWillFinishLaunching:(NSNotification *)notification
{
[window setContentView:view]; // Hook the view up to the window
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
[window makeKeyAndOrderFront:self]; // Show the window
}
And Bingo... you're good to go! You can start working from there in the AppDelegate pretty much like you're familiar with. Hope that helps!
UPDATE: I don't create menus in code anymore as I've shown above. I've discovered you can edit MainMenu.xib source in Xcode 6.1. Works nice, very flexible and all it takes is a little experimentation to see how it works. Faster than messing around in code and easy to localize! See the picture to understand what I am on about:
See https://github.com/sindresorhus/touch-bar-simulator/blob/master/Touch%20Bar%20Simulator/main.swift
In main.swift
let app = NSApplication.shared()
let delegate = AppDelegate()
app.delegate = delegate
app.run()
Swift 4:
// File main.swift
autoreleasepool {
// Even if we loading application manually we need to setup `Info.plist` key:
// <key>NSPrincipalClass</key>
// <string>NSApplication</string>
// Otherwise Application will be loaded in `low resolution` mode.
let app = Application.shared
app.setActivationPolicy(.regular)
app.run()
}
// File Application.swift
public class Application: NSApplication {
private lazy var mainWindowController = MainWindowController()
private lazy var mainAppMenu = MainMenu()
override init() {
super.init()
delegate = self
mainMenu = mainAppMenu
}
public required init?(coder: NSCoder) {
super.init(coder: coder) // This will newer called.
}
}
extension Application: NSApplicationDelegate {
public func applicationDidFinishLaunching(_ aNotification: Notification) {
mainWindowController.showWindow(nil)
}
}
Override the -init method in your AppWindowController class to create the window and then call super's -initWithWindow: method (which is NSWindowController's designated initializer) with that window.
But I generally agree with the comments that there's little reason to avoid NIBs.
I am developing a iPhone application which sends tweets to twitter. For this I am using SA_OAuthTwitterEngine + MGTwitterEngine classes.
I register applicaiton to www.twitter.com/apps and pass Consumer key and Consumer secret to controller my code is this.
if(!_engine){
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];
_engine.consumerKey = slPcFUjUh5y1hex0zvEhPg;
_engine.consumerSecret = u6ydovMdP9yeiVqDukVhIzZPgJR9XDPUwfxymzNs;
}
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];
if (controller){
[self presentModalViewController: controller animated: YES];
intTwitterFlag = 1;
}
Previously on twitter.com/apps I select Application type = client and my application will generate PIN and accessToken. But when i change my Application type = Browser it cannot generate PIN and accessToken.
Previously when application type is client i am giving user name and password and then control return to my application from the webview but now after entering user name and password it cannot dismissModalViewController but showing Select and Copy the PIN.
Thank you for your time and any help you can give me!
Here it is:just replace the method in SA_OAuthTwitterController.m:
- (void) webViewDidFinishLoad: (UIWebView *) webView {
_loading = NO;
//[self performInjection];
if (_firstLoad) {
[_webView performSelector: #selector(stringByEvaluatingJavaScriptFromString:) withObject: #"window.scrollBy(0,200)" afterDelay: 0];
_firstLoad = NO;
} else {
/*
NSString *authPin = [self locateAuthPinInWebView: webView];
NSLog(#"authPin: %#", authPin);
if (authPin.length) {
[self gotPin: authPin];
return;
}
NSString *formCount = [webView stringByEvaluatingJavaScriptFromString: #"document.forms.length"];
if ([formCount isEqualToString: #"0"]) {
[self showPinCopyPrompt];
}*/
//*****************************************************
// This is to bypass the pin requirement
// in case the call back URL is set in Twitter settings
//*****************************************************
[_engine requestAccessToken];
if ([_delegate respondsToSelector: #selector(OAuthTwitterController:authenticatedWithUsername:)])
{
[_delegate OAuthTwitterController: self authenticatedWithUsername: _engine.username];
}
[self performSelector: #selector(dismissModalViewControllerAnimated:) withObject: (id) kCFBooleanTrue afterDelay: 1.0];
//[self dismissModalViewControllerAnimated:YES];
}
[UIView beginAnimations: nil context: nil];
_blockerView.alpha = 0.0;
[UIView commitAnimations];
if ([_webView isLoading]) {
_webView.alpha = 0.0;
} else {
_webView.alpha = 1.0;
}
}
I too faced the same issue, and i just removed callback url from the twitter app settings. For my surprise login proceeds without any issue.
The replacement for this method
(void) webViewDidFinishLoad: (UIWebView *) webView
in the class SA_OAuthTwitterController.m works well.
Its better to use xAuth route for mobile apps
http://dev.twitter.com/pages/xauth
check XAuthTwitterEngine which implements xauth for the MGTwitterEngine