I have a view controller (in a storyboard) with 6 buttons and 6 subviews. Those subviews are connected to IBOutlets and named like "mySubView1", "mySubView2" etc:
#property (weak, nonatomic) IBOutlet UIView *mySubView1;
#property (weak, nonatomic) IBOutlet UIView *mySubView2;
I want to animate the alpha value of the subview depending on which button was pressed. All 6 buttons are connected to the same IBAction (below) and each button has a tag (1 to 6).
How do I get the subview property name dynamically? eg, mySubView1 if sender.tag = 1 for example, in the animations block below:
- (IBAction)animateSubviews:(id)sender {
[UIView animateWithDuration:0.4
delay: 0.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
self.mySubView1.alpha = 1;
}
completion:^(BOOL finished){
}];
}
so what I need is something like : self.mySubView[sender tag].alpha = 1 sort of thing, but I'm stumped, so any advice much appreciated.
Many thanks
1) Assigning tags to subviews
Assign tags to your UIViews, for example the view that corresponds to button with tag 1 could have tag 101.
- (IBAction)animateSubviews:(id)sender {
[UIView animateWithDuration:0.4
delay: 0.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
UIView *subview = [self.view viewWithTag:((UIButton*)sender).tag + 100];
subview.alpha = 1;
}
completion:^(BOOL finished){
}];
}
2) Using dynamic selectors (which answers your original question)
You can use the method NSSelectorFromString which returns a SEL.
UIView *subview = [self performSelector:NSSelectorFromString([NSString stringWithFormat:#"mySubView%d", ((UIButton*)sender).tag])];
#property (strong, noatomatic) NSArray* arrayForSubViews;
if (!self.arrayForSubViews) {
self.arrayForSubViews = #[ self.mySubView1, self.mySubView2, ... ];
}
//get the second view
self.arrayForSubViews[1];
Related
I want to change my calendar height's constraint 124 to zero when calendar button is clicked how can I do that? I want to use button statement but I couldn't at objective c. Thanks.
#property (weak, nonatomic) IBOutlet UIBarButtonItem *calendarButton;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *heightOfCalendar;`
- (IBAction)calendarAction:(id)sender {
}
you need to set constant property
heightOfCalendar.constant = requiredheight
You can Do like this to change size on each tap
- (IBAction)calendarAction:(id)sender {
sender.selected = !sender.selected;
heightOfCalendar.constant = (sender.selected)?0:124;
}
Doing it with an animation will looks good
(IBAction)calendarAction:(id)sender {
[UIView animateWithDuration:0.5
animations:^{
heightOfCalendar.constant =0;
[self.view layoutIfNeeded];
}];
}
Write this code in your IBAction:
- (IBAction)calendarAction:(id)sender {
self.heightOfCalendar.constant = 0;
}
Also make sure you have your IBAction connection set properly.
And I see a ` symbol at the end of the following statement:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *heightOfCalendar;`
I'm building a to-do app where user can select the reminder option. If reminder is enabled, then a UIDatePicker would be enabled. I created the main UI with storyboard. So my initial UI is like this
But after disabling and enabling again the reminder switch, the Datepicker placed on top of the view. I need to position it to the bottom of the view.
My ViewController code is like this
- (IBAction)isReminderSelected:(UISwitch *)sender {
if([_reminderSetField isOn]){
[_reminderSetField.viewForBaselineLayout addSubview: _reminderDatePicker];
}
else {
[_reminderDatePicker removeFromSuperview];}
}
I created the main UI via storyboard. Then initialize the datepicker in the header file....
My *.h file
#property (strong, nonatomic) IBOutlet UISwitch *reminderSetField;
#property (strong, nonatomic) IBOutlet UIDatePicker *reminderDatePicker;
- (IBAction)isReminderSelected:(UISwitch *)sender;
You should set y position of the picker view like
float y = [UIScreen mainScreen].bounds.size.height - _reminderDatePicker.frame.size.height;
[_reminderDatePicker setFrame:CGRectMake(0, y, _reminderDatePicker.frame.size.width, _reminderDatePicker.frame.size.height)];
i think you should try like this
- (IBAction)isReminderSelected:(UISwitch *)sender {
if([_reminderSetField isOn]){
float y = [UIScreen mainScreen].bounds.size.height - _reminderDatePicker.frame.size.height;
[_reminderDatePicker setFrame:CGRectMake(0, y, _reminderDatePicker.frame.size.width, _reminderDatePicker.frame.size.height)];
[yourView addSubView:_reminderDatePicker];
}
else {
[_reminderDatePicker removeFromSuperview];}
}
I'm very new to iOS programming and I have two simple UIView's.
.h file:
#property (strong, nonatomic) IBOutlet UIView *mainPageOutlet;
#property (strong, nonatomic) IBOutlet UIView *secondPageOutlet;
.m file:
- (IBAction)secondPageToMain:(id)sender {
self.view = [self mainPageOutlet];
}
- (IBAction)mainPageToSecondPage:(id)sender {
self.view = [self secondPageOutlet];
}
How can I use animations or transitions when switching between these two views? Currently they only appear when the button is clicked. Ideally I'd like to have something like push, modal, slide in or up.
Is there a way of doing this?
Many thanks.
Use following code.
- (IBAction)secondPageToMain:(id)sender {
[UIView transitionFromView:[self secondPageOutlet]
toView:[self mainPageOutlet]
duration:2
options: UIViewAnimationOptionTransitionFlipFromRight
completion:^(BOOL finished){
[[self secondPageOutlet] sendSubviewToBack];
}];
}
- (IBAction)mainPageToSecondPage:(id)sender {
[UIView transitionFromView:[self mainPageOutlet]
toView:[self secondPageOutlet]
duration:2
options: UIViewAnimationOptionTransitionFlipFromRight
completion:^(BOOL finished){
[[self mainPageOutlet] sendSubviewToBack];
}];
}
And in your viewDidLoad method add following code,
[[self secondPageOutlet] sendSubviewToBack];
So initially your second view will be behind your main view and on performing action it will come in front with animation.
I have a parent VC with 3 children - Settings, Location and Diary.
Settings, Location & Diary are all accessed via IBActions based on their respective buttons.
When I go between Location and Diary, everything is fine. When I click Settings, it works, but when I click back to Location or Diary I get the dreaded must have a common parent view controller error. Funny thing is the exception says that the Diary and Location VC's don't share the same parent even though in that case I am clicking between Settings and either Diary or Location.
.m properties
#interface LPMainContentViewController ()
#property (weak, nonatomic) IBOutlet UIView *containerView;
#property (weak, nonatomic) IBOutlet UIButton *locationButton;
#property (weak, nonatomic) IBOutlet UIButton *diaryButton;
#property (weak, nonatomic) IBOutlet UIButton *settingsButton;
#property (strong, nonatomic) UIViewController *locationViewController;
#property (strong, nonatomic) UIViewController *diaryViewController;
#property (strong, nonatomic) UIViewController *settingsController;
- (IBAction)goToLocationView:(UIButton *)sender;
- (IBAction)goToDiaryView:(UIButton *)sender;
- (IBAction)goToSettings:(UIButton *)sender;
#end
I use if statements in the code to determine which was the previous child VC for the transition method and to set enabled on the button to YES or NO.
- (IBAction)goToLocationView:(UIButton *)sender {
if ([_diaryViewController isViewLoaded]){
[self cycleFromViewController:_diaryViewController toViewController:_locationViewController];
_locationButton.enabled = NO;
_diaryButton.enabled = YES;
}
else if ([_settingsController isViewLoaded]){
[self cycleFromViewController:_settingsController toViewController:_locationViewController];
_locationButton.enabled = NO;
_settingsButton.enabled = YES;
}
}
- (IBAction)goToDiaryView:(UIButton *)sender {
if ([_locationViewController isViewLoaded]){
[self cycleFromViewController:_locationViewController toViewController:_diaryViewController];
_locationButton.enabled = YES;
_diaryButton.enabled = NO;
}
else if ([_settingsController isViewLoaded]){
[self cycleFromViewController:_settingsController toViewController:_diaryViewController];
_diaryButton.enabled = NO;
_settingsButton.enabled = YES;
}
}
- (IBAction)goToSettings:(UIButton *)sender {
if ([_locationViewController isViewLoaded]){
[self cycleFromViewController:_locationViewController toViewController:_settingsController];
_locationButton.enabled = YES;
_settingsButton.enabled = NO;
}
else if ([_diaryViewController isViewLoaded]){
[self cycleFromViewController:_diaryViewController toViewController:_settingsController];
_diaryButton.enabled = YES;
_settingsButton.enabled = NO;
}
}
and here's the transition method pulled straight from the Apple docs
- (void) cycleFromViewController: (UIViewController*) oldVC
toViewController: (UIViewController*) newVC
{
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
newVC.view.frame = _containerView.bounds;
CGRect endFrame = _containerView.bounds;
[self transitionFromViewController: oldVC toViewController: newVC
duration: 0.0 options:0
animations:^{
newVC.view.frame = oldVC.view.frame;
oldVC.view.frame = endFrame;
}
completion:^(BOOL finished) {
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
}];
}
isViewLoaded is the wrong thing to check. The view will get loaded once and then stick around, so you're probably choosing the wrong "current" view controller after a few cycles. Have another property to hold the current view controller instead, it will simplify your code and also actually work:
#property (weak, nonatomic) UIViewController *currentViewController;
You'll need to set this to whatever the initial child view controller is when you first set up the container view controller.
Then, change the goToXX methods to be like the following:
- (IBAction)goToDiaryView:(UIButton *)sender {
self.settingsButton.enabled = YES;
self.locationButton.enabled = YES;
self.diaryButton.enabled = NO;
[self cycleFromViewController:self.currentViewController toViewController:self.diaryViewController];
}
Last of all, in your cycle method, make sure you keep this property updated in the completion block of the transition:
completion:^(BOOL finished) {
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
self.currentViewController = newVC;
}];
Note that it is safer to use your properties rather than the _instanceVariables. That's what they're there for.
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 :