I am creating an application on MacOS and I am having this problem. I am using custom traffic buttons. When the window is not active if I click they do not respond, what I get is the window becoming active.
How could I fix that so the buttons will respond even if the window is not the active one.
I have them added to a custom view, here is what I have:
My View:
#implementation TrafficView
- (id)init {
if ((self = [super init])) {
trackingArea_.reset([[NSTrackingArea alloc]
initWithRect:[self bounds]
options:(NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways |
NSTrackingInVisibleRect)
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_.get()];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (BOOL)acceptsFirstResponder {
return YES;
}
- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
return YES;
}
- (void)mouseEntered:(NSEvent*)event {
mouseInside_ = YES;
for (NSView* view in self.subviews)
[view setNeedsDisplay:YES];
}
- (void)mouseExited:(NSEvent*)event {
mouseInside_ = NO;
for (NSView* view in self.subviews)
[view setNeedsDisplay:YES];
}
- (BOOL)_mouseInGroup:(NSButton *)button {
return mouseInside_;
}
#end
Now to add the buttons I do this in the window implementation (I will post the code for only one button)
trafficView_.reset([[TrafficView alloc] init]);
[self.frameView addSubview:trafficView_];
// close button is declared as NSButton
closeButton_ =
[NSWindow standardWindowButton:NSWindowCloseButton
forStyleMask:NSTitledWindowMask];
[trafficView_ addSubview:closeButton_];
Note that my custom view (trafficView_) is added to the NSThemeFrame class, self.frameView
- (NSView*)frameView {
return [[self contentView] superview];
}
I do not why the buttons do not respond.
Could someone help on this?
Thanks in advance
I have a launchViewController A and two other view controllers B and C. In the app, present sequence is A->B->C. I also have a need to dismiss C and directly back to A.
The problem is:
In iOS7, I call [self dismissViewControllerAnimated:false completion:^{}] in A. the functions viewWillAppear and viewDidAppear in B view controller will not be called.
But in iOS8, things are different. viewWillAppear and viewDidAppear will be called in B view controller. This results in a flashes of B's contents when dismiss.
Can anyone help me to find a way to fix this.
if you want to dismiss current ViewController in Which you are you should use
[self.presentingViewController dismissViewControllerAnimated:false completion:^{}]
instead of [self dismissViewControllerAnimated:false completion:^{}]
I fixed the issue by using this:
- (void)dismissDeepViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion {
if([UIDevice currentDevice].systemVersion.floatValue >= IOSCapVersion){
UIView *window = ((AppDelegate *)[UIApplication sharedApplication].delegate).window;
UIView *view = [window snapshotViewAfterScreenUpdates:false];
[((AppDelegate *)[UIApplication sharedApplication].delegate).window addSubview:view];
[self dismissViewControllerAnimated:false completion:^{
[UIView animateWithDuration:AnimationDurationDismissViewController animations:^{
view.alpha = 0;
}completion:^(BOOL finished) {
if (finished) {
[view removeFromSuperview];
}
}];
completion();
}];
}
else{
[self dismissViewControllerAnimated:flag completion:^{
completion();
}];
}
}
I have an NSBox subclass called dragBox. I want to be able to drag it around a canvas. The code is as follows:
-(void) awakeFromNib
{
[[self superview] registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}
-(void) mouseDown:(NSEvent *)theEvent
{
[self dragImage:[[NSImage alloc] initWithContentsOfFile:#"/Users/bruce/Desktop/Untitled-1.png"] at:NSMakePoint(32, 32) offset:NSMakeSize(0,0) event:theEvent pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] source:self slideBack:YES];
}
-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender // validate
{
NSLog(#"Updated");
return [sender draggingSourceOperationMask];
}
-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSLog(#"Drag Entered");
return [sender draggingSourceOperationMask];
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSLog(#"Move Box");
[self setFrameOrigin:[sender draggingLocation]];
return YES;
}
-(BOOL) prepareForDragOperation:(id<NSDraggingInfo>)sender
{NSLog(#"Prepared");
return YES;
}
Why isn't dragEntered being called? I have tried to use all the pboard types and such. Nothing seems to work. I have also changed the registerForDraggedTypes to just work off of the [self] view. The box is a subview of a canvas.
Bruce
I found that awakeFromNib was the wrong place to put my registerForDragTypes call since I am programmatically adding my view (i.e. not adding it via a Nib). I had to put the call into initWithFrame:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes: [NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
}
return self;
}
Bruce,
Your Code needs to be changed in the below way. I believe that view should be registered for drag types to make the method draggingEntered to get called.
#interface NSModifiedBox : NSBox
#end
#implementation NSModifiedBox
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
[super drawRect:dirtyRect];
}
- (NSDragOperation)draggingEntered:(id )sender
{
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
== NSDragOperationGeneric)
{
return NSDragOperationGeneric;
} // end if
// not a drag we can use
return NSDragOperationNone;
}
- (BOOL)prepareForDragOperation:(id )sender
{
return YES;
}
#end
Now Drag and Drop a NSBox on the Xib and the Modify the class of NSBox to NSModifiedBox.
Set a break point to the method "draggingEntered".
Now Drag a ".png" or ".gif" file and drop on the NSModifiedBox and you see the "draggingEntered" will get invoked
Or you can check by using NSLog as well inside a "draggingEntered".
Hope my answer will help you :)
UIBarButtonItem does not extend UIView, so there is nothing like a frame property.
But is there any way I can get what is it's CGRect frame, relative to the application UIWindow?
Do you like to use private APIs? If yes,
UIView* view = thatItem.view;
return [view convertRect:view.bounds toView:nil];
Of course no one wants this when targeting the AppStore. A more unreliable method, and also uses undocumented features, but will pass Apple's test, is to loop through the subviews to look for the corresponding button item.
NSMutableArray* buttons = [[NSMutableArray alloc] init];
for (UIControl* btn in theToolbarOrNavbar.subviews)
if ([btn isKindOfClass:[UIControl class]])
[buttons addObject:btn];
UIView* view = [buttons objectAtIndex:index];
[buttons release];
return [view convertRect:view.bounds toView:nil];
The index is the index to your bar item in the array of .items, after removing all blank items. This assumes the buttons are arranged in increasing order, which may not be. A more reliable method is to sort the buttons array in increasing .origin.x value. Of course this still assumes the bar button item must inherit the UIControl class, and are direct subviews of the toolbar/nav-bar, which again may not be.
As you can see, there are a lot of uncertainty when dealing with undocumented features. However, you just want to pop up something under the finger right? The UIBarButtonItem's .action can be a selector of the form:
-(void)buttonClicked:(UIBarButtonItem*)sender event:(UIEvent*)event;
note the event argument — you can obtain the position of touch with
[[event.allTouches anyObject] locationInView:theWindow]
or the button view with
[[event.allTouches anyObject] view]
Therefore, there's no need to iterate the subviews or use undocumented features for what you want to do.
I didn't see this option posted (which in my opinion is much simpler), so here it is:
UIView *barButtonView = [barButtonItem valueForKey:#"view"];
In iOS 3.2, there's a much easier way to show an Action Sheet popover from a toolbar button. Merely do something like this:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIActionSheet *popupSheet;
// Prepare your action sheet
[popupSheet showFromBarButtonItem:sender animated:YES];
}
This is the implementation I use for my WEPopover project: (https://github.com/werner77/WEPopover):
#implementation UIBarButtonItem(WEPopover)
- (CGRect)frameInView:(UIView *)v {
UIView *theView = self.customView;
if (!theView.superview && [self respondsToSelector:#selector(view)]) {
theView = [self performSelector:#selector(view)];
}
UIView *parentView = theView.superview;
NSArray *subviews = parentView.subviews;
NSUInteger indexOfView = [subviews indexOfObject:theView];
NSUInteger subviewCount = subviews.count;
if (subviewCount > 0 && indexOfView != NSNotFound) {
UIView *button = [parentView.subviews objectAtIndex:indexOfView];
return [button convertRect:button.bounds toView:v];
} else {
return CGRectZero;
}
}
#end
As long as UIBarButtonItem (and UITabBarItem) does not inherit from UIView—for historical reasons UIBarItem inherits from NSObject—this craziness continues (as of this writing, iOS 8.2 and counting ... )
The best answer in this thread is obviously #KennyTM's. Don't be silly and use the private API to find the view.
Here's a oneline Swift solution to get an origin.x sorted array (like Kenny's answer suggests):
let buttonFrames = myToolbar.subviews.filter({
$0 is UIControl
}).sorted({
$0.frame.origin.x < $1.frame.origin.x
}).map({
$0.convertRect($0.bounds, toView:nil)
})
The array is now origin.x sorted with the UIBarButtonItem frames.
(If you feel the need to read more about other people's struggles with UIBarButtonItem, I recommend Ash Furrow's blog post from 2012: Exploring UIBarButtonItem)
I was able to get Werner Altewischer's WEpopover to work by passing up the toolbar along with the
UIBarButton:
Mod is in WEPopoverController.m
- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item toolBar:(UIToolbar *)toolBar
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
self.currentUIControl = nil;
self.currentView = nil;
self.currentBarButtonItem = item;
self.currentArrowDirections = arrowDirections;
self.currentToolBar = toolBar;
UIView *v = [self keyView];
UIButton *button = nil;
for (UIView *subview in toolBar.subviews)
{
if ([[subview class].description isEqualToString:#"UIToolbarButton"])
{
for (id target in [(UIButton *)subview allTargets])
{
if (target == item)
{
button = (UIButton *)subview;
break;
}
}
if (button != nil) break;
}
}
CGRect rect = [button.superview convertRect:button.frame toView:v];
[self presentPopoverFromRect:rect inView:v permittedArrowDirections:arrowDirections animated:animated];
}
-(CGRect) getBarItemRc :(UIBarButtonItem *)item{
UIView *view = [item valueForKey:#"view"];
return [view frame];
}
You can get it from the UINavigationBar view. The navigationBar is a UIView which has 2 or 3 custom subviews for the parts on the bar.
If you know that the UIBarButtonItem is currently shown in the navbar on the right, you can get its frame from navbar's subviews array.
First you need the navigationBar which you can get from the navigationController which you can get from the UIViewController. Then find the right most subview:
UINavigationBar* navbar = curViewController.navigationController.navigationBar;
UIView* rightView = nil;
for (UIView* v in navbar.subviews) {
if (rightView==nil) {
rightView = v;
} else if (v.frame.origin.x > rightView.frame.origin.x) {
rightView = v; // this view is further right
}
}
// at this point rightView contains the right most subview of the navbar
I haven't compiled this code so YMMV.
This is not the best solution and from some point of view it's not right solution and we can't do like follow because we access to object inside UIBarBattonItem implicitly, but you can try to do something like:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[button setImage:[UIImage imageNamed:#"Menu_Icon"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(didPressitem) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = item;
CGPoint point = [self.view convertPoint:button.center fromView:(UIView *)self.navigationItem.rightBarButtonItem];
//this is like view because we use UIButton like "base" obj for
//UIBarButtonItem, but u should note that UIBarButtonItem base class
//is NSObject class not UIView class, for hiding warning we implicity
//cast UIBarButtonItem created with UIButton to UIView
NSLog(#"point %#", NSStringFromCGPoint(point));
as result i got next:
point {289, 22}
Before implement this code, be sure to call [window makeKeyAndVisible] in your Applition delegate application:didFinishLaunchingWithOptions: method!
- (void) someMethod
{
CGRect rect = [barButtonItem convertRect:barButtonItem.customview.bounds toView:[self keyView]];
}
- (UIView *)keyView {
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
if (w.subviews.count > 0) {
return [w.subviews objectAtIndex:0];
} else {
return w;
}
}
I handled it as follows:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIView* view = [sender valueForKey:#"view"]; //use KVO to return the view
CGRect rect = [view convertRect:view.bounds toView:self.view];
//do stuff with the rect
}
I am trying to create a simple framework with a nib that has a button on it which can be customized (selector wise and title wise.) for this, i did the following:
I added a property:
#property (nonatomic,retain) NSButton*accessoryButton;
and connected it to my outlet:
#synthesize accessoryButton = littleButton;
I then shared the instance as such:
+ (TestWindow *)sharedPanel
{
return sharedPanel ? sharedPanel : [[[self alloc] init] autorelease];
}
- (id)init
{
if (sharedPanel) {
[self dealloc];
} else {
sharedPanel = [super init];
}
return sharedPanel;
}
and load the nib:
if( !somewindow )
{
[NSBundle loadNibNamed: #"window" owner:nil];
}
[NSApp activateIgnoringOtherApps:YES];
[somewindow center];
[somewindow setLevel:NSModalPanelWindowLevel];
[somewindow makeKeyAndOrderFront:self];
When I however want to change the title for example from my sample project, it never works.
[TestWindow sharedPanel] setTitle:#"hi"]; //doesnt work
Here's my setTitle: method:
-(void)setTitle:(NSString *)buttonTitle
{
[[self accessoryButton] setTitle:buttonTitle];
[[self accessoryButton] display];
}
I don't get an error but nothing happens either. What am I missing?
Is the button nil at runtime? Are you sure your button's outlet is connected?
Does your init function get called when the NIB is loaded?