Set aspectRatio of the main window on MacOS - objective-c

I need to make main window to preserve width/height ratio when the user resizes it on MacOS.
My first idea was to handle the following notification:
#interface Override_iOS : NSObject
#end
__strong Override_iOS *_instance;
#implementation Override_iOS
+(void)load
{
NSLog(#"[Override_iOS load]");
_instance = [Override_iOS new];
[[NSNotificationCenter defaultCenter] addObserver:_instance
selector:#selector(applicationDidFinishLaunching:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
}
-(void)applicationDidFinishLaunching:(NSNotification*) notification
{
NSLog(#"[Override_iOS applicationDidFinishLaunching:%#]", notification);
//qDebug() << "applicationDidFinishLaunching handler";
}
#end
and try to set something like aspectRatio property of the main window, but looks like that this code would not work on MacOS, but only on iOS (at least in my app).
Is it possible to do something like this on MacOS?
Is there an option in XCode project?
EDIT1
I need something like this:
- (void)awakeFromNib {
// Keep the aspect ratio constant at its current value
[window setAspectRatio:window.frame.size];
}
but it is not clear where to add this.
EDIT1
Created a test app:

I have done very little with MacOS apps, but this may be what you're looking for...
Create a NSWindow subclass:
MyWindowController.h
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyWindowController : NSWindowController
#end
NS_ASSUME_NONNULL_END
MyWindowController.m
#import "MyWindowController.h"
#interface MyWindowController ()
#end
#implementation MyWindowController
- (void)windowDidLoad {
[super windowDidLoad];
// set the aspect ratio here
[self.window setAspectRatio:NSMakeSize(320.0, 640.0)];
}
#end
Then assign the custom class of your window controller:

Related

NSPanel not showing.

My approach to this may be all wrong so I appreciate your patience.
I have a button in my main XIB file linked to this method in my document.m file:
- (IBAction)showTagModal:(id)sender {
if (!_FileTagWindowController){
_FileTagWindowController = [[FileTagWindowController alloc]init];
}
[_FileTagWindowController showWindow:self];
}
_FileTagWindowController is declared as a property in document.h and using breakpoints when the method is called, as far as I can tell is initializing properly, however _windowNibName and _window remains nil.
FileTagWindowController.h looks like this.
#import <Cocoa/Cocoa.h>
#interface FileTagWindowController : NSWindowController{
}
#property (strong) IBOutlet NSArrayController *tagsArray;
- (IBAction)saveContext:(id)sender;
#end
FileTagWindowController.m looks like this:
#import "FileTagWindowController.h"
#interface FileTagWindowController ()
#end
#implementation FileTagWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"Window Did Load!");
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (IBAction)saveContext:(id)sender {
}
#end
in my FileTagWindowController.xib I have File Owner set to FileTagWindowController as the custom class. I have the File Owner's "window" outlet linked to the window (NSPanel). That's all that should be required correct? The NSLOG statement in WindowDidLoad never gets called. I tried using [super initWithWindowNibName] in FileTagWindowController.m but that crashes not only the app, but Xcode as well with an endless initialization loop. Am I missing something obvious here?
Thanks all so much.
Try something like the following.
// document.h
#import "FileTagWindowController.h"
#property (strong) filetagWindowController *FileTagWindowController;
// document.m
#synthesize filetagWindowController;
- (IBAction)showTagModal:(id)sender {
if (self.filetagWindowController == nil) {
self.filetagWindowController = [[FileTagWindowController alloc] initWithWindowNibName:#"FileTagWindowController"];
}
[filetagWindowController showWindow:self];
[[filetagWindowController window] setReleasedWhenClosed:NO];
[NSApp runModalForWindow:filetagWindowController.window];
filetagWindowController = nil;
}
You may also want to call NSWindowWillCloseNotification to observe its state and see if filetagWindowController is closed.

Objective-C delegate function not working correctly

I have an app that has three views. All three views have an Ad Banner at the bottom of the screen. The first view creates an audio streamer which is paused when the Ad Banner on this view is clicked. I'm trying to use the AdBanner delegate methods on the second view to stop/start the audio. When the Ad Banner is selected the AdBanner delegate methods should call my custom delegate functions. The code compiles and runs but doesn't function correctly.
Using NSLog I've determined that the Ad Banner is calling its delegate function correctly but this isn't calling the custom delegate.
Hope this makes sense. Any help would be appreciated. Here is my code.
SecondViewControler H-file
#import "SecondViewController.h"
#protocol demoViewControllerDelegate <NSObject>
#required
-(void)stopSent;
-(void)startSent;
#end
#interface SecondViewController ()
{
id<demoViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<demoViewControllerDelegate> delegate;
#end
SecondViewController M-file
#implementation SecondViewController
#synthesize delegate;
Protocols
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave {
[delegate stopSent];
return YES;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner{
[delegate startSent];
}
FirstViewController H-file
#import <QuartzCore/QuartzCore.h>
#import "iAd/iAd.h"
#import <MessageUI/MessageUI.h>
#import "AudioStreamer.h"
#import "Reachability.h"
#import "SecondViewController.h"
#import "MFAppDelegate.h"
#import "MFSideMenu.h"
Class secondViewConroller;
#interface DemoViewController : UIViewController <ADBannerViewDelegate,demoViewControllerDelegate> {
}
#end
FirstViewController M-file
-(void)stopSent{
if (isPlaying) {
[streamer stop];
wasPlaying=true;
}
}
-(void)startSent{
if (wasPlaying) {
[streamer start];
isPlaying=true;
}
}
Your protocol methods need to be implemented in the class that you've designated as your delegate target.
It looks like your DemoViewController (or FirstViewController) is the object you've designated as the delegate, since you've given the interface the "<ADBannerViewDelegate,demoViewControllerDelegate>" designations.
Then, from your Second View Controller, you can call the object you designated and set as a delegate by doing:
[delegate startSent];
and
[delegate stopSent];
in the appropriate locations, which appear to be "bannerViewActionShouldBegin" and "bannerViewActionDidFinish", respectively.
You should also make sure that the delegate is properly set, therefore instead of:
[delegate startSent];
you should actually do this:
if(delegate)
[delegate startSent];
else
NSLog( #"delegate is null; we should figure out why" );

Calling view controller method from appdelegate doesn't work when using Storyboard

Good day everyone,
I am not able to call the method refresh in my ViewController when using a Storyboad. However it works if I create the project without the Storyboard, just a NIB. Why it doesn't work and how to overcome this issue?
Here is my code:
AppDelegate.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
ViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet ViewController *viewController;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
#synthesize window;
#synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
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.
NSLog(#"app will enter foreground");
[viewController refresh:NULL];
}
- (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
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
-(IBAction) refresh:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)loadData {
// contains information the ViewController makes use of
}
-(IBAction)refresh:(id) sender {
[self loadData];
}
#end
The viewController property in your app delegate are not populated when using a storyboard. Include the value in your log statement for confirmation - it will be null.
Register your view controller for the notification instead, and let it handle it's own refreshing.
In viewDidLoad:
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(refresh:)
name:UIApplicationDidBecomeActiveNotification
object: nil];
You'll want to remove this observer in dealloc / viewDidUnload as well.

Delegate - How to Use?

I think I understand the logic behind a delegate. I got more the problem to use it. How many steps are involved? Do I have to use existing delegates? Or can I use my one ones?
In my example I got the AppDelegate that created many views (Objects / View Controllers) of the same type. Each view should somehow call a method on the AppDelegate to close itself. This would happen when a button within the view is touched. The method call would include the reference of the view (self).
So far I know from other languages responders, event listeners and so on. They are so simple to use.
Can anybody help me. I just found massive examples with a lot of code in the web. It can't be that hard to just call a parent in Objective C.
I think you should use for this the NSNotificationCenter
in you AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(buttonPushed:) name:#"ButtonPushedNotification" object:nil];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
...
...
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
this is the selector it will be called when the notification happens (we are still in the AppDelegate.m)
- (void)buttonPushed:(NSNotification *)notification {
NSLog(#"the button pushed...");
}
and in the ViewController.m when the button pushed (inside the method), you should post a notification like this:
{
...
[[NSNotificationCenter defaultCenter] postNotificationName:#"ButtonPushedNotification" object:nil];
...
}
You can create your own:
In MyView1.h:
#class MyView1;
#protocol MyView1Delegate <NSObject>
- (void)closeMyView1:(MyView1 *)myView1;
#end
#interface MyView1 : NSObject
{
id<MyView1Delegate> _delegate;
}
#property (assign, nonatomic, readwrite) id<MyView1Delegate> delegate;
...
#end
In MyView1.m:
#interface MyView1
#synthesize delegate = _delegate;
...
// The method that tells the delegate to close me
- (void)closeMe
{
....
if ([_delegate respondsToSelector:#selector(closeMyView1:)])
{
[_delegate closeMyView1:self];
}
}
#end
In AppDelegate.h:
#import "MyView1.h"
#interface AppDelegate <MyView1Delegate>
{
MyView1 *_myView1;
}
...
#end
In AppDelegate.m:
- (void)someCreateViewMethod
{
_myView1 = [[MyView1 alloc] initWithFrame:NSMakeRect(0, 0, 100, 200)];
[_myView1 setDelegate:self];
...
}
An easy way to get what you want is to just start with one view. Then, have each other view be presented modally. When the button in the view is pressed do
[self dismissModalViewControllerAnimated:YES];
And here's something I made a while ago when I was starting iPhone development that might help you with delegates
Delegates
//In parent .m file:
//assign the delegate
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueName"])
{
childController *foo = segue.destinationViewController;
foo.delegate = self;
}
}
//implement protocol method(s):
- (void) methodName:(dataType*) dataName
{
//An example of what you could do if your data was an NSDate
buttonLabel.titleLabel.text = [[date description] substringToIndex:10];
}
//In parent .h file:
//import child header
#import "ChildName.h"
//indicate conformity with protocol
#interface ParentName : UIViewController <ChildNameDelegate>
//In child .h file
//declare protocol
#protocol ChildNameDelegate
- (void) methodName:(dataType*) dataName;
#end
//declare delegate
#property (unsafe_unretained, nonatomic) id<ChildNameDelegate> delegate;
//In child .m file
//synthesize delegate
#synthesize delegate;
//use method
- (IBAction)actionName:(id)sender
{
[delegate methodName:assignedData];
}

Connect NSImageView to view using IB?

I've only programmed on the iPhone so far, so Cocoa is sort of confusing in certain ways for me. Here's where I've hit a snag. I wanted my window so that the background was invisible, and without a title-bar. Something like this:
Here's how I'm doing it:
I set my window's class to a custom window, which I've created like this:
CustomWindow.h
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface CustomWindow : NSWindow {
#private
NSPoint initialLocation;
}
#property(assign)NSPoint initialLocation;
#end
CustomWindow.m
//trimmed to show important part
#import "CustomWindow.h"
#implementation CustomWindow
#synthesize initialLocation;
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
// Removes the window title bar
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
[self setAlphaValue:1.0];
[self setOpaque:NO];
}
return self;
}
#end
Now, in my .xib file for this window I've added a custom view onto the window. I've set the view class to a custom class I've created that inherits from NSView. Here's how I'm setting that up:
MainView.h
#import <Cocoa/Cocoa.h>
#interface MainView : NSView {
#private
//nothing to see here, add later
}
#end
MainView.m
//trimmed greatly again to show important part
#import "MainView.h"
#implementation MainView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(NSRect)rect {
// Clear the drawing rect.
[[NSColor clearColor] set];
NSRectFill([self frame]);
}
#end
So here's my question. I've added a NSImageView to my custom view (MainView) in Interface Builder. However, for some reason I can't figure out how to connect this image view to an instance variable in my custom view. They seem like they can't be connected like I normally would if I was creating an iPhone app. Any ideas how this would be done?
You connect objects created in your XIB in Mac OS X the same way you do for iOS programs. Just add an NSImageView property to your main view, mark it as an IBOutlet and connect it up.
For example,
In MainView.h create a property for your NSImageView and make it an IBOutlet:
#import <Cocoa/Cocoa.h>
#interface MainView : NSView {
NSImageView *imageView;
}
#property(retain) IBOutlet NSImageView *imageView;
#end
In interface builder, make sure the class for the custom view is set to MainView, to do this click on the File's Owner object in the custom view XIB and then select the identity option in the inspector and enter MainView as the class type.
Next, CTRL+click File's owner and drag the arrow to the NSImageView and select the imageView outlet.
That's all there is to it. You should be able to reference the image view from code now.