How to position progress spinner inside the title bar of a Mac application - objective-c

I am trying to place a progress spinner at the right side of the the title bar of the window of my Mac OS X application, but I can't do that with the Interface Builder, as it doesn't let me drag the view inside it.
So, I tried to put it in the title bar programmatically, with the following code inside the applicationDidFinishLaunching method in AppDelegate.m:
loadingSpinner = [[NSProgressIndicator alloc] init];
[loadingSpinner setFrame:NSMakeRect(485, 0, 17, 17)];
[loadingSpinner setStyle:NSProgressIndicatorSpinningStyle];
NSView *titleBarView = [[_window standardWindowButton:NSWindowCloseButton] superview];
[titleBarView addSubview:loadingSpinner];
However, this is putting my progress spinner view at the bottom of the window instead of the title bar. It appears NSMakeRect() is positioning it relative to the bottom of the window, not the top.
If I change the second parameter of NSMakeRect (y position) to something like 370, it puts the loading spinner in the place I want it to be, but obviously when I resize the window vertically, it brings the progress spinner together to the bottom.
I've never seen something like this before. How can I fix that?
P.S.: Also, I don't know if there's a "more right" way to get the title bar view. As you can see, I'm using the superview of the close button view to get it, which seems a bit "dirty".

Randall Brown has some code to put a button in an NSWindow's title bar on his blog. You were on the right track. The superview of the close button is the entire window including the title bar. Its a little less hacky to get it with [[_window contentView] superview].

Related

Resize views when menubar autohides/appear in fullscreen mode

I'm working on an OSX app that supports a fullscreen mode.
The window is generated from a nib file but everything else is handled programmatically.
When I goes in fullscreen mode, my views resize properly but when the menu bar appear/disappear, setFrame don't get called for either the contentView or my own views. I'd to be notified
Is there a delegate to implement to catch those notifications? Or do I have to subclass NSWindow and find out how Safari handles its menu bar by reversing it?
It would be helpful to see some code, how exactly "your views resize properly".
But next info might help:
When a window goes fullscreen it occupies entire screen after the end of the fullscreen animation. The main menu bar shows over the window ("above" in sense of z-ordering). So when main menu bar shows/hides frame of your window and content view don't change.
Also note, that -[NSScreen visibleFrame] returns unoccupied frame. And it will not return whole screen frame until the end of the fullscreen animation.
After some researches, I could at least get ride of the dirty gray bar at the top of the screen by subclassing the window's content view and add the following code to the setFrame method, before call super:
//isFullscreen
if(([self.window styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask)
{
frameRect.size.height = self.window.frame.size.height;
frameRect.size.width = self.window.frame.size.width;
}
The window get resized to the screen size before setFrame get called, so we can use its size to update frameRect to window's size.

Xcode - content offset in scroller view

I have a bug I've been looking at for over two hours and still don't know why it happens.
I have a registration form on a scroller view, which scrolls down if they keyboard is in the way of the textfield (function called at Editing Did Start).
Here's the code segment where the bug happens:
CGPoint scrollerOffset = scroller.contentOffset;
UITextField *currentTextField = sender;
CGPoint textFieldOrigin = currentTextField.frame.origin;
if (scrollerOffset.y < textFieldOrigin.y) {
scrollerOffset.y = textFieldOrigin.y - currentTextField.frame.size.height;
NSLog(#"Offset: %f",scrollerOffset.y);
[scroller setContentOffset:scrollerOffset animated:YES];
}
Now here's where the fun starts.
If I have the simulator like this and click inside the Phone Nr field, NSLog shows 590.000 for scrollerOffset.y and the text field jumps all the way to the top of the scrollview, as it should, just as the image next to it shows.
BUT if I have the simulator like this and click inside the Phone Nr field, NSLog shows 590.000 again but the scrollview doesn't jump up to show the text field...
Edit: If I don't animate the scrolling, it works perfectly, only bugs out when it's animated.
Well I've found the solution after a day of thinking, so if anyone has the same problem, heres's the solution:
Solution from here: http://www.pressingquestion.com/1128098/Disable-Uiscrollview-Scrolling-When-Uitextfield-Becomes-First-Responder
Basically, iOS has an in-built scroller, which is also scrolling for you if the text field is off the screen (usually the center of the text field is off the screen, off the screen being out of view, if it's hidden by the keyboard, it's still considered to be on the screen). So I called my function, did the scrolling, then iOS did his scrolling and messed me up. So you need to disable iOS' in-built scrolling.

Resized UIToolbar refuses to keep its UIBarButtonItem aligned correctly

I have a UIToolbar at the top of my view, and it needs to be resized in an animation. The toolbar contains:
A UIBarButtonItem using system item UIBarButtonSystemItemAdd (the '+' button)
A UIBarButtonItem using a custom view (the title)
A UIBarButtonItem using the style UIBarButtonItemStyleBordered (the 'Edit' button)
I am adding these buttons using a spacer between each, which keeps the title in the center:
[toolbar setItems: #[addButton, spacer, titleButton, spacer, editButton] animated:NO];
The toolbar resizes just fine, and the two buttons keep their locations pinned to the outside edges. However, the title button does not stay in the center of the toolbar. Instead, it seems to pin its right edge to the same location, creating a space on its left side. During the animation, this give the impression that it is sliding right.
To be clear, I do want the title bar to keep its same width - I do not want it to expand as the toolbar grows. But I need the title to stay in the center of the toolbar.
Since a UIBarButtonItemis not a UIView, I can't (?) use the autoresizingmask functionality.
How do I keep the title in the center of the toolbar?
Additional Info
This might be because the Add and Edit buttons have different widths - when I add only the title (with a spacer either side), the behavior is correct.
It turns out that I was leaving out a crucial detail. I am manually setting the width of the edit button to 50.
If this is removed, the resizing works as expected. However, I am then unable to control the size of the Edit button, which I like to set to 50 so it matches the one on the navigation bar.
I found a workable solution. Keep the width of the Edit button at 50. Add a fixed separator immediately after the '+' button, and set its width to 18 (50 - the width of the '+' button). This balances the items either side of the title.
I know it's too bad, still you can use custom button as bar button item and use images of Add/Edit button in it. So that you can set the button frames, at any places. So your button won't placed to right most place.
UIButton * addButton = [[UIButton alloc] init];
[addButton setBackgroundImage:<AddimageName> forState:UIControlStateNormal];
UIBarButtonItem * addItem = [[UIBarButtonItem alloc] initWithCustomView:addButton];

Rotating UITabBarController Icon

I have an UITabBar in my application. One of the tab bar icons looks like a loading symbol. When the user presses the loading button I want the icon to spin/rotate until the loading is done. Should I use UIImageView to animate or something else? How should I make this happen?
Jacos, unfortunately you cannot do that with the UITabBarController and manipulate the tabBarController's tabBar properties. My best bet would be that you use a UIToolBar and assign a black color and make it appear like a tabBar and have buttons added in them as a subView so that they look like tabBarItems.
Its much more customizable, and you can even provide a scrolling experience and add more buttons to it.
I know this question is 4 years old but I had the same problem and managed to fix it by reading the tutorial in here:
https://medium.com/#werry_paxman/bring-your-uitabbar-to-life-animating-uitabbaritem-images-with-swift-and-coregraphics-d3be75eb8d4d#.bjfpbdnut
The main point is to get the view for desired UITabBarItem and the get the UIImageView from it in viewDidLoad:
UIView *plusView = self.tabBar.subviews[1];
self.plusImageView = plusView.subviews.firstObject;
self.plusImageView.contentMode = UIViewContentModeCenter;
Then in didSelectItem method you can do this:
[UIView animateWithDuration:0.4 animations:^{
[self.plusImageView setTransform:CGAffineTransformMakeRotation(M_PI/4)];
}];
My code only rotate the image view for 45 degrees but you can change as you wish.
I guess you could change the UITabBarItem's icon on a timer, but that seems pretty kludgey. You would have to pre-render each frame of your "loading" icon rather than rotate an ImageView.
Another hackey solution would be to add your ImageView to the UIWindow and move it on top of the TabBarController's TabBar (adding it to the TabBar itself is asking for trouble).
You shouldn't try to animate the actual UIImageView within the UITabBarController. I would take this approach:
Set the image for the relevant tab to nil or a blank image.
Create a UIActivityIndicatorView and add it over the tab bar. Position it over the correct tab.
[self.tabBarController.tabBar addSubview:activityIndicatorView];
When your loading task has completed, restore the normal image to the tab and remove the activityIndicator from the tab bar.

Showing an image on my window's drag area... how can I make a click on the image still drag the window?

I'm using an NSImageView to display an image on an NSWindow. The image will be on top of the area where the user can normally drag the window.
The window is styled in such a way that it's not easily apparent that you can drag the window... I thus need this little image to indicate that you can in fact drag the window.
Unfortunately, I can't figure out how to make a click on the image drag the window! It needs this. Dragging anywhere else in the window's title area works fine.
How can I make clicking and holding the image drag the entire window?
Bonus points if you can tell me how to give the mouse a "drag icon" cursor while hovered on this image.
I think the easiest way is to subclass NSImageView and return YES to mouseDownCanMoveWindow (which it inherits from NSView).
-(BOOL)mouseDownCanMoveWindow {
return YES;
}
You can set a new cursor when the mouse enters the image view by overriding resetCursorRects like this in the NSImageView subclass (I don't know what you mean by "drag icon" cursor, so this example uses the pointing hand cursor:
- (void)resetCursorRects {
[self addCursorRect:self.bounds cursor:[NSCursor pointingHandCursor]];
}
NSImageViews have their own mouse handling. To change the way that view behaves, you will have to subclass, either NSView or NSImageView.