I have a navigationBar with both Left and Right bar buttons on each side. I have a customTitlelabel which I set as the titleView of the UINavigationItem.
[self.navigationItem setTitleView:customTitleLabel];
All is fine now. The problem, the size of the rightbarButton is dynamic based on the input I get in one of the text fields.
Therefore the title is automatically centered based on the available space between the buttons.
how can i set the title to a fixed position?
Setting the titleView property of the nav bar works just fine - no need to subclass or alter any frames other than those of your custom view.
The trick to getting it centered relative to the overall width of UINavigationBar is to:
set the width of your view according to the size of the text
set the alignment to centered and
set the autoresizingmask so it gets resized to the available space
Here's some example code that creates a custom titleView with a label which remains centred in UINavigationBar irrespective of orientation, left or right barbutton width:
self.title = #"My Centered Nav Title";
// Init views with rects with height and y pos
CGFloat titleHeight = self.navigationController.navigationBar.frame.size.height;
UIView *titleView = [[UIView alloc] initWithFrame:CGRectZero];
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
// Set font for sizing width
titleLabel.font = [UIFont boldSystemFontOfSize:20.f];
// Set the width of the views according to the text size
CGFloat desiredWidth = [self.title sizeWithFont:titleLabel.font
constrainedToSize:CGSizeMake([[UIScreen mainScreen] applicationFrame].size.width, titleLabel.frame.size.height)
lineBreakMode:UILineBreakModeCharacterWrap].width;
CGRect frame;
frame = titleLabel.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleLabel.frame = frame;
frame = titleView.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleView.frame = frame;
// Ensure text is on one line, centered and truncates if the bounds are restricted
titleLabel.numberOfLines = 1;
titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
titleLabel.textAlignment = NSTextAlignmentCenter;
// Use autoresizing to restrict the bounds to the area that the titleview allows
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
titleView.autoresizesSubviews = YES;
titleLabel.autoresizingMask = titleView.autoresizingMask;
// Set the text
titleLabel.text = self.title;
// Add as the nav bar's titleview
[titleView addSubview:titleLabel];
self.navigationItem.titleView = titleView;
You can't do what you want directly -- the position of your title view is out of your control (when managed by UINavigationBar).
However, there are at least two strategies to get the effect you want:
1) Add the title view not as the 'proper' title view of the nav bar, but as a subview of the UINavigationBar. (Note: this is not 'officially' sanctioned, but I've seen it done, and work. Obviously you have to watch out for your title label overwriting bits of the buttons, and handle different size nav bars for different orientations, etc. -- a bit fiddly.)
2) Make an intelligent UIView subclass that displays a given subview (which would be your UILabel) at a position calculated to effectively show the subview perfectly centered on the screen. In order to do this, your intelligent UIView subclass would respond to layout events (or frame property changes etc.) by changing the position (frame) of the label subview.
Personally, I like the idea of approach 2) the best.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.topItem?.title = ""
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationItem.title = "Make peace soon"
}
The right answer is to override sizeThatFits: of your custom titleView and return its content size. Navigation bar centers custom title view until it has no space left to do that.
For example if you have UIView container with UILabel inside:
#interface CustomTitleView : UIView
#property UILabel* textLabel;
#end
#implementation CustomTitleView
- (CGSize)sizeThatFits:(CGSize)size {
CGSize textSize = [self.textLabel sizeThatFits:size];
CGSize contentSize = size;
contentSize.width = MIN( size.width, textSize.width );
return contentSize;
}
#end
I tried aopsfan's answer but it didn't work. A breakpoint revealed that the bar's center was "(480.0, 22.0)" (The X coordinate way off) .
So I changed it into this:
- (void)layoutSubviews
{
[super layoutSubviews];
// Center Title View
UINavigationItem* item = [self topItem]; // (Current navigation item)
[item.titleView setCenter:CGPointMake(160.0, 22.0)];
// (...Hard-coded; Assuming portrait iPhone/iPod touch)
}
...and it works like a charm. The slide/fade effect when pushing view controllers is intact. (iOS 5.0)
I had similar problem.
My solution is do hide the original back button, add add your own implementation. Since the system will reserve space for the left items.
UIImage* cancelIcon = [UIImage imageNamed:#"ic_clear"];
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc] initWithImage:cancelIcon style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
and the selector is simple
- (void)back:(UIButton *) sender
{
[self.navigationController popViewControllerAnimated:YES];
}
now it looks like this:
oh...and don't forget to use autolayout in your custom title view if you have dynamic length content like label in it. I add an additional layout in the customview to give it like "wrap_content" in Android by setting it centered to parent , and leading and trailing space ">=" 0
I had a similar situation where a titleView should be centered in UINavigationBar. I like occulus's approach of subclassing a UIView and overriding setFrame:. Then, I can center the frame inside the dimensions of UINavigationBar.
In the UIView subclass:
-(void)setFrame:(CGRect)frame{
super.frame = CGRectMake(320 / 2 - 50, 44 / 2 - 15, 100, 30);
}
The UIView subclass can then be assigned normally to titleView for each navigationItem. The developer does not have to programmatically add and remove special subviews from UINavigationBar.
Related
I see that few apps are extending keyboard but I would like to know how they do it.
Here are 2 examples.
Textastic &
Prompt
Now I know that I can add inputAccessoryView to UITextView but it still has small thin line that separates keyboard from UIToolbar like on image bellow.
How they do it? Extending UIWindow that holds keyboard or in some other way?
Update 1 with answer:
So I have used solution that Tyraz wrote.
Subclass UIToolbar
Instead of image I have used UIView with background color same as the finishing color of the keyboard gradient and with UIViewAutoResizingMaskFlexibleWidth so that it covers keyboard when rotated, with height of 3 pixels
Here is the code for the subclassed UIToolbar
- (void)didMoveToSuperview {
[self.separatorHideView removeFromSuperview];
CGRect seperatorRect = CGRectMake(self.frame.origin.x,
self.frame.size.height,
self.frame.size.width,
3.0);
self.separatorHideView = [[UIView alloc]];
self.separatorHideView.backgroundColor = [UIColor colorWithRed:0.569 green:0.600 blue:0.643 alpha:1.000];
self.separatorHideView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self addSubview:self.separatorHideView];
}
Update 2: Here is code how I'm adding it to UITextView and what color I'm using for tint.
I'm adding it to the UITextView in viewDidLoad with following code
CustomToolbar *accessoryToolbar = [[CustomToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 38)];
accessoryToolbar.tintColor = [UIColor colorWithRed:0.569 green:0.600 blue:0.643 alpha:1.000];
editor.inputAccessoryView = accessoryToolbar;
And this is how it looks like with solution applied to it
On iOS 7 you can create an inputAccessoryView to match keyboard style easily:
[[UIInputView alloc] initWithFrame:<#frame#>
inputViewStyle:UIInputViewStyleKeyboard];
I would try to use a inputAccessoryView plus a second view that sits on top of the separator line and "fills the gap".
Once the inputAccessoryView is added to the keyboard (overwrite the didMoveToSuperview method in your accessory UIView subclass to get notified when this happens), add it to the inputAccessoryView's superview.
Should be something like that in your accessory UIView subclass:
- (void)didMoveToSuperview {
[self.separatorHideView removeFromSuperview];
CGRect seperatorRect = CGRectMake(self.frame.origin.x,
self.frame.origin.y + self.frame.size.height,
self.frame.size.width,
2.0);
UIImage *gapGradient = [UIImage imageNamed:#"GapGradient"];
self.separatorHideView = [[UIImageView alloc]initWithImage:gapGradient];
self.separatorHideView.frame = seperatorRect;
[self.superview addSubview:self.separatorHideView];
}
I would also overwrite setFrame in your accessory UIView subclass to update the frame of the gapView in case the frame of the keyboard is changed.
I took a regular UITabBar and changed it's background image to a custom one which has a lower height, so I changed the height of the frame.
At first what I got is a blank space below the tab bar. so I changed the origin of the frame too. But now the blank space has moved up above the tab bar and this is the result:
And this is the code declaring the tab bar in the AppDelegate:
self.tabContoller = [[UITabBarController alloc] init];
//customizing the tabbar
UIImage * tabBackgroundImage = [UIImage imageNamed:#"tabBarBg.png"];
self.tabContoller.tabBar.backgroundColor = [UIColor colorWithRed:245.f/255.f green:245.f/255.f blue:245.f/255.f alpha:255.f/255.f];
self.tabContoller.tabBar.backgroundImage = tabBackgroundImage;
//setting the tabbar height to the correct height of the image
CGRect tabR = self.tabContoller.tabBar.frame;
CGFloat diff = tabR.size.height - tabBackgroundImage.size.height;
tabR.size.height = tabBackgroundImage.size.height;
tabR.origin.y += diff;
self.tabContoller.tabBar.frame = tabR;
I guess that the problem is that the ViewControllers draw themselves above a constant space which is the height of the regular tab bar. Is there any way to change it?
Change your UITabBarController's subviews to a full-sized frame, this worked for me:
[[yourTabBarController.view.subviews objectAtIndex:0] setFrame:CGRectMake(0, 0, 320, 480)];
Try creating your own class extending from UITabBar and use this function:
- (CGSize)sizeThatFits:(CGSize)size {
CGSize auxSize = size;
auxSize.height = 54; // Put here the new height you want and that's it
return auxSize;
}
This will resize the UITabBar to the size you want. Simple and easy.
If changing the frame like #JoaT mentioned doesn't work make sure the view controller's view has the correct autoresizing mask set.
This SO link may be helpful.
I tried by changing the height and origin of tabbar, for me it worked properly.You can try by increasing the height of your viewcontroller.
How would I draw a rectangle in a custom table cell class? The cell currently has a background image with a few text labels. I would like to draw a rectangle behind each of the labels so they are easier to read over the detailed background image.
I know I could just set the background colour of the label but I would like to have padding between the background colour and the text. If that is possible, I'd love to know how! :)
I'm subclassing a TTTableMessageItemCell in Three20, a method below gets called in which you can play with subviews of the cell,
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat padding = 16;
CGFloat boxWidth = self.contentView.width - 2*padding;
CGFloat textWidth = boxWidth - (padding*2);
CGFloat textHeight = 100;
CGFloat top = kTableCellSmallMargin;
// Position Heading Text
_titleLabel.frame = CGRectMake(padding, top, textWidth, _titleLabel.font.ttLineHeight);
top += _titleLabel.height;
// Position Detail Text
[self.detailTextLabel sizeToFit];
self.detailTextLabel.top = top+2*padding;
self.detailTextLabel.left = 2*padding;
self.detailTextLabel.width = textWidth;
self.detailTextLabel.height = 100;
}
I would like the rectangles to be placed behind the _titleLable and detailTextLabel labels.
edit
I have been able to add the right box using the following,
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor whiteColor];
view.frame = CGRectMake(padding, top, textWidth, textHeight+2*padding);
[self insertSubview:view belowSubview:self.detailTextLabel];
It is laying on top of the label and I cant seem to get it behind it...
edit
I was adding the view to the wrong subview, fixed it with,
[[self.subviews objectAtIndex:0] insertSubview:view atIndex:0];
You can add the labels to views and these to the cell.
You could use insertSubview:belowSubview: to add views behind your labels. With backgroundColor and the right frame they will do what you intend to.
You can also bring detailLabel to front
I am writing an iPhone app with a tab bar and navigation bar. At a certain point, I am pushing an instance of my DetailsViewController class onto the navigation controller stack to show it.
This controller creates its view hierarchy in code: the controller's view property is set to a UIScrollView, which contains a plain UIView (let's call it "contentView") sized to hold all the content to be shown. At the bottom of this contentView, I have four UIButtons.
Now when I run the app (in the simulator at present), and scroll to the bottom of the view, the top two buttons respond to touches; the third responds to touches only in the top portion of it, and the lower button doesn't respond to touches at all. By clicking in various parts of the third button, it appears that the lower 93 pixels of the scroll view is not passing touch events through to its subviews.
93 is suspicious: it's also the combined height of the tab bar (49 pixels) and navigation bar (44 pixels). Yet the navigation bar and tab bar are outside the scroll view. Any suggestions why this might be happening?
Here's the code in question:
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] applicationFrame];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
scrollView.delegate = self;
self.view = scrollView;
UIView *contentView = [[UIView alloc] initWithFrame:scrollView.bounds];
contentView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[scrollView addSubview:contentView];
CGSize viewSize = contentView.bounds.size;
CGSize size;
CGFloat y = 0;
/* Snip creating various labels and image views */
/* Actions view creates and lays out the four buttons; its sizeThatFits:
** method returns the frame size to contain the buttons */
actionsView = [[PropertyDetailActionsView alloc] initWithFrame:CGRectZero];
actionsView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth);
actionsView.delegate = self;
size = [actionsView sizeThatFits:viewSize];
actionsView.frame = CGRectMake(0, y, size.width, size.height);
[contentView addSubview:actionsView];
y += size.height;
[contentView setFrame:CGRectMake(0, 0, viewSize.width, y)];
scrollView.contentSize = contentView.frame.size;
[contentView release];
[scrollView release];
}
As I suggested on Twitter yesterday, it may have something to do with the flexible bottom margin set to the actionsView.
That suggestion did not resolve the problem, yet it lead to the right direction. By removing the flexible height of the contentView the problem has been fixed.
So if anyone out there is having similar problems, try to play with your autoresizingMasks.
also make sure all your content views are the height that covers the bottom button.
I make each view a different color to see them.
Setup: I have a UITextView inside a UITableViewCell's contentView. I want it to take up the full size of the cell. I create the text view like so:
UITextView *textView = [[[UITextView alloc] initWithFrame:CGRectMake(0,0,268,43)] autorelease];
textView.backgroundColor = [UIColor redColor];
textView.layer.cornerRadius = 10;
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
And I override heightForRowAtIndexPath to return 200 for that row.
The background color is just so I can tell where it is. The cell seems to be automatically sizing it correctly upon first view. However, I need it to continue to resize it correctly while autorotating the interface, which only seems to work sometimes, and only when I'm not editing the textView. Other times, it resizes the view to have a very small height (looks like -1), or makes it too wide, or just doesn't even resize it at all.
I've tried overriding layoutSubviews in the cell and just do nothing, but even that doesn't stop the view from resizing all over the place.
I've been hacking away at this for awhile now, but still have found no solution.
A UITableViewCell has a fixed height, the height provided by the UITableView's delegate. When you rotate your device, the height of the row will never change, unless you call -reloadData on your tableView. I'd get rid of the autoresizing and manage it yourself.
When you init your textField, you can easily set the frame to CGRectZero. Then implement -layoutSubviews (and call super in that method, before setting the frames of your subviews) and set the frame of the UITextField according to the contentRect property of the cell.
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if(self = [super ...]){ // or -initWithStyle:reuseIdentifier: whatever you want
_textView = [[UITextView alloc] initWithFrame:CGRectZero]; // Instance variable
// Probably not needed to set autoresizing mask
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_textView.frame = CGRectMake(0.0f, 0.0f, self.contentRect.size.width, self.contentRect.size.height); // Adjust as needed
}