In Swift, I was trying to replicate the animated underline from this App. When a user taps "local" or "world", the underline bar slides across the screen and positions under the button selected.
How is the animation done? Is the bar an imageview that moves?
You ask:
How is the animation done? Is the bar an imageview that moves?
More than likely it's just a UIView whose background color is that light blue, and whose frame (or whose constraints that define the frame) are changed within a [UIView animateWithDuration:] block.
For example, in Objective-C, you might have these properties:
#property (nonatomic, weak) UIView *underlineView;
#property (nonatomic, strong) NSArray *underlineConstraints;
#property (nonatomic, weak) IBOutlet UIButton *button1;
and this code:
- (void)addUnderline {
UIView *underlineView = [[UIView alloc] init];
underlineView.translatesAutoresizingMaskIntoConstraints = NO;
underlineView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:underlineView];
self.underlineView = underlineView;
[self updateConstraintsForUnderlineView:underlineView underButton:self.button1];
}
- (IBAction)didTapButton:(UIButton *)sender {
[self updateConstraintsForUnderlineView:self.underlineView underButton:sender];
[UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
- (void)updateConstraintsForUnderlineView:(UIView *)underlineView underButton:(UIButton *)button {
if (self.underlineConstraints) {
[self.view removeConstraints:self.underlineConstraints];
}
NSDictionary *views = NSDictionaryOfVariableBindings(underlineView, button);
self.underlineConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button]-[underlineView(5)]" options:NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing metrics:nil views:views];
[self.view addConstraints:self.underlineConstraints];
}
Or in Swift:
weak var underlineView: UIView!
var underlineConstraints: [AnyObject]!
#IBOutlet weak var button1: UIButton!
func addUnderline() {
let underlineView = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
underlineView.backgroundColor = UIColor.lightGrayColor()
view.addSubview(underlineView)
self.underlineView = underlineView
updateConstraintsForUnderlineView(underlineView, underButton:button1)
}
#IBAction func didTapButton(sender: UIButton) {
updateConstraintsForUnderlineView(underlineView, underButton:sender)
UIView.animateWithDuration(0.25, delay: 0.0, options: .CurveEaseOut, animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
func updateConstraintsForUnderlineView(underlineView: UIView, underButton button: UIButton) {
if underlineConstraints != nil {
view.removeConstraints(underlineConstraints)
}
let views = ["underlineView": underlineView, "button": button]
underlineConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[button]-[underlineView(5)]", options: .AlignAllLeading | .AlignAllTrailing, metrics: nil, views: views)
view.addConstraints(underlineConstraints)
}
Related
I'm trying to add AVPlayerLayer to UIView
self.player = AVPlayer(URL: NSURL(fileURLWithPath: path!))
self.playerLayer = AVPlayerLayer(player: player);
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.playerLayer.frame = ctrVideo.bounds;
self.ctrVideo.layer.addSublayer(playerLayer);
player.play();
This is the video's container (in blue):
Constraints:
Final result:
I can't figure out why video is not bounds to UIVIew coordinates. If i bounds it into controller's superview, it is ok.
Sublayer won't resize automatically when view's frame changes, you have to do it manually. You can subclass a UIView to make it easier.
class VideoContainerView: UIView {
var playerLayer: CALayer?
override func layoutSublayersOfLayer(layer: CALayer) {
super.layoutSublayersOfLayer(layer)
playerLayer?.frame = self.bounds
}
}
And then in your code:
self.player = AVPlayer(URL: NSURL(fileURLWithPath: path!))
self.playerLayer = AVPlayerLayer(player: player);
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.playerLayer.frame = ctrVideo.bounds;
self.ctrVideo.layer.addSublayer(playerLayer);
self.ctrVideo.playerLayer = playerLayer;
player.play();
Just make sure that you change that class of your "ctrVideo" from UIView to VideoContainerView.
Here's #almas' awesome answer in Swift 4
class VideoContainerView: UIView {
var playerLayer: CALayer?
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
playerLayer?.frame = self.bounds
}
}
I adding AVPlayerLayer in viewDidLoad event... Before constraints apply.
Here is AVPlayerLayer-backed view, that tries to keep its aspect ratio.
PlayerView.h:
#interface PlayerView : UIView
#property (strong, nonatomic) AVPlayerLayer *layer;
#end
PlayerView.m:
#interface PlayerView ()
#property (strong, nonatomic) NSLayoutConstraint *aspectConstraint;
#end
#implementation PlayerView
#dynamic layer;
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (instancetype)init {
self = [super init];
if (self) {
self.userInteractionEnabled = NO;
[self addObserver:self forKeyPath:#"layer.videoRect" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:#"layer.videoRect"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:#"layer.videoRect"]) {
CGSize size = [change[NSKeyValueChangeNewKey] CGRectValue].size;
if (change[NSKeyValueChangeNewKey] && size.width && size.height) {
self.aspectConstraint.active = NO;
self.aspectConstraint = [self.widthAnchor constraintEqualToAnchor:self.heightAnchor multiplier:size.width / size.height];
self.aspectConstraint.priority = UILayoutPriorityDefaultHigh;
self.aspectConstraint.active = YES;
}
else {
self.aspectConstraint.active = NO;
}
}
}
#end
I know that this question has been asked over and over again, but nothing seems to be working for me. Most of the solutions around are pretty out of date, and the rest are incredibly huge blocks of code that are ten times larger then the actual projects coding. I have a few UITextFields lined up vertically, but when the keyboard launches to edit one, it covers up the text field. I was wondering if there is a simple beginner way to scroll the view up, and then back down when the editing starts and ends?
Thank you.
I have made solutions that work with scroll and non-scroll views using keyboard notification and a detection of the current first responder, but sometimes I use this trivial solution instead: The simple way is to detect the opening keyboard via the text field delegate's textViewDidBeginEditing: method and to move the entire view up. The easiest way to do this is with something along the lines of changing self.view.bounds.origin.y to -100 (or whatever). Use the corresponding textViewShouldEndEditing: method to set it to the opposite, which is 100 in this case. Changing bounds is a relative procedure. After changing it the frame is moved but the bounds origin is still zero.
Since I found it, I use TPKeyboardAvoiding - https://github.com/michaeltyson/TPKeyboardAvoiding.
It is working great, and is very easy to setup:
Add a UIScrollView into your view controller's xib
Set the scroll view's class to TPKeyboardAvoidingScrollView (still in the xib, via the identity inspector)
Place all your controls within that scroll view
You can also create it programmatically, if you want.
There is a class for the same need inside a UITableViewController ; it is only needed in case you support a version of iOS below 4.3.
#BenLu and other users who are facing problem of the function are never getting called is because of following reason:
As the delegate inbuild function bydefaults return void instead of BOOL this is how it should be as follows:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = -100;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = 100;
[self.view setFrame:frame];
[UIView commitAnimations];
}
I spent sometime on this problem and gathered pieces code to create one final solution. My problem was related to UITableView scrolling and keyboard open/close.
You need two partial methods in your cell class:
void EditingBegin(UITextField sender)
{
// Height of tallest cell, you can ignore this!
float tableMargin = 70.0f;
float tableHeight = _tableView.Frame.Size.Height;
float keyBoardHeight = KeyboardHeight();
NSIndexPath[] paths = this._tableView.IndexPathsForVisibleRows;
RectangleF rectLast = this._tableView.RectForSection(paths[paths.Length - 1].Section);
RectangleF rectFirst = this._tableView.RectForSection(paths[0].Section);
float lastCellY = rectLast.Y - rectFirst.Y;
if (lastCellY > tableHeight - keyBoardHeight)
{
float diff = lastCellY - (tableHeight - tableMargin - keyBoardHeight);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
}
float cellPosition = this._tableView.RectForSection(this._section).Y;
if (cellPosition > tableHeight - keyBoardHeight)
{
if (this._tableView.ContentInset.Bottom == 0.0f)
{
float diff = cellPosition - (tableHeight - tableMargin - keyBoardHeight);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
}
else
{
this._tableView.ScrollToRow(NSIndexPath.FromItemSection(0, this._section), UITableViewScrollPosition.Middle, true);
}
}
}
partial void EditingEnd(UITextField sender)
{
UIView.BeginAnimations(null);
UIView.SetAnimationDuration(0.3f);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, 0.0f, 0.0f);
UIView.CommitAnimations();
}
and then in your view controller class:
public override void WillAnimateRotation(UIInterfaceOrientation toInterfaceOrientation, double duration)
{
base.WillAnimateRotation(toInterfaceOrientation, duration);
float bottom = this.TableView.ContentInset.Bottom;
if (bottom > 0.0f)
{
if (toInterfaceOrientation == UIInterfaceOrientation.Portrait || toInterfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown)
{
bottom = bottom * UIScreen.MainScreen.Bounds.Width / UIScreen.MainScreen.Bounds.Height;
}
else
{
bottom = bottom * UIScreen.MainScreen.Bounds.Height / UIScreen.MainScreen.Bounds.Width;
}
UIEdgeInsets insets = this.TableView.ContentInset;
this.TableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, bottom, 0.0f);
}
}
If you have a UITableView or a UIScrollView it's better to change values for contentOffset instead of making changes to the frame.
Working on Peter's Answer, adding this method to your class works nicely:
- (void)textViewDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGPoint offset = self.tableView.contentOffset;
offset.y += 200; // You can change this, but 200 doesn't create any problems
[self.tableView setContentOffset:offset];
[UIView commitAnimations];
}
That's it, no need to add the textViewDidEndEditing method.
I shouldn't need to say this, but for this to work your UITextField or UITextView must be a delegate of your controller.
Starting with Peter's answer, I developed the following approach in Swift 3.0 under iOS 10.1. I'm doing this for a textView, so I have implemented the UITextViewDelegate functions textViewDidBeginEditing and textViewDidEndEditing where I adjust the view's bounds. As you can see, I set the origin Y value to a small positive number to scroll up and then back to 0 to return to the original position.
Here is the relevant code from my ViewController. You don't need to animate, but it adds a nice touch.
func textViewDidBeginEditing(_ textView: UITextView)
{
if UIScreen.main.bounds.height < 568 {
UIView.animate(withDuration: 0.75, animations: {
self.view.bounds.origin.y = 60
})
}
}
func textViewDidEndEditing(_ textView: UITextView)
{
if UIScreen.main.bounds.height < 568 {
UIView.animate(withDuration: 0.75, animations: {
self.view.bounds.origin.y = 0
})
}
}
i have a scrollview and 3 text fields in this. I have a simple code from my own application :
.h file is :
#import <UIKit/UIKit.h>
#interface AddContactViewController : UIViewController<UITextFieldDelegate, UIScrollViewDelegate>
#property (nonatomic, retain) NSDictionary *dict_contactDetail;
#property (nonatomic, retain) IBOutlet UILabel *lbl_name;
#property (nonatomic, retain) IBOutlet UITextField *txtField_tel;
#property (nonatomic, retain) IBOutlet UITextField *txtField_address;
#property (nonatomic, retain) IBOutlet UITextField *txtField_email;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#end
.m file :
#import "AddContactViewController.h"
#interface AddContactViewController ()
#end
#implementation AddContactViewController
#synthesize dict_contactDetail;
#synthesize lbl_name, txtField_tel, txtField_email, txtField_address, scrollView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// NSLog(#"dict_contactDetail : %#", dict_contactDetail);
UIBarButtonItem * rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Add" style:UIBarButtonSystemItemDone target:self action:#selector(addEmergencyContact:)];
self.navigationItem.rightBarButtonItem = rightButton;
lbl_name.text = [NSString stringWithFormat:#"%# %#", [dict_contactDetail valueForKey:#"fname"], [dict_contactDetail valueForKey:#"lname"]];
txtField_tel.returnKeyType = UIReturnKeyDone;
txtField_email.returnKeyType = UIReturnKeyDone;
txtField_address.returnKeyType = UIReturnKeyDone;
}
-(void)addEmergencyContact:(id)sender
{
scrollView.frame = CGRectMake(0, 0, 320, 460);
}
#pragma mark - text field delegates
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if([textField isEqual:txtField_tel])
{
[scrollView setContentOffset:CGPointMake(0, 70)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
if([textField isEqual:txtField_address])
{
[scrollView setContentOffset:CGPointMake(0, 140)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
if([textField isEqual:txtField_email])
{
[scrollView setContentOffset:CGPointMake(0, 210)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
scrollView.frame = CGRectMake(0, 0, 320, 460);
[textField resignFirstResponder];
return YES;
}
#end
My question is as simple as the title, but here's a little more:
I have a UITextField, on which I've set the background image. The problem is that the text hugs so closely to it's edge, which is also the edge of the image. I want my text field to look like Apple's, with a bit of horizontal space between the background image and where the text starts.
This is the quickest way I've found without doing any subclasses:
UIView *spacerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
[textfield setLeftViewMode:UITextFieldViewModeAlways];
[textfield setLeftView:spacerView];
In Swift:
let spacerView = UIView(frame:CGRect(x:0, y:0, width:10, height:10))
textField.leftViewMode = UITextFieldViewMode.Always
textField.leftView = spacerView
Swift 5:
let spacerView = UIView(frame:CGRect(x:0, y:0, width:10, height:10))
textField.leftViewMode = .always
textField.leftView = spacerView
You have to subclass and override textRectForBounds: and editingRectForBounds:. Here is a UITextfield subclass with custom background and vertical and horizontal padding:
#interface MyUITextField : UITextField
#property (nonatomic, assign) float verticalPadding;
#property (nonatomic, assign) float horizontalPadding;
#end
#import "MyUITextField.h"
#implementation MyUITextField
#synthesize horizontalPadding, verticalPadding;
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x + horizontalPadding, bounds.origin.y + verticalPadding, bounds.size.width - horizontalPadding*2, bounds.size.height - verticalPadding*2);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#end
Usage:
UIImage *bg = [UIImage imageNamed:#"textfield.png"];
CGRect rect = CGRectMake(0, 0, bg.size.width, bg.size.height);
MyUITextField *textfield = [[[MyUITextField alloc] initWithFrame:rect] autorelease];
textfield.verticalPadding = 10;
textfield.horizontalPadding = 10;
[textfield setBackground:bg];
[textfield setBorderStyle:UITextBorderStyleNone];
[self.view addSubview:textfield];
A good approach to add padding to UITextField is to subclass UITextField , overriding the rectangle methods and adding an edgeInsets property. You can then set the edgeInsets and the UITextField will be drawn accordingly. This will also function correctly with a custom leftView or rightView set.
OSTextField.h
#import <UIKit/UIKit.h>
#interface OSTextField : UITextField
#property (nonatomic, assign) UIEdgeInsets edgeInsets;
#end
OSTextField.m
#import "OSTextField.h"
#implementation OSTextField
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
}
return self;
}
- (CGRect)textRectForBounds:(CGRect)bounds {
return [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [super editingRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}
#end
I turned this into a nice little extension for use inside Storyboards:
public extension UITextField {
#IBInspectable public var leftSpacer:CGFloat {
get {
return leftView?.frame.size.width ?? 0
} set {
leftViewMode = .Always
leftView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
}
}
}
My 2 cents:
class PaddedTextField : UITextField {
var insets = UIEdgeInsets.zero
var verticalPadding:CGFloat = 0
var horizontalPadding:CGFloat = 0
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + insets.left, y: bounds.origin.y + insets.top, width: bounds.size.width - (insets.left + insets.right), height: bounds.size.height - (insets.top + insets.bottom));
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
}
I suggest converting your UITextField to a UITextView and setting the contentInset. For example to indent by 10 spaces:
textview.contentInset=UIEdgeInsetsMake(0, 10, 0, 0);
Sometimes the leftView of the textField is a Apple's magnifying glass image.
If so, you can set an indent like this:
UIView *leftView = textField.leftView;
if (leftView && [leftView isKindOfClass:[UIImageView class]]) {
leftView.contentMode = UIViewContentModeCenter;
leftView.width = 20.0f; //the space you want indented.
}
If you don't want to create #IBOutlet's could simply subclass (answer in Swift):
class PaddedTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
let spacerView = UIView(frame:CGRect(x: 0, y: 0, width: 10, height: 10))
leftViewMode = .always
leftView = spacerView
}
}
how to set rounded corner for a UITextView ?
fist import the file
#import <QuartzCore/QuartzCore.h>
and then set the property of your text view
yourTextViewName.layer.cornerRadius = kCornerRadius;
where kCornerRadius is a constant you set as a radius for corner
Try this it will work for sure
you have to import
QuartzCore/QuartzCore.h
UITextView* txtView = [[UITextView alloc] initWithFrame:CGRectMake(50, 50, 300, 100)];
txtView.layer.cornerRadius = 5.0;
txtView.clipsToBounds = YES;
I define an category class for UITextView in .h:
#interface UITextView (RoundedCorner)
-(void) roundedCornerDefault;
-(void) roundedCornerWithRadius:(CGFloat) radius
borderColor:(CGColorRef) color
borderWidth:(CGFloat) width;
#end
and the implementation class:
#import <QuartzCore/QuartzCore.h>
#import "UITextView+RoundedCorner.h"
#implementation UITextView (RoundedCorner)
-(void) roundedCornerDefault {
[self roundedCornerWithRadius:10
borderColor:[[UIColor grayColor] CGColor]
borderWidth:1];
}
-(void) roundedCornerWithRadius:(CGFloat) radius
borderColor:(CGColorRef) color
borderWidth:(CGFloat) width {
self.layer.cornerRadius = radius;
self.layer.borderColor = color;
self.layer.borderWidth = width;
self.clipsToBounds = YES;
}
#end
Example to use it:
#import "UITextView+RoundedCorner.h"
...
[self.myTextView roundedCornerDefault];
Work for me as below code and step
create textView var
#IBOutlet weak var currentAddressOutlet: KMPlaceholderTextView!
create this function
private func setBorderForTextView() {
self.currentAddressOutlet.layer.borderColor = UIColor.lightGray.cgColor
self.currentAddressOutlet.layer.borderWidth = 0.5
self.currentAddressOutlet.layer.cornerRadius = 5
}
call above function in your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
setupTable()
setBorderForTextView()
}
Result is here
I want to make the top of the navigation view a bit smaller. How would you achieve this? This is what I've tried so far, but as you can see, even though I make the navigationbar smaller, the area which it used to occupy is still there (black).
[window addSubview:[navigationController view]];
navigationController.view.frame = CGRectMake(0, 100, 320, 280);
navigationController.navigationBar.frame = CGRectMake(0, 0, 320, 20);
navigationController.view.backgroundColor = [UIColor blackColor];
[window makeKeyAndVisible];
Create a UINavigationBar Category with a custom sizeThatFits.
#implementation UINavigationBar (customNav)
- (CGSize)sizeThatFits:(CGSize)size {
CGSize newSize = CGSizeMake(self.frame.size.width,70);
return newSize;
}
#end
Using this navigation bar subclass I've successfully created a larger navigation bar on iOS 5.x to iOS 6.x on the iPad. This gives me a larger navigation bar but doesn't break all the animations.
static CGFloat const CustomNavigationBarHeight = 62;
static CGFloat const NavigationBarHeight = 44;
static CGFloat const CustomNavigationBarHeightDelta = CustomNavigationBarHeight - NavigationBarHeight;
#implementation HINavigationBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// UIColor *titleColor = [[HITheme currentTheme] fontColorForLabelForLocation:HIThemeLabelNavigationTitle];
// UIFont *titleFont = [[HITheme currentTheme] fontForLabelForLocation:HIThemeLabelNavigationTitle];
// [self setTitleTextAttributes:#{ UITextAttributeFont : titleFont, UITextAttributeTextColor : titleColor }];
CGAffineTransform translate = CGAffineTransformMakeTranslation(0, -CustomNavigationBarHeightDelta / 2.0);
self.transform = translate;
[self resetBackgroundImageFrame];
}
return self;
}
- (void)resetBackgroundImageFrame
{
for (UIView *view in self.subviews) {
if ([NSStringFromClass([view class]) rangeOfString:#"BarBackground"].length != 0) {
view.frame = CGRectMake(0, CustomNavigationBarHeightDelta / 2.0, self.bounds.size.width, self.bounds.size.height);
}
}
}
- (void)setBackgroundImage:(UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics
{
[super setBackgroundImage:backgroundImage forBarMetrics:barMetrics];
[self resetBackgroundImageFrame];
}
- (CGSize)sizeThatFits:(CGSize)size
{
size.width = self.frame.size.width;
size.height = CustomNavigationBarHeight;
return size;
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
[self resetBackgroundImageFrame];
}
#end
For swift
create a subclass of Uinavigation bar.
import UIKit
class higherNavBar: UINavigationBar {
override func sizeThatFits(size: CGSize) -> CGSize {
var newSize:CGSize = CGSizeMake(self.frame.size.width, 87)
return newSize
}
There will be two blank strips on both sides, I changed the width to the exact number to make it work.
However the title and back button are aligned to the bottom.
It's not necessary to subclass the UINavigationBar. In Objective-C you can use a category and in Swift you can use an extension.
extension UINavigationBar {
public override func sizeThatFits(size: CGSize) -> CGSize {
return CGSize(width: frame.width, height: 70)
}
}
I have found the following code to perform better on iPad (and iPhone):
- (CGSize)sizeThatFits:(CGSize)size
{
return CGSizeMake(self.superview.bounds.size.width, 62.0f);
}
If you want to use a custom height for your nav bar, I think you should probably, at the very least, use a custom nav bar (not one in your nav controller). Hide the navController's bar and add your own. Then you can set its height to be whatever you want.
I was able to use the following subclass code in Swift. It uses the existing height as a starting point and adds to it.
Unlike the other solutions on this page, it seems to still resize correctly when switching between landscape and portrait orientation.
class TallBar: UINavigationBar {
override func sizeThatFits(size: CGSize) -> CGSize {
var size = super.sizeThatFits(size)
size.height += 20
return size
}
}
Here's a pretty nice subclass in Swift that you can configure in Storyboard. It's based on the work done by mackross, which is great, but it was pre-iOS7 and will result in your nav bar not extending under the status bar.
class UINaviationBarCustomHeight: UINavigationBar {
// Note: this must be set before the navigation controller is drawn (before sizeThatFits is called),
// so set in IB or viewDidLoad of the navigation controller
#IBInspectable var barHeight: CGFloat = -1
#IBInspectable var barHeightPad: CGFloat = -1
override func sizeThatFits(size: CGSize) -> CGSize {
var customSize = super.sizeThatFits(size)
let stockHeight = customSize.height
if (UIDevice().userInterfaceIdiom == .Pad && barHeightPad > 0) {
customSize.height = barHeightPad
}
else if (barHeight > 0) {
customSize.height = barHeight
}
// re-center everything
transform = CGAffineTransformMakeTranslation(0, (stockHeight - customSize.height) / 2)
resetBackgroundImageFrame()
return customSize
}
override func setBackgroundImage(backgroundImage: UIImage?, forBarPosition barPosition: UIBarPosition, barMetrics: UIBarMetrics) {
super.setBackgroundImage(backgroundImage, forBarPosition: barPosition, barMetrics: barMetrics)
resetBackgroundImageFrame()
}
private func resetBackgroundImageFrame() {
if let bg = valueForKey("backgroundView") as? UIView {
var frame = bg.frame
frame.origin.y = -transform.ty
if (barPosition == .TopAttached) {
frame.origin.y -= UIApplication.sharedApplication().statusBarFrame.height
}
bg.frame = frame
}
}
}
I am a newbie in ios yet. I solved the problem in following way :
I have created a new class that inherits from UINavigationBar
I override the following method :
(void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
self.frame = CGRectMake(0, 0, 320, 54);
}
3.To get a custom background of the navigation bar, I overrided the following method :
-(void)drawRect:(CGRect)rect {
[super drawRect:rect];
UIImage *img = [UIImage imageNamed:#"header.png"];
[img drawInRect:CGRectMake(0,0, self.frame.size.width, self.frame.size.height)];
}
In xib file, I have changed the default UINavigationBar class of the navigation bar to my class.