Customized UINavigationbar in Universal App - iphone

I have a Universal App in which I customize my UINavigationBar.
In my iPhone AppDelegate I use this to achieve it:
#implementation UINavigationBar (CustomImage)
static NSMutableDictionary *navigationBarImages = NULL;
- (void)initImageDictionary
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawing navbar2");
UIImage *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject: self]];
if (imageName==nil) {
imageName=[UIImage imageNamed:#"bg_titleBar.png"];
UIImage *image = imageName;
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
}
- (void)setMyImage:(UIImage*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject: self]];
[self setNeedsDisplay];
}
#end
Now my questions: why does this code get called, although I start the iPad simulator?
And more important it seems to corrupt the UIPopoverController because it looks like this:
http://awesome-apps.com/pic/ok.png
While it should look somehow like this:
http://awesome-apps.com/pic/nok.png
Besides it corrupts more in my App, but this should be it for starters :)
Can anyone help me with this? Have you ever had a similar experience?

So ignore the fact that it gets called when you run in the iPad simulator, because as you'll see in a minute even if you used two different categories (one for iPhone, one for iPad) you'd still have this problem.
Here's why:
You are using a category to override the UINavigationBar behaviour. I assume you know what that means - crucially any and all navigation bars in your app will use your supplied methods in the category.
This can cause problems if you're using standard apple elements that use UINavigationBars - the exact thing you're seeing in the popover controller. What's happening is the UIPopoverController uses a UINavigationBar. But because you've defined a category, the app assumes you want the popover navbar to use that category as well.
So that's why you're seeing your weird behaviour in your pop-over controller.
As long as you use categories you'll have this problem, because you can't selectively tell the system which bars should use your category.
I'd suggest you tell us exactly you're trying to customise in the navbar, because there are other ways to achieve customisation outside of categories.

Related

UINavigationBar change tint color in Category

I know I can change the tint color of navigation bar in xib file easily. But the problem is that if i have to change in a lot of places.
Hence i tried using the (fancy) category and overriding the draw rect method.
#import "UINavigationBar+customTint.h"
#implementation UINavigationBar (customTint)
-(void)drawRect:(CGRect)rect{
self.tintColor = [UIColor redColor];
}
#end
I have imported it in the corresponding view controller as well. What am i doing wrong?
Whats the right way of doing it?
THanks in advance
That is one way of doing it. It would probably just be easier in you ViewController where ever you need to change it add the line:
self.navigationController.navigationBar.tintColor = [UIColor redColor];
Using a category for just one line is probably overkill.
EDIT:
If you do want to use a category, you may need to call setNeedsDisplay on the navigation bar. or override another method and call it.
Something like ,
[self.navigationController.navigationBar setNeedsDisplay];
Also according to Apple's documentation
In iOS 5, the UINavigationBar, UIToolbar, and UITabBar implementations have changed so that the drawRect: method is not called unless it is implemented in a subclass. Apps that have re-implemented drawRect: in a category on any of these classes will find that the drawRect: method isn't called. UIKit does link-checking to keep the method from being called in apps linked before iOS 5 but does not support this design on iOS 5 or later.
Apps can either:
Use the customization API for bars in iOS 5 and later, which is the preferred way.
Subclass UINavigationBar (or the other bar classes) and override drawRect: in the subclass.
The best way to therefore go about this would be to place this in your ApplicationDidFinishLaunching
NSString *reqSysVer = #"5.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
[[UINavigationBar appearance] setTintColor:myColor];
And also leave your DrawRect, so that it will work on iOS less than 5
If you're using iOS5 then the correct way to do it is to use the UIAppearance proxy.
[[UINavigationBar appearance] setTintColor:myColor];
Details here: https://developer.apple.com/library/ios/#documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html

iOS: Creating tip / help popups

Are there any built in, open source, or tutorials for creating a reusable easy to use popup for use with in game-help.
Essentially I would like to, on first run of a game, show popup tips / help that "point to" various on screen objects to help a user orient themselves with the game.
Update: Here is an example of how I ultimately want it to look / behave although I don't need it that generic but as close as possible would be good
I like those: https://github.com/chrismiles/CMPopTipView.
Nice and easy to set up.
Essentially what you need is a custom view.
You cannot use Apple's UIAlertView since its purpose is very different from what you are looking for.
I don't know what are your specific needs, but you may use a simple UILabel:
CGRect ref = objectToAddress.frame;
UILabel *tip = [[UILabel alloc] initWithFrame:CGRectMake(ref.x+ref.width,
ref.y+ref.height,
width,
height)];
[tip setText:messageToShow];
[self.view addSubview:tip];
[tip release];
where width and height are the dimensions of the tip you want to show and messageToShow is the message you want to display.
You can, of course, customize your UILabel as you like, changing font or background color. Check the reference for additional informations.
EDIT:
You may take a look at a possible popover implementation for iPhone: WEPopover. On the iPad you can use directly Apple's UIPopoverController
What I've done is to create two functions
- (void) showOverlay: (BOOL) show withMessage: (NSString*) message
{
if(show)
{
// I create or load a UIView with labels, etc, and with an alpha of 0.6/07
// give it a tag for later dismissal
overlay.tag = tag; // any arbitrary value
// add as subview
[self.view addSubview: overlay];
}
else
{
// hide the view
UIView *overlay = [self.view viewWithTag: tag];
[overlay removeFromSuperview];
}
}
Then I have a hide overlay function
- (void) hideOverlayInSecs: (NSInterval) time
{
[self performSelector: #selector(hideOverlay) withObject: nil afterDelay: time];
}
Then you can write a wrapper function to show / dismiss it for varying durations
[self showOverlay: YES withMessage: #"help tip"];
[self hideOverlayInSecs: 2];
In my App, the tips were fairly static, so I created an tip image using my favorite image editor, and then simply created a UIImageView with the tip image, and then added that as a subview to the current view, making sure to place it on top of other views.
It worked out pretty nicely, but again, my tips are fairly static.
If you want to display them only on the first run through, you'll need to create a BOOL that is saved in NSUserDefaults or something.
How about this?
I wrote this myself. It's pretty simple and probably what you are looking for.
Popup any UIView instance on top or bottom then disappear after a few seconds.
https://github.com/SaKKo/SKTipAlertView
Hope you find it useful. cheers,

UISlider subclass causing UI hang in only one UIView of universal app

I have been working on the front-end to a universal iOS app, setting up separate iPhone and iPad view controllers as subclasses of a XIB-less view controller (as described in http://www.kotancode.com/2011/04/05/ios-universal-apps/). Each subclassed view controller contains a device-specific XIB, with each XIB containing portrait and landscape UIViews which are switched upon orientation change (I followed http://aseriesoftubes.com/articles/ipad-development-101-orientation/ for this). The controls common to all four UIViews are synchronised with an IBAction method linked through IBOutletCollections.
Just today I have created a UISlider subclass for my own slider design, which appears to work fine, apart from one large issue: when testing with the iPhone in the simulator starting in portrait orientation the slider works fine; when switched to landscape it is also fine; however when I switch back to portrait the slider works for up to two operations and then freezes, taking some other controls of the view with it; stranger still, if I switch back to landscape the slider works fine! This happens only with the iPhone simulator, not the iPad.
I have tried switching IB's 'Continuous' option on and off, and tried both the 'Touch Up Inside' and 'Value Changed' connections from my slider's Sent Events, with no change (as it will be for a volume control I will need 'Value Changed' to work). My slider's implementation code is thus:
#implementation VolumeSlider
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super initWithCoder: decoder])) // Double paretheses to silence warning
{
// Instantiate slider images
UIImage *thumbImage = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"volume thumb" ofType: #"png"]];
UIImage *trackImage = [[UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"volume track" ofType: #"png"]] stretchableImageWithLeftCapWidth: 20 topCapHeight: 0];
// Set control elements with images
[self setThumbImage: thumbImage forState: UIControlStateNormal];
[self setMinimumTrackImage: trackImage forState: UIControlStateNormal];
[self setMaximumTrackImage: trackImage forState: UIControlStateNormal];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
The two action methods that the sliders trigger are:
- (IBAction)volumeChange:(id)sender
{
NSLog(#"VOLUME slider changing");
// VOLUME slider action
}
- (IBAction)volumeChangeFinish:(id)sender
{
NSLog(#"VOLUME change finished");
VolumeSlider *incomingVolume = sender;
for(VolumeSlider *volumeSlider in self.volumeSliders)
{
volumeSlider.value = incomingVolume.value;
}
}
(volumeChange will use the slider value to change volume in real time, volumeChangeFinished will update the other sliders in the view on touch inside up.)
This one has me stumped – it feels like a memory issue but I can't figure out exactly where. Does anyone have an idea what could be causing this?
(Mac OS 10.6.8, Xcode 4.0.2, building for iOS 4.3)
UPDATE ---
I have tried instantiating a new, stock UISlider into the iPhone view and this suffers the same problem, without having been connected up to anything. This points towards an issue with the view controller, or even some bug.
Although I have no definitive reason for the issue, I have managed to fix this. Basically I created a new UIView for the iPhone portrait orientation in Interface Builder, rebuilt the interface and connected it up in place of the dodgy one and the issue was gone. I don't know if I could have had a corrupted view in the XIB, that's the only answer I can proffer.

Changing UINavigationBar when interface orientation changes

I have a NavigationController with customized UINavigationBar:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"banner.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
I'm allowing rotation using shouldAutorotateToInterfaceOrientation.
When i'm rotating, i would like to change the banner.png with banner_port.png
How can i do it?
[UIDevice currentDevice].orientation will tell you which way the device thinks it is...
you can do an if or switch at that point on the value it gives you.
You can make your navigation bar object to listen global UIDeviceOrientationDidChangeNotification, which has property 'orientation', and make necessary changes, when event occurs.
See also Custom UINavigationBar Background
I agree wit with Dave DeLong - overriding drawRect isn't wise, it may break some day. Plus, your category's drawRect replaces the original drawRect -- you don't have the option of calling super to get default functionality.
Note that if you listen for changes to UIDeviceOrientationDidChangeNotification, you need to be aware that there are six device orientations (the usual four, plus face up and face down).
Poorly written code is often seen which doesn't acknowledge the possibility of face up and face down device orientations.
N.B. you can also access the user interface's current status bar orientation - which isn't necessarily the same as the UIDeviceOrientation!

iPhone - Is it possible to hide native scrollbar in UIWebView?

I would like to hide the native scrollbar / scroller that appears when you are scrolling a UIWebView, but still keep the scrolling functionality intact. Is this possible?
Thanks in advance,
William
It seems this question needs an updated answer:
You can directly access the scroll view associated with the web view. (read-only)
in iOS 5.0 an above.
I don't think developers should be supporting anything prior to iOS 5.0 unless in exceptional circumstances.
From the Apple developer docs.
#property(nonatomic, readonly, retain) UIScrollView *scrollView
Discussion
Your application can access the scroll view if it wants to customize the scrolling behavior of the web view.
Availability
Available in iOS 5.0 and later.
Declared In
UIWebView.h
Now you can directly write something like this:
webView.scrollView.showsHorizontalScrollIndicator = NO;
webView.scrollView.showsVerticalScrollIndicator = NO;
No need to go to the subviews of the webView.
UIWebView doesn't inherit directly from UIScrollView, but you may be able to use UIScrollView properties on the UIWebView subview:
[(UIScrollView*)[webview.subviews objectAtIndex:0] setShowsHorizontalScrollIndicator:NO];
[(UIScrollView*)[webview.subviews objectAtIndex:0] setShowsVerticalScrollIndicator:NO];
No idea if this is acceptable, but it builds okay and I think it should work. Please report back if this works for you.
Also consider filing a feature request to Apple at bugreport.apple.com to add this property to a future UIWebView implementation.
Do it in that way:
for (id subview in self.myWebView.subviews) {
if ([[subview class] isSubclassOfClass: [UIScrollView class]]) {
((UIScrollView *)subview).bounces = NO;
((UIScrollView *)subview).showsVerticalScrollIndicator = NO;
((UIScrollView *)subview).showsHorizontalScrollIndicator = NO;
}
}
In Swift :
webView.scrollView.showsHorizontalScrollIndicator = false
webView.scrollView.showsVerticalScrollIndicator = false
If someone is looking for swift solution then below is the code for swift 3.0
yourWebView.scrollView.showsHorizontalScrollIndicator = false
yourWebView.scrollView.showsVerticalScrollIndicator = false
There's seems to be the beginning of an answer here :
http://discussions.apple.com/thread.jspa?threadID=1781730
If you disable user interaction, this seems to remove the scrollbar (this may be ok if the web page you display does not exceed the screen height).
A kind of javascript hack seems to be described also but I'm not mastering it :/ (you need to have access to the web page you try to display however, and this may not be your case....)