Segment Bar in iOS accross different Size classes - ios

I'm using UISegmentController (default provided by iOS). I want to show text for iPad or iPhone landscape view. Where as in case of iPhone, I want to show icons (not text) in the same segment bar.
Is it possible? If yes, then how can I achieve this in story board or programmatically?

You can achieve this by adding UISegmentedControl programatically.
Code is as follows in controller viewWillAppear method,
- (void)viewWillAppear:(BOOL)animated {
UISegmentedControl * cntrl = [[UISegmentedControl alloc] initWithItems:#[[UIImage imageNamed:#"Circle"],#"2"]];
cntrl.frame = CGRectMake(30, 100, 200, 50);
[self.view addSubview:cntrl];
}
With above code we're creating segment control with one image in first segment and text in second segment. Check attached screenshot.
Use following code to check either device is iPad or iPhone
-(BOOL) isiPad {
return UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad;
}
Based on the type of the device, customise segment control.
For existing segment control, when you want to set image,
[cntrl setImage:[UIImage imageNamed:#"Tick"] forSegmentAtIndex:0];
use above method by specifying which image you want to set at particular segmentIndex.
At the same way when you want to set text at particular index, use
[cntrl setTitle:#"Hello" forSegmentAtIndex:0];

It doesnt seem possible to use the storyboard completely on its own to do this kind of functionality, but a combination of this and this inside your viewDidLoad might get you want you want

Related

Moving Tab Bar Controller in iOS [duplicate]

This question already has answers here:
Positioning UITabBar at the top
(9 answers)
Closed 7 years ago.
To preface, I just started iOS programming but do have a rather comprehensive background in Objective-C. So please explain what you are saying, but don't feel like you have to talk like a two year old.
I am building an application that uses a tabbed view controller. I want to move it to the top of the application like Facebook or Ask.fm do with their applications.
I know there have been other articles, but they are kind of old and I don't really understand them. So if you could provide some sample code with your answer that would be great.
All I want to do is move the bar, I don't want to create a new tabbed bar controller or something.
In iOS development, UITabBar is not for the top side. iOS divides each area in different sections, for example UINavigationBar is for top side part not for the footer same as UITabBar can only be use in footer according to Apple's recommendation.
You can use a custom view with the help of UIView and as many UIButtons in it as you want. Let's say you want to create top bar with two button so you can achieve this by doing this:
First, you have to hide UINavigationBar like this:
- (void)viewDidLoad {
[super viewDidLoad];
[[self navigationController] setNavigationBarHidden:YES animated:NO];
_topBar=[self createTopBar];
[self.View addSubview:_topBar];
}
after that you can create your own custom view like this:
- (UIView *)createTopBar {
UIView * topBar=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
UIButton * btnOne=[UIButton buttonWithType:UIButtonTypeCustom];
btnOne.frame=CGRectMake(0, 5, 34, 34);
[btnOne setBackgroundColor:[UIColor clearColor]];
[btnOne addTarget:self action:#selector(YourMethodOne) forControlEvents:UIControlEventTouchUpInside];
UIButton * btnTwo=[UIButton buttonWithType:UIButtonTypeCustom];
btnTwo.frame=CGRectMake(270, 5, 34, 34);
[btnTwo setBackgroundColor:[UIColor clearColor]];
[btnTwo addTarget:self action:#selector(YourMethodTwo) forControlEvents:UIControlEventTouchUpInside];
[topBar addSubview:btnOne];
[topBar addSubview:btnTwo];
}
-(void) YourMethodOne {
NSLog(#"First button Pressed");
//Do whatever you want when btnOne is pressed
}
-(void) YourMethodTwo {
NSLog(#"Second button Pressed");
//Do whatever you want when btnTwo is pressed
}
In this example I create a local method and a local variable. I prefer to create a proper class and your these button as public instance.
I am building an application that uses a tabbed view controller. I want to move it to the top of the application like Facebook or Ask.fm do with their applications.
You're confusing UITabBar and UITabBarController, which are entirely different classes. The former is a view, while the latter is a view controller, i.e. not a view at all. Also, perhaps because of that confusion, you're making assumptions about how other apps are implemented. Because the apps you mention put the tabs at the top, it's very unlikely that they use UITabBarController.
All I want to do is move the bar, I don't want to create a new tabbed bar controller or something.
UITabBarController manages an array of other view controllers that you provide, displaying view associated with the currently selected view controller, and it also manages a tab bar. How it lays out the views it manages (tab bar, selected view) is not customizable -- if you want a behavior that's different from what you get with UITabBarController, you need to use a different class.

How to properly utilize UISegmentedControl?

I am new to iOS development and would like to know the proper way to implement a UISegmentedControl.
I've dragged out the segment, dropped it where I want, changed its tint color via the attributes in the sidebar, then changed the text of each segment. I then held control and dragged to ViewController.m to create an outlet. Here's that function:
- (IBAction)touchSegment:(UISegmentedControl *)sender {
NSString *selectedSegmentTitle = [sender titleForSegmentAtIndex:sender.selectedSegmentIndex];
if ([selectedSegmentTitle isEqualToString:#"2 cards"]) {
NSLog(#"first tapped");
}
else if ([selectedSegmentTitle isEqualToString:#"3 cards"]) {
NSLog(#"second tapped");
}
}
This is intuitive, but it's not working properly. While the attributes in XCode all state I've set a custom tint color (selected the segment as a whole, then double clicked each individual one), only the left segment is tinted when launching the app - the border of the right is still the default blue. If in iOS Simulator I click the right segment then the color is changed to the correct set tint. I've also set which one should be selected by default in the sidebar, yet it doesn't show that in the storyboard. Not sure if I've done it right and these are minor bugs in XCode or if I've missed something or done it wrong in the first place.
Xcode storyboard:
iOS Simulator upon launch:
I'm running Xcode 5.0.2 and the iOS Simulator v7.0.3. Thanks!
That is the way that the tint color works in iOS 7. You can customize your control further by using the function
[control setBackgroundImage: forState: barMetrics:]
you could call it like so -
[control setBackgroundImage:unselectedBackImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[control setBackgroundImage:selectedBackgroundImage forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

iOS custom shape navigation bar

I want to developer app with a custom navigation bar like in the following images:
I think that i need to subclass UINavigationBar and add button to centre of nav bar, but i don't really know how to make navigation bar look like on image. Can you please give me advice what should i do, links to any kind of documentation would be awesome!
Similar questions about navBar that doesn't helped me:
ios back button in the bar
Use custom Navigation Bar in iOS
Custom Navigation Bar in iOS 5
rogcar
EDIT:
My idea is next: make custom navigation bar height little bigger than default size, and add background image with arrow in it and with some transparency on the edges.
If you want a button (you probably do want) you can achieve it completely by subclassing UINavigationBar. You should remember that height of UINavigationBar is read-only property.
Style but not tappable:
So let's assume we subclass the navigation bar and add button there. You could do this and it will be going look great. For example:
- (void)drawRect:(CGRect)rect
{
self.backgroundColor = [UIColor lightGrayColor];
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width/2-50, 0 , 100, 100)];
[myButton setBackgroundColor:[UIColor lightGrayColor]];
[myButton setTitle:#"Normal" forState:UIControlStateNormal];
[myButton setTitle:#"Highlighted" forState:UIControlStateHighlighted];
[self addSubview:myButton];
[self sendSubviewToBack:myButton];
}
But you will facing a problem that your button is non tapeable below UINvaigationBar. (I post an image on the bottom of the answer)
So there is clearly not a path you want to follow. Don't even try that.
Style but not tappable 2:
You may override this method in your navigation bar subclass
- (CGSize) sizeThatFits:(CGSize)size {
return CGSizeMake(custom_width, custom_height);
}
And then mask it using UIBezierPath for example
The right (tappable) way:
You have to create a view stick to your UINavigationBar. What i will do here (if you want it to every screen) is:
Make a Category of UIViewController which can draw (for example - this is easiest way) UIButton.
Style this 'UIButton' whatever you want (if you want
Pin action to 'UIButton': [btn addTarget:self action:#selector(menuShow:) forControlEvents:UIControlEventTouchUpInside];
menuShow: method should be declare in your category
You can call drawing button every time you want to redraw view controller.
As you can see there there will be two separates View: UINavigationBar and UIButton. This is allow you to set content under this little button and make it tapable.
So why just don't hide navigation bar, and use different view? Because iOS7 ;) When Apple change it in iOS7 for example then you have to rebuild your pseudo NavigationBar, with only additional view, you don't need to do anything.
You do not need to subclass UINavigationBar. Create UIView add to it UIImageView as background with image in the shape you need, add button.
Subclass UINavigationController hide UINavigationBar, add custom navigation bar.
First Hide navigation bar using -
self.navigationController.navigationBarHidden = YES;
Then create UIView with required height,height of navigationBar is 44px.Then create background image view, object of required UIButton and add all objects on created UIView as a subview.It will look like navigationBar.Thank you.
You can add your custom shaped view as titleView on the navigation bar.
Just make sure that clipsToBounds is set to NO, so it doesn't get clipped.

How to use the same [Storyboard] view for multiple tabs, and just change little things about it depending on the tab selected? (iOS)

I know this question is worded kind of strangely, but let me explain what I'm trying to do:
Let's say that I have an app with 2 tabs, each with their own view. Both views are almost exactly the same, except one contains a button or two that the other one doesn't, and vice versa.
I already made one view in Storyboard mode, now I want to change just a little tiny bit about it and reuse the same view for the other tab.
I don't want to do a deep copy, because if I change one, then I would have to change the other, and that might cause some inconsistency between the two.
So how should I go about using the same generic view for multiple tabs, and just change little things about it depending on which tab I am in?
Should I add the universal content in storyboard view, and then programmatically add the particular tab-exclusive content? If I did this, I would need a way to detect what tab is selected, right? Is there a way to do that?
Thank you for any suggestions!
You don't need a Storyboard for that. Sure you can drag a view in there and assign it to a specific class and program all stuff in that, but if you really need only one view in there the storyboard is useless. You should deal with subviews programmatically if you want to change something. This is very good and you have great features like animationWithDuration:
For checking which Tab in your UITabBarController is selected you can use self.tabBarController.selectedIndex and compare it with the given Index of the Tab which is selected.
EDIT:
If we assume you have a green Button on Tab with index 0, and the same Button should slide a little bit above and a red Button should appear where green Button when Tab with index 1 is tapped.
UIButton *greenButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 320, 100, 20)];
greenButton.backgroundColor = [UIColor greenColor];
[self.view addSubview:greenButton];
UIButton *redButton = [[UIButton alloc] initWithFrame:CGRectMake(0, greenButton.frame.origin.y, 100, 20)];
greenButton.backgroundColor = [UIColor redColor];
if (self.tabBarController.selectedIndex == 1) {
[UIView animateWithDuration:1.0 animations:^{
greenButton.frame = CGRectMake(0, 300, 100, 20);
} completion:^(BOOL finished) {
[self.view addSubview:redButton];
}];
}
First you create the two buttons and add the green one to the view. (The first view is at index 0). Then you check if the selectedIndex is 1 and animate the Buttons around.

UISegmentedControl does not respect divider images set for UIControlStateDisabled

I'm using the new UIAppearance API in iOS 5 to style a UISegmentedControl with custom graphics. I need to be able to set some segments to be disabled at times during execution, but the UIAppearance methods don't seem to allow me to set a divider image for the UIControlStateDisabled state.
I'm calling:
[[UISegmentedControl appearance] setDividerImage:disabledSelectedImage
forLeftSegmentState:UIControlStateDisabled
rightSegmentState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
where disabledSelectedImage is a resizable image from this resource:
Yet when I set the left segment to be disabled ([UISegmentedControl setEnabled:forSegmentAtIndex:]), the result is this:
You can clearly see that the UISegmentedControl has defaulted to use the UIControlStateNormal-UIControlStateNormal divider image.
It seems perfectly happy for me to set a background image using UIControlStateDisabled
[[UISegmentedControl appearance] setBackgroundImage:disabledImage
forState:UIControlStateDisabled
barMetrics:UIBarMetricsDefault];
(and respects the image I supply while in the disabled state) but not a divider image. Has anyone come across this or found a solution?
I've decided that this must be an iOS bug and have filed a radar with Apple. My solution to the problem for now is to remove segments, rather than disabling them.
A bit of an ugly workaround but i managed to fix it with the following until apple fixes it itself.
First you need to subclass UISegmentedControl and add the following:
#implementation MJSegmentedControl
- (void)layoutSubviews
{
[super layoutSubviews];
NSInteger cachedIndex = self.selectedSegmentIndex;
self.selectedSegmentIndex = 0;
self.selectedSegmentIndex = cachedIndex;
}
#end
I haven't had a need to use the appearance controls of iOS 5 yet but if all else fails you could add the resizable image as a child of the segmented control to cover up the ugliness. It's a hack, but it may work and will be relatively forwards-compatibile. Just be certain to set the autosizing masks appropriately.
I had the same issue and it really seems to be a bug. However I've found a solution (a workaround).
I've used XIB file with a controller. In XIB file segmented control was just placed and all of the customisations were done in -viewDidLoad method.
Then I've created a UIView subclass which represented entire view in the XIB. It made possible moving all view customisation code to the -awakeFromNib method of this UIView subclass. After moving this code the divider images were set properly.
As suggested by Fernando in this thread:
Customizing UISegmentedControl in iOS 5
You can try to dispatch your UISegmentedControl settings on the main queue via:
dispatch_async(dispatch_get_main_queue(),^{
// disable part of the segmented control
[self.eventScopeSegmentedControl setEnabled:NO forSegmentAtIndex:2];
});
I did this in viewDidLoad and it worked fine for a while but when my app is really busy at startup, this doesn't always work. I'm guessing there's a race condition that may still revert any settings you made when the appearance proxy goes to work.
I added another ugly hack to make this call in viewWillAppear (after the call to super:viewWillAppear) with a flag (set from viewWillLoad) to ensure this only runs once.
There is actually a pretty simple way to get this done. The current behavior is obviously a bug so this is not an ideal solution but simply a workaround that works beautifully. Namely, use an additional UIView as a "disabled visual cue".
The general steps:
Add a UIView as a sibling to the UISegmentedControl. Ensure the UIView is in front of the UISegmentedControl
Apply the desired color and a transparency to the UIView to match your app skin
Move the UIView to be exactly on top of the UISegmentedControl
Shape the UIView to have the exact size top of the UISegmentedControl
Apply a rounded corner to the UIView to mirror the exact shape of the UISegmentedControl
When the UISegmentedControl is supposed to be disabled, simply show the UIView and disable the user interaction on the UISegmentedControl.
When the UISegmentedControl is supposed to be enabled, simply hide the UIView and enable the user interaction on the UISegmentedControl.
In both cases do not change the UISegmentedControl.enabled property.
Note that it seems like a lot of steps but all of this can be coded in so to add support for disabling your custom UISegmentedControl becomes pretty much a 1 liner after you add this to a configure segmented control method.
Here is how my custom segmented control looks when applying this solution:
Enabled Segmented Control
"Disabled" Segmented Control
Here are some code snippets of interest:
Shape the UIView to match the UISegementedControl (load time configuration)
UISegmentedControl* segmentedControl = ...
//Segmented Control disabled visual cue view
UIView* view = ...
//Step #2
view.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6];
//Step #3 and #4
view.frame = segmentedControl.frame;
//Step #5
view.layer.cornerRadius = 5
view.clipsToBounds = YES;
//Ensure this is disabled by default
view.userInteractionEnabled = NO;
Enable/"Disable" UISegementedControl (runtime state change)
BOOL segmentedControlEnabled = ...
if(segmentedControlEnabled) {
segmentedControl.userInteractionEnabled = YES;
view.hidden = YES;
} else {
segmentedControl.userInteractionEnabled = NO;
view.hidden = NO;
}
That's it.
-

Resources