I want to add a UISearchBar to the bottom of my UINavigationBar and I want to make it look like it is a part of the bar. In the video below it shows a default animation of the nav bar sliding into the view from the top. I want to slide in the search bar just before that and make it look like one object.
How can I re-create this animation for a UISearchBar for the showing and hiding animation? And make it seamlessly animate?
Animation
I think you use a custom animation for navigation bar not default animation.
My test code is following.
#import "ViewController.h"
#interface ViewController ()
// Add a UISearchBar below navigation bar in Interface Builder
#property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
#end
#implementation ViewController{
// this is flag for navigation bar status (shown or hidden)
BOOL _isNaviBarHidden;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_isNaviBarHidden = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// trigger for show navigation bar. I don't know when you want to show. so I add a button in my test viewcontroller
- (IBAction)showNavibar:(id)sender {
if(_isNaviBarHidden == NO){
return;
}
[self setNavibarAndSearchBarHidden:NO];
}
- (IBAction)hideNavibar:(id)sender {
if(_isNaviBarHidden == YES){
return;
}
[self setNavibarAndSearchBarHidden:YES];
}
-(void)setNavibarAndSearchBarHidden:(BOOL)hidden{
CGFloat yOffset = -130;
CALayer *naviBarLayer = self.navigationController.navigationBar.layer;
CALayer *searchBarLayer = self.searchBar.layer;
[UIView animateWithDuration:0.3 animations:^{
if(hidden){
naviBarLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMakeTranslation(0, yOffset));
searchBarLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMakeTranslation(0, yOffset));
}else{
naviBarLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformIdentity);
searchBarLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformIdentity);
}
} completion:^(BOOL finished) {
_isNaviBarHidden = hidden;
}];
}
Related
I'm not able to rectify the situation which is arising in
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
where, memory assigned every time and not releasing after dismissing the view.
Well, the situation is that, I have created a simple login screen as rootviewcontroller, then through a button (login button) presenting a tabviewcontroller where, I have added a view as subview on tabviewcontroller, on that subview four buttons are there, through delegate I'm controlling the tabIndex, everything is working fine, but when I check about the leaking memory, above view.frame keeping the memory.
I'm posting the image, please look at this,
Here is my TabView Controller with custome tabbar.
Any help will be appreciable.
UPDATE:
Here is my complete code.
From Login Page:
- (IBAction)clickLoginBtnAction:(id)sender {
tabControler = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarController"];
[self presentViewController:tabControler animated:YES completion:nil];
}
From Tabbar Controller:
#import "TabBarController.h"
#interface TabBarController ()<TabBarIndexProtocol>{
// CustomTabViewController *customTabView;
}
#property (strong) CustomTabViewController *customTabView;
#end
#implementation TabBarController
#synthesize customTabView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
customTabView = [self.storyboard instantiateViewControllerWithIdentifier:#"CustomTabViewController"];
self.tabBar.hidden = YES;
[self frameCustomTabViewOnTabBar];
customTabView.tabBarIndexDelegate = self;
}
-(void)viewDidDisappear:(BOOL)animated{
// customTabView.view.frame = nil;
}
// For Screen Rotation
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self frameCustomTabViewOnTabBar];
}];
}
-(void)frameCustomTabViewOnTabBar{
customTabView.view.frame = CGRectMake(0, self.view.frame.size.height-49, self.view.frame.size.width, 49);
[self.view addSubview:customTabView.view];
}
# pragma mark - TabBarIndexProtocol from CustomTabBarClass
-(void)tabBarIndexSelected :(int)tabNumber{
NSLog(#"%d",(int)tabNumber);
[self setSelectedIndex:tabNumber];
}
In My CustomeTab.h File:
#import <UIKit/UIKit.h>
#protocol TabBarIndexProtocol <NSObject>
#required
-(void)tabBarIndexSelected :(int)tabNumber;
#end
#interface CustomTabViewController : UIViewController
#property(weak) id <TabBarIndexProtocol> tabBarIndexDelegate;
#end
and from its .m file
#import "CustomTabViewController.h"
#interface CustomTabViewController ()
#end
#implementation CustomTabViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)tabNumberOne:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:0];
}
- (IBAction)tabNumberTwo:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:1];
}
- (IBAction)tabNumberThree:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:2];
}
- (IBAction)tabNumberFour:(id)sender {
[self.tabBarIndexDelegate tabBarIndexSelected:3];
}
Thats all, where I'm mistaking :( .
How i can add header like this inside my app and receive the touch event also control over it to hide/show ?
Here is something to work on.
I made a new project for iOS 7, with a single ViewController. Here is the storyboard :
I added a simple UIView at the top, a UINavigationBar just under it, and a large UIView taking the remaining space. This last view is where you should put your content.
Now here is the ViewController code :
ViewController.h :
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIView *littleView;
#property (weak, nonatomic) IBOutlet UINavigationBar *navBar;
#property (weak, nonatomic) IBOutlet UIView *mainView;
- (IBAction)toggleLittleView:(id)sender;
#end
littleView is the view behind the status bar, navbar is the navigation bar, mainView is the content view, and toggleLittleview is linked to the button in the nav bar.
ViewController.m :
#implementation ViewController {
BOOL isVisible;
CGFloat statusBarOffset;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Setting initial values
isVisible = YES;
statusBarOffset = 20;
// Adding a gesture recognizer to littleView
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[_littleView addGestureRecognizer:tap];
}
// Linked to the nav bar button
- (IBAction)toggleLittleView:(id)sender {
// If littleView is not on screen, show it before animation
if (!isVisible) {
_littleView.hidden = !_littleView.hidden;
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
// Animate to the new frames
[UIView animateWithDuration:0.25
animations:^{
_littleView.frame = CGRectOffset(_littleView.frame, 0, isVisible ? -(_littleView.frame.size.height-statusBarOffset) : (_littleView.frame.size.height-statusBarOffset));
_navBar.frame = CGRectMake(_navBar.frame.origin.x, _littleView.frame.origin.y + _littleView.frame.size.height, _navBar.frame.size.width, _navBar.frame.size.height);
CGFloat offSet = isVisible ? self.view.frame.size.height - _navBar.frame.size.height + statusBarOffset : self.view.frame.size.height - _navBar.frame.size.height - _littleView.frame.size.height + statusBarOffset;
_mainView.frame = CGRectMake(_mainView.frame.origin.x, _navBar.frame.origin.y + _navBar.frame.size.height, _mainView.frame.size.width, offSet);
isVisible = !isVisible;
}
completion:^(BOOL finished) {
// If view is not visible after animation, hide it
if (!isVisible) {
_littleView.hidden = !_littleView.hidden;
[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleDefault];
}
}];
}
// Do stuff on tap
-(void)handleTap:(UIGestureRecognizer*)tap {
[[[UIAlertView alloc] initWithTitle:#"Test" message:#"You tapped !" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
#end
WARNING
This code is not perfect, but it basically replicates the effect you're looking for. You should work on it to make it behave as you wish. Also, this was implemented in a simple UIViewController, not embed in a UINavigationController, with a navigation bar I added manually. This code won't work in a UITableViewController or a UIViewController embed in a UINavigationController. It does not use autolayout, which you definitely should use under iOS 6/7.
Here is gif preview :
I need a ViewController to be called modally to show some UIButton and other UIView on top of the current window. I want the background to be partially transparent and showing the current window below it - something similar to a UIActionSheet but with a custom design. I coded my VC to do the following: 1) during init the VC sets self.view.frame equals to [[UIApplication sharedApplication]keyWindow].frame 2) when show() is called the VC adds self.view on top of [[UIApplication sharedApplication]keyWindow] subViews 3) when an internal button calls the private method release() the VC remove self.view from its superview. Example with a single release button as follows:
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
//set my view frame equal to the keyWindow frame
self.view.frame = [[UIApplication sharedApplication]keyWindow].frame;
self.view.backgroundColor = [UIColor colorWithWhite:0.3f alpha:0.5f];
//create a button to release the current VC with the size of the entire view
UIButton *releaseMyselfButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[releaseMyselfButton setTitle:#"Release" forState:UIControlStateNormal];
releaseMyselfButton.frame = CGRectMake(0, 0, 90, 20);
[releaseMyselfButton addTarget:self action:#selector(releaseMyself) forControlEvents:UIControlEventTouchDown];
//add the button to the view
[self.view addSubview:releaseMyself];
}
return self;
}
- (void) show
{
//add self.view to the keyWindow to make sure that it will appear on top of everything else
[[[UIApplication sharedApplication]keyWindow] addSubview:self.view];
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
}
#end
If I create an instance of ModalController from another VC and I call show() everything goes as expected:
#interface CurrentVC ()
#property (strong, nonatomic) ModalController *myModalController;
#end
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.myModalController = [[ModalController alloc]init];
[self.myModalController show];
}
#end
To make it work I need to retain the ModalController in a property until release () is called. However I would like to have the same freedom I have with UIActionSheet and simply keep an instance of it in a local variable:
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
ModalController *myModalController = [[ModalController alloc]init];
[myModalController show];
}
#end
If I do this with the current code ARC will release myModalController straight after show() is called and the release button will be pointing to nil. How can I make this work without storing the object in a property? I've identified a work around but I'm not sure it's a good design option:
#interface ModalController ()
#property (strong, nonatomic) ModalController *myselfToAutorelease;
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
... ... ...
self.myselfToAutorelease = self;
}
return self;
}
- (void) show
{
... ... ...
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
self.myselfToAutorelease = nil;
}
What I've done is making ModalController "self sufficient" - it stores a pointer to itself during init and set it to nil when it's ready to release himself. It works but I have the feeling that this is against the ARC design principles! Is this approach correct? If not, how can I handle this differently?
Thanks in advance for your help.
Doesn't work like that.
You don't keep a reference to self.
In the main view controller you just create your object. If you need it to be around longer keep it in a property in the main view controller , when done, set the property to nil in the main view controller.
I have UITextFields in tableviewcells. When you swipe over the cell not part of the textfield, the delete action comes up as expected. If you swipe over the textfield, it stops the delete from popping up.
How do I fix this so that you can swipe over the inputs and the cell will trigger the delete action?
I think the issue here is that the touch on the text field is interfering with your swipe gesture recognizer (presumably attached to the parent view). I had a similar problem with a text field that was placed in a UIScrollView.
I got around this problem by overlaying a clear UIView on top of my UITextField. I then assigned a UITapGestureRecognizer to this clear view to set the text field as the first responder when the user taps on the field. Otherwise, the swiped is sent to the parent view (a scroll view) which recognizes the swipe without any issues. It's kind of lame but it works.
This scenario is a bit different from yours, but I think it's the same problem. Here is what my code looks like, hopefully it helps:
// UIView subclass header
#interface LSAddPageView : UIView
#property (weak, nonatomic) IBOutlet UITextField *textField; // Connected to the UITextField in question
#property (strong, nonatomic) UIView *textFieldMask;
#property (assign, nonatomic) BOOL textFieldMaskEnabled;
#end
// UIView subclass implementation
#implementation LSAddPageView
- (void)awakeFromNib
{
[super awakeFromNib];
_textFieldMask = [UIView new];
_textFieldMask.backgroundColor = [UIColor clearColor];
[self insertSubview:_textFieldMask aboveSubview:self.textField];
}
- (void)layoutSubviews
{
[super layoutSubviews];
_textFieldMask.frame = self.textField.frame;
}
- (BOOL)textFieldMaskEnabled
{
return _textFieldMask.hidden == NO;
}
- (void)setTextFieldMaskEnabled:(BOOL)textFieldMaskEnabled
{
_textFieldMask.hidden = !textFieldMaskEnabled;
}
#end
And then in the controller:
- (void)viewDidLoad
{
[super viewDidLoad];
_addPageView = (LSAddPageView*)self.view;
_maskGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapMask:)];
_maskGestureRecognizer.numberOfTapsRequired = 1;
_maskGestureRecognizer.numberOfTouchesRequired = 1;
[_addPageView.textFieldMask addGestureRecognizer:_maskGestureRecognizer];
self.textField.delegate = self; // Set delegate to be notified when text field resigns first responder
}
- (void)didTapMask:(UIGestureRecognizer*)recognizer
{
_addPageView.textFieldMaskEnabled = NO;
[self.textField becomeFirstResponder];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
_addPageView.textFieldMaskEnabled = YES;
return YES;
}
Sounds like you need to set the cancelsTouchesInView property
yourGestureRecognizer.cancelsTouchesInView = NO;
from UIButton & UITextField will block UITableViewCell being swipe to delete
self.tableView.panGestureRecognizer.delaysTouchesBegan = YES;
this will make the textfield not stop left swipe guestures
Can anyone help me to understand what is going wrong with my function below pls?
I'm fairly new to native IOS dev.
I wanted to implement a side panel into our app using this tutorial (http://www.appcoda.com/ios-programming-sidebar-navigation-menu/).
All that works fine and is straightforward enough - but I wanted to trigger the reveal functionality via a standard button rather than a dynamic button in the header - to suit our design..
The original code is as follows -
#import "SWRevealViewController.h"
// Change button color
_sidebarButton.tintColor = [UIColor colorWithWhite:0.96f alpha:0.2f];
// Set the side bar button action. When it's tapped, it'll show up the sidebar.
_sidebarButton.target = self.revealViewController;
_sidebarButton.action = #selector(revealToggle:);
// Set the gesture
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
to replicate that functionality triggered by a button I added the following to the .h file -
#property (weak, nonatomic) IBOutlet UIBarButtonItem *sidebarButton;
#property (weak, nonatomic) IBOutlet UIButton *revealLeftBtn;
- (IBAction)btnClickRevealL:(id)sender;
and the following in my .m file
- (IBAction)btnClickRevealL:(id)sender {
[self.revealViewController];
[#selector(revealToggle:)];
}
I get the error 'suspected identifier' for the above lines - I dont understand how the functions triggered with the previous method and not with the above - can anyone help?
Cheers
for reference this is the entire .M file -
#import "MainViewController.h"
#import "SWRevealViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"";
// Change button color
_sidebarButton.tintColor = [UIColor colorWithWhite:0.96f alpha:0.2f];
[self.navigationController.navigationBar setTranslucent:TRUE];
// Set the side bar button action. When it's tapped, it'll show up the sidebar.
// Set the gesture
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnClickRevealL:(id)sender {
[self.revealViewController];
[#selector(revealToggle:)];
}
#end
This line:
(IBAction)btnClickRevealL:(id)sender {
[self.revealViewController];
[#selector(revealToggle:)];
}
Does nothing.
I guess this is what you want to do:
[self.revealViewController revealToggle:sender];
When you set the target and the action of a button, you set which class is doing the action (target) and what method is called (action).
that's the equivalent of doing [target action]
- (IBAction)logInBtnClicked:(id)sender {
//Remove WhiteSpace from start of uitextfield
self.emailIdTxtFldRef.text = [self.emailIdTxtFldRef.text stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
self.passwordTxtFldRef.text = [self.passwordTxtFldRef.text stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
if ([self.emailIdTxtFldRef.text isEqualToString:#""]||[self.passwordTxtFldRef.text isEqualToString:#""]) {
[self showMessage:#"All fields are mandatory" withTitle:#"Error"];
}else{
SWRevealViewController *revealVC = [self.storyboard instantiateViewControllerWithIdentifier:#"swRevealViewContoller"];
revealVC.delegate=self;
[[UIApplication sharedApplication]keyWindow].rootViewController = revealVC;
}
}