Fullscreen app in OS X with multiple windows? - objective-c

I want to make a fullscreen app that shows the background of the new space, in addition to having normal window behavior. Basically, when the user goes full screen, I want every NSWindow in the app to stay the same size, stay in the same position of the screen, but move to the new space. Is this possible? If so, where is the documentation for that kind of behavior?
EDIT: I know this is now quite old, but I have just discovered customWindowsToEnterFullScreenForWindow. I have used it to solve the problem. The code is below, just incase anyone else is interested in doing this too.
- (NSArray*) customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
if ([window isEqualTo:self.window]) {
return [NSArray arrayWithObjects:window, otherwindow, nil];
}
return nil;
}
- (NSArray*) customWindowsToExitFullScreenForWindow:(NSWindow*)window {
if ([window isEqualTo:self.window]) {
return [NSArray arrayWithObjects:window, otherwindow, nil];
}
return nil;
}
These are NSWindowDelegate methods though, so be sure to set the window's delegate. Otherwise, it may cause some confusion.

An app can only be on one space at a time. This is a hard limitation. You should file a bug if you want to be able to manage multiple windows on multiple spaces in fullscreen.

Related

Why NSApplicationDelegate method openFiles: is being called multiple times on a multiple drag to the dock icon?

I have a Mac OS X application that implements the -(void)application openFiles: method to react to dragged files on the application icon.
I have a list of allowed filetypes in the document types section of my target info settings and the Finder indeed allows drags, but when a PDF is in the list of dragged items, my delegate method is called twice: one for all the elements without the PDF, and one for the PDF alone.
This of course makes it impossible for me to handle the situation properly.
Can anybody help me or explain what is happening? Thanks
I've seen this behavior in one of my apps (usually when dragging a whole bunch of files at one time). As I workaround, instead of opening the files directly from application:openFiles:, I queue them up and open the queued files after a small delay. Something like the following:
- (void) application:(NSApplication*)sender openFiles:(NSArray*)filenames
{
// I saw cases in which dragging a bunch of files onto the app
// actually called application:openFiles several times, resulting
// in more than one window, with the dragged files split amongst them.
// This is lame. So we queue them up and open them all at once later.
[self queueFilesForOpening:filenames];
[NSApp replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
}
- (void) queueFilesForOpening:(NSArray*)filenames
{
[self.filesToOpen addObjectsFromArray:filenames];
[self performSelector:#selector(openQueuedFiles) withObject:nil afterDelay:0.25];
}
- (void) openQueuedFiles
{
if( self.filesToOpen.count == 0 ) return;
[self makeNewWindowWithFiles:self.filesToOpen];
[self.filesToOpen removeAllObjects];
}

How can I disable the touch events on two UIImageViews after i drag them into each other?

I have two draggable UIImageViews within a UIView. When i drag one UIImageView over the other, i would like to disable touch events to those two UIImageViews and create another UIImageView which does respond to my touch events. Ive tried using setUserInteractionEnabled: but it isnt really doing anything at all. Forgive me if this is a no brainer but i am new to programming, here is what i have so far. Please give me some feedback on my code and give me some constructive criticism because i feel as though i am setting this up all wrong.
-(void)swapImageViews
{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
[self.imgView1 setUserInteractionEnabled:NO];
[self.imgView2 setUserInteractionEnabled:NO];
}
}
So once again, the goal is to swap out two image views with a fresh one that i can also drag around.
The problem i am running into is that my touch events are still moving the first two image views around behind the third one.
You say that the third image appeared, but the the other two are still in the view and they still receive touch events. There are two two scenarios for this:
You've already added the third image view somewhere else, and your method is never called, or the condition is never met.
self.imgView1 and self.imgView2 are not set to the actual objects in your view stack. They're probably just nil. you can debug that by NSLog(#"%# %#", self.imgView1, self.imgView2); in that method, before the condition, or in viewWillAppear or viewDidLoad.
The reasons that support my opinion:
In that method, you call removeFromSuperview to the two image views you want to disable, yet they are still visible and receiving touch events. That means that this method is not executed. Which means they're nil or some other objects, that are different than the visible ones.
The condition in the start of the method is a bit strict. It's hard to be easily met. Yet the method is executed as the third image view is added to the view. This can happen when the object is nil, so the values returned by methods are the default values.
Check this link "the 10th item" and this one too.
Well this is not the best way to do that, but with the code that you provide to us, we have not a lot of options, you can do something like that, to remove the gestures:
-(void)swapImageViews{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
NSArray* gestures1 = [self.imgView1.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures1){
[self.imgView1 removeGestureRecognizer:gesture];
}
NSArray* gestures2 = [self.imgView2.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures2){
[self.imgView2 removeGestureRecognizer:gesture];
}
}
}

NSPopover - Hide when focus lost? (clicked outside of popover)

I'm using the doubleClickAction of a NSTableView to display a NSPopover. Something like this:
NSInteger selectedRow = [dataTableView clickedRow];
NSInteger selectedColumn = [dataTableView clickedColumn];
// If something was not selected, then we cannot display anything.
if(selectedRow < 0 || selectedColumn < 0)
{
NSLog(#"Invalid selected (%ld,%ld)", selectedRow, selectedColumn);
return;
} // End of something was not selected
// Setup our view controller, make sure if there was already a popover displayed, that we kill that one off first. Finally create and display our new popover.
DataInspectorViewController * controller =
[[DataInspectorViewController alloc] initWithNibName: #"DataInspectorViewController"
bundle: nil];
if(nil != dataPreviewPopover)
{
[dataPreviewPopover close];
} // End of popover was already visible
dataPreviewPopover = [[NSPopover alloc] init];
[dataPreviewPopover setContentSize:NSMakeSize(400.0f, 400.0f)];
[dataPreviewPopover setContentViewController:controller];
[dataPreviewPopover setAnimates:YES];
[dataPreviewPopover showRelativeToRect: [dataTableView frameOfCellAtColumn: selectedColumn row: selectedRow]
ofView: dataTableView
preferredEdge: NSMinYEdge];
Which works just fine. My popovers get created and removed on the cells that I double click on . The problem is, I want to the popover to go away if I click anywhere outside of it (like a single click on another cell). I have been looking around, but for the life of me cannot figure out how to do it.
This is something I would assume is built into a popover, (I'm fairly certain it was in the iOS UIPopoverController class) so I'm just wondering if im missing something simple.
You need to change the property behavior of your popover (in code or on interface builder) to:
popover.behavior = NSPopover.Behavior.transient;
NSPopover.Behavior.transient
The system will close the popover when the user interacts with a user interface element outside the popover.
Read more about this in Apple's documentation.
the .transient flag doesn't work for me.
However I can make things work by the following:
1) Whenever I show my popover I make sure I activate the app
(my app is a menu-bar app, so this doesn't happen automatically)
NSApp.activate(ignoringOtherApps: true)
2) When I click outside the app, then my app will be deactivated. I can detect this in the AppDelegate
func applicationWillResignActive(_ notification: Notification) {
print("resign active")
}
and act accordingly
After calling show(relativeTo:of:preferredEdge:) method,
Add below line
popover.contentViewController?.view.window?.makeKey()
And make sure you set
popover.behavior = .transient
Sorry, I've added solution in Swift.
While transient worked for most cases, it was an issue when the user interacted with elements outside of the application, as the popover would hide but not close.
What finally ended working for me was:
popover.behavior = .semitransient
Now the popover closes when changing app, or interacting with any other element outside of the app. But will not close when interacting with a NSMenu, and maybe won't close either with other interactions.
Quoting from the documentation for NSPopover.Behavior.semitransient:
The exact interactions that cause semi-transient popovers to close are not specified.
Similar to the documentation for NSPopover.Behavior.transient:
The exact interactions that will cause transient popovers to close are not specified.

Why won't my custom view become First-Responder, iOS?

I am following the book, iOS Programming Big Nerd Ranch Guide, and I have come to a lesson where I am to create a custom view, HypnosisView. Now, I am suppose to have this view change it's color on shake, but it says I am suppose to make it the First-Responder.
I used,
- (BOOL)canBecomeFirstResponder
{
return YES;
}
and
BOOL success = [view becomeFirstResponder];
if (success) {
NSLog(#"HypnosisView became the first responder"):
} else {
NSLog(#"Could not become first responder");
}
However, whenever I run my app, it always says that it could not become the first responder.
Any help would be appreciated.
UPDATE
I forgot to mention I get this output message.
Application windows are expected to have a root view controller at the end of application launch
Alright. I figured it out. I needed to put the delegate method
- (BOOL)canBecomeFirstResponder
{
return YES;
}
In the CustomView.m file, not my App Delegate file. Easy fix.

Is it possible to hide a button during a update process in Objective C?

I have the following problem:
I want that the user make a download when pressing a button. During this download, I want to hide the other buttons (which would open the downloaded files, so I want to ensure that no one tries to open files when the update haven't finished yet).
Is it possible to hide these buttons during this process?
So what I have tried and experienced so far:
Changes to the buttons I get always just at the end (when it isn't necessary anymore, because then the update is done).
I tried the following (Pseudocode):
-(void)updatingprogress
{
buttona.hidden=TRUE;
}
-(void)updatingfinished
{
buttona.hidden=FALSE;
}
updateFiles()
{
[self updatingprogress]
... make downloads...
[self updatingfinished]
}
So with logging I see, that I get in my functions at the moment I want, but the changes of the buttons aren't done during "updatingprogress". Any Idea how to solve this problem?
Thanks and best regards!
A common problem is that you are trying to update UI elements on a background thread. If your updateFiles method is happening on a different thread your button may not be hidden properly. To dispatch methods to the main threads you can either use the NSOperationQueue API or the GCD API.
NSOperationQueue:
[[NSOperationQueue mainQueue] addBlockOperation:^ {
buttona.hidden = YES;
}];
GCD:
dispatch_async(dispatch_get_main_queue(), ^ {
buttona.hidden = YES;
});
Both of these APIs do the same thing. I generally try to use the highest abstraction possible so in this case I would use the NSOperationQueue method
Another possibility is that you're doing all the work on the main thread, but failing to allow for the fact that, as a rule, UIKit changes don't take effect until you drop down to the runloop.
The background logic is that you don't want partial changes to be visible, so e.g. if you wrote:
// okay, set to success
label.textColor = [UIColor greenColor]; // was previously red
label.text = #"Success"; // previously said 'Failure'
What you explicitly don't want is for the word 'Failure' to appear in green, then for the word to change to 'Success'. You want the two changes to occur atomically. Apple achieve this by batching UIKit updates together and effecting them outside of any of your scheduled methods.
So if you have a function on the main thread that does some UI changes, does some work and then undoes the UI changes, but all without at any point exiting to the runloop, then the changes will never be seen.
The quickest solution would be:
- (void)updateFiles
{
[self updatingProgress];
[self performSelector:#selector(doFileUpdate) withObject:nil afterDelay:0.0];
// what does the above achieve? It schedules doFileUpdate on the runloop, to
// occur as soon as possible, but doesn't branch into it now. So when this
// method returns UIKit will update your display
}
- (void)doFileUpdate
{
/* heavy lifting here */
[self updatingFinished];
}