Is it possible for a layer-backed NSView to correctly do OpenGL rendering in drawRect:, or is it necessary to use NSOpenGLLayer/CAOpenGLLayer? Until recently, I haven't needed to worry about that, I just used a non-layer-backed view. But as stated in the AppKit release notes for 10.14:
Windows in apps linked against the macOS 10.14 SDK are displayed using Core Animation when the app is running in macOS 10.14.
To show how this breaks my rendering, I put together a sample app, with code shown below. If the project is run on an OS before Mojave, or is built using an SDK before 10.14, then the view shows solid red that stays that way if the window is resized. But if the project is build using the 10.14 SDK and run under macOS 10.14, then as the window is resized, the view flickers madly, and when you stop resizing, the view may or may not show anything.
Here's the project folder. Also, here is the app, built under High Sierra but with two instances of the view, one marked layer-backed in the nib.
-- AppDelegate.h --
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (strong) NSOpenGLPixelFormat* pixelFormat;
#end
-- AppDelegate.m --
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (instancetype) init
{
self = [super init];
if (self != nil)
{
NSOpenGLPixelFormatAttribute attribs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFAColorSize,
32,
0
};
_pixelFormat = [[NSOpenGLPixelFormat alloc]
initWithAttributes: attribs];
NSLog(#"Got pixel format %#", _pixelFormat);
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self.window makeKeyAndOrderFront: self];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
}
#end
-- MyGLView.h --
#import <Cocoa/Cocoa.h>
#class AppDelegate;
#interface MyGLView : NSView
#property (weak) IBOutlet AppDelegate* appDelegate;
#end
-- MyGLView.m --
#import "MyGLView.h"
#import "AppDelegate.h"
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#interface MyGLView ()
#property (strong) NSOpenGLContext* openGLContext;
#end
#implementation MyGLView
- (void) awakeFromNib
{
self.openGLContext = [[NSOpenGLContext alloc]
initWithFormat: self.appDelegate.pixelFormat shareContext: nil];
self.openGLContext.view = self;
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: #selector(resized:)
name: NSViewFrameDidChangeNotification
object: self];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
}
- (void) resized: (NSNotification*) note
{
[self.openGLContext update];
}
- (void) drawRect:(NSRect) dirtyRect
{
[self.openGLContext makeCurrentContext];
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
[self.openGLContext flushBuffer];
}
#end
Update:
It's looking as if what I wanted to do can't be done. So my choices are to use NSOpenGLView, which uses a private class _NSOpenGLViewBackingLayer for its layer, or use NSOpenGLLayer.
I find NSOpenGLLayer confusing because although it seems to be rendering on screen, it's actually rendering to an FBO. And even if you start with a pixel format that supports double-buffering, you're really doing single-buffered rendering. In contrast, NSOpenGLView appears to render to the main frame buffer, double-buffered if you like.
Related
This is my first try with IB_DESIGNABLE on Xcode.
I have this class to add color to a NSView.
header
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE #interface NSViewComCor : NSView
#property (nonatomic, weak) IBInspectable NSColor *backgroundColor;
#end
implementation
#import "NSViewComCor.h"
#implementation NSViewComCor
#synthesize backgroundColor = _backgroundColor;
- (void)awakeFromNib {
[super awakeFromNib];
[self setWantsLayer:YES];
self.backgroundColor = [NSColor whiteColor]; //default color
}
- (NSColor *) backgroundColor
{
CGColorRef colorRef = self.layer.backgroundColor;
return [NSColor colorWithCGColor:colorRef];
}
- (void) setBackgroundColor:(NSColor *)backgroundColor
{ // color should change when changed on interface builder inspectable color box
self.layer.backgroundColor = backgroundColor.CGColor;
_backgroundColor = backgroundColor;
}
even with IB_DESIGNABLE, this class does not render with the correct color on interface builder... why?
As the point out in WWDC 2014 "What's New in Interface Builder" video, you have to:
Create framework.
Create class.
Mark view as designable (and properties as inspectable).
Go back to the main project and specify the base class in Interface Builder.
For example, I added a new "framework" target to the project, and added the following NSView subclass source to that framework:
IB_DESIGNABLE #interface CustomView : NSView
#property (nonatomic, strong) IBInspectable NSColor *backgroundColor;
#end
and
#implementation CustomView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[self.backgroundColor setFill];
NSRectFill(dirtyRect);
}
#end
Then, when I went back to my main project and tried adding this CustomView as a subview on my storyboard, the "background color" was IB "inspectable" and I could see the changes in the color immediately rendered in IB.
I'm programming a App for iOS 8. I've a problem because i don't want use storyboard, I want to put a .xib file at the first time. Muy problem is that always, after splash be a black screen.
The Class who I want to put after Splash is LoginViewController
At general options, the "Main interface" is white (empty) (if put the name of the class, i've NSException (NSUknownException)).
In the .xib file i'have the files owner connected, and at the right side of the screen i've the name of the class at "Custom Class" (LoginViewController).
My appDelegate.h is: (I try with " #property (strong, nonatomic) LoginViewController *viewController;" too)
#import <UIKit/UIKit.h>
#import "LoginViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
My appDelegate.m is: (i try a lot of variants)
#import "AppDelegate.h"
#import "LoginViewController.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.viewController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navController.navigationBarHidden = YES;
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
The LoginViewController.h:
#import <UIKit/UIKit.h>
#interface LoginViewController : UIViewController
{
IBOutlet UILabel *labelPrueba;
IBOutlet UIButton *botonPrueba;
IBOutlet UIButton *boton2prueba;
IBOutlet UILabel *label2Prueba;
IBOutlet UILabel *dsfd;
IBOutlet UIButton *dfdf;
}
#end
The LoginViewController.m:
#import "LoginViewController.h"
#interface LoginViewController ()
#end
#implementation LoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
label2Prueba.text = #"laaaaaaaaaaaaa";
NSLog(#"Entra en viewDidLoad");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Someone can help me please. I'm going crazyyyyy.
Thanks a lot.
The solution was put this windows like init frame:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// That was the solution
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// That was the solution
self.viewController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navController.navigationBarHidden = YES;
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
Thanks
I've already spent 2 full days trying to figure out how to use NSViewControllers in order to create a multiview application.
Here is what I do.
I have 2 View Controllers and the MainMenu.xib's Window.
I also have an AppController that is the delegate for both View Controllers.
When I launch the app, I'm first greeted with the MainMenu.xib's Window's view which holds a button. On clicking this button, an IBAction is sent to the appController and asks for the SecondViewController to display it's nib. So far, everything's fine and the nib file is displayed correctly.
On the secondViewController, there's another button that sends another IBAction to the appController and asks for the FirstViewController to be displayed but nothing happens,
no crash, no warning... Any help would be much appreciated...
Thanks in advance for your patience...
Here is the code for the AppController.h :
#import <Foundation/Foundation.h>
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface AppController : NSObject
#property (strong) IBOutlet NSWindow *mainWindow;
#property (strong) IBOutlet SecondViewController *secondViewController;
#property (strong) IBOutlet FirstViewController *firstViewController;
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender;
- (IBAction)buttonClicked:(id)sender;
#end
and here is the code for the AppController.m :
#import "AppController.h"
#implementation AppController
#synthesize mainWindow = mainwindow;
#synthesize secondViewController;
#synthesize firstViewController;
- (IBAction)buttonClicked:(id)sender {
NSLog(#"button from second View Controller clicked");
self.secondViewController = [[SecondViewController
alloc]initWithNibName:#"SecondViewController" bundle:nil];
self.mainWindow.contentView = self.secondViewController.view;
[self.secondViewController.view setAutoresizingMask:NSViewWidthSizable |
NSViewHeightSizable];
}
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender {
NSLog(#"button from first ViewController clicked");
self.firstViewController = [[FirstViewController
alloc]initWithNibName:#"FirstViewController" bundle:nil];
self.mainWindow.contentView = [self.firstViewController view];
}
#end
Well, anyone can help me, I just need a single view application that displays a first ViewController with a button on the first viewController that takes me to a second view controller with a second button that takes me back to my first viewcontroller... I've already spent more than a week on that... in vain... PS : I don't want any button on the mainMenu.xib window nor tabs.
here is the solution to my question then.
Here's the code for the AppDelegate.h:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "FirstViewController.h"
#import "SecondViewController.h"
//We need to declare the AppDelegate class as being the delegate for both
//FirstViewController and SecondViewController
#interface AppDelegate : NSObject <NSApplicationDelegate,
FirstViewControllerDelegate, SecondViewControllerDelegate>
#property (strong, nonatomic) NSWindow *window;
#property (strong) FirstViewController *firstViewController;
#property (strong) SecondViewController *secondViewController;
-(void) goToSecondView;
-(void) goToFirstView;
#end
Now, here's the AppDelegate.m:
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize firstViewController;
#synthesize secondViewController;
-(void) awakeFromNib {
[self goToFirstView];
self.firstViewController.delegate = self;
}
-(void) goToSecondView {
if (self.secondViewController ==nil) {
self.secondViewController =[[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
}
self.window.contentView = [self.secondViewController view];
}
-(void) goToFirstView {
if (self.firstViewController ==nil) {
self.firstViewController =[[FirstViewController alloc]
initWithNibName:#"FirstViewController" bundle:nil];
}
self.window.contentView = [self.firstViewController view];
}
#end
Next we need to set delegates in the FirstViewController and the SecondViewController
// FirstViewController.h
#import <Cocoa/Cocoa.h>
#import "SecondViewController.h"
//We declare the delegation protocole:
#protocol FirstViewControllerDelegate <NSObject>
-(void)goToSecondView;
#end
#interface FirstViewController : NSViewController
- (IBAction)firstViewControllerButtonClicked:(id)sender;
#property (nonatomic, strong) id <FirstViewControllerDelegate> delegate;
#end
And here is the FirstViewController.m:
// FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)firstViewControllerButtonClicked:(id)sender {
NSLog(#"button from first View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToSecondView)]) {
[self.delegate goToSecondView];
}
}
#end
Now, same thing for the SecondViewController:
// SecondViewController.h
#import <Cocoa/Cocoa.h>
#protocol SecondViewControllerDelegate <NSObject>
-(void)goToFirstView;
#end
#interface SecondViewController : NSViewController
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender;
#end
And here's the SecondViewController.m:
// SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender {
NSLog(#"button from Second View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToFirstView)]) {
[self.delegate goToFirstView];
}
}
#end
Well, I guess this code may be improved and if you have any suggestion, feel free to let me know. Hope it will help others.
THE PROBLEM: When the user presses a button in View2, you want View1 to appear. It's not.
STEP 1: You say that the button should be invoking an action on your AppController. Set a breakpoint (or add a diagnostic log) in that action, just to verify that it is, in fact, being invoked.
STEP 2: Think about what you want that action to do, precisely. My guess is that you want to hide View2 and show View1. Perhaps
[view2 setHidden: YES];
[view1 setHidden: NO];
(I'm not using your names here, of course.) Or you might animate the transitions, either cross-fading the views or moving them.
STEP 3: My guess is that STEP 2 will solve your problem. If it doesn't, use the debugger again to verify that view1 and view2 are not null. (If they're null, you probably have weak variables where you need them to be strong.)
STEP 4: In the unlikely event that you're still stuck, check the frames of view1 and view2. Perhaps view1 isn't where you think it is.
STEP 5: If you're still stuck, check the alphaValue of view1. Maybe you set it to be transparent, and it's being drawn transparently in the right place.
STEP 6: I bet there is no step 6!
This isn't much of an answer at the moment, however I have some concerns about your code that I wanted to work through with you.
Are you sure you have connected the outlets and actions in Interface Builder. Please verify this.
You don't need mainWindow as there is already a window property that points to the main window (verify this in Interface Builder). Also this looks wrong:
#synthesize mainWindow = mainwindow;
^
W
So dump that and just use the existing window outlet provided by Xcode.
Don't re-create the view controllers if they already exist:
if (self.secondViewController == nil)
{
self.secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController"
bundle:nil];
}
self.window.contentView = self.secondViewController.view;
I'm quite a newbie to Cocoa & Mac programming. I have a WebView and I want to move it. Is there a way to change the coordinates of the WebView?
EDIT: Some relevant code:
NistractAppDelegate.m
#import "NistractAppDelegate.h"
#implementation NistractAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[window setAlphaValue:0];
[window setBackgroundColor:[NSColor colorWithCalibratedRed:0.125490196078431 green:0.125490196078431 blue:0.125490196078431 alpha:1]];
[mainWebView setBackgroundColor:[NSColor colorWithCalibratedRed:0.125490196078431 green:0.125490196078431 blue:0.125490196078431 alpha:1]];
[[mainWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
mainWebView.frame = NSMakeRect(mainWebView.frame.origin.x + 10, mainWebView.frame.origin.y, mainWebView.frame.size.width, mainWebView.frame.size.height);
[window makeKeyAndOrderFront:self];
[[window animator] setAlphaValue:1];
}
#end
NistractAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface NistractAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
IBOutlet NSView *mainView;
IBOutlet id mainWebView;
}
#property (assign) IBOutlet NSWindow *window;
#end
If you just want to move the entire webview you need set it's frame.
WebView.frame = NSMakeRect(x,y,width,height);
Eg if you want to just shift it left by ten pixels:
WebView.frame = NSMakeRect(WebView.frame.origin.x + 10, WebView.frame.origin.y, WebView.frame.size.width, WebView.frame.size.height);
When in doubt always you look in the documentation, which you can do from within side XCode, by selecting some text and right clicking then selecting Find Text In Documentation
Or use Google by searching "{Class name} class reference" Eg "nsview class reference"
Well, I'm learning how to program on a mac, and I'm tired of searching the for answers, can you guys explain me what I did wrong on my program.
First, I dragged 2 buttons on my window (Load Image, and Unload). Then, I dragged a custom view item and here`s what I did with the Test_Loading_ImagesAppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface Test_Loading_ImagesAppDelegate : NSView {
NSWindow *window;
IBOutlet NSView *mypicture;
}
- (IBAction)LoadImage: (id)sender;
- (IBAction)Unload: (id)sender;
#property (assign) IBOutlet NSWindow *window;
#end
After that, I linked up things on Interface Builder normally and made the Test_Loading_ImagesAppDelegate.m:
#import "Test_Loading_ImagesAppDelegate.h"
#implementation Test_Loading_ImagesAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
-(IBAction)LoadImage: (id) sender {
NSURL *myurl;
NSImage *image;
NSData *myurldata;
myurl = [NSURL URLWithString:#"http://....jpg"];
myurldata = [NSData dataWithContentsOfURL:myurl];
image = [NSImage initWithData:myurldata];
[mypicture setImage: image];
}
-(IBAction)Unload: (id) sender {
//Well, still thinking how I'm going to do this, and I would like to make another question: If I dealloc the View or something else the image will disappear?
}
#end
Thanks in advance.
Well, after coming here again I've found this http://www.markj.net/iphone-asynchronous-table-image/ it's for iPhone but can be adapted to OS X easily. Thanks.