I'm interesting, how can it is possible to make text moving from right to left like a "running news line in TV"?
I can do text moving from right to left (or any other) in UILabel, but this animation moving should be infinity loop, not only one time.
Here's a few:
https://github.com/caydenliew/CLTickerView
https://github.com/malcommac/DMScrollingTicker
https://github.com/MugunthKumar/MKTickerViewDemo
https://github.com/jeffhodnett/JHTickerView
https://github.com/cbess/AutoScrollLabel
There's probably more at Cocoa Controls.
How about like this:
-(void) animate {
label.center = CGPointMake(self.view.bounds.size.width + label.bounds.size.width/2, label.center.y);
[UIView animateWithDuration:20 delay:0 options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionRepeat) animations:^{
label.center = CGPointMake(0 - label.bounds.size.width/2, label.center.y);
} completion:nil];
}
// CrawlView.h
#import <UIKit/UIKit.h>
#interface CrawlView : UIScrollView
#property (assign, nonatomic) NSTimeInterval period;
#property (strong, nonatomic) NSMutableArray *messages;
- (void)go;
#end
// CrawlView.m
#define kWORD_SPACE 16.0f
#import "CrawlView.h"
#interface CrawlView ()
#property (assign, nonatomic) CGFloat messagesWidth;
#end
#implementation CrawlView
- (void)buildSubviews {
for (UIView *subview in [self subviews]) {
if ([subview isKindOfClass:[UILabel self]]) {
[subview removeFromSuperview];
}
}
CGFloat xPos = kWORD_SPACE;
for (NSString *message in self.messages) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = message;
CGSize size = [message sizeWithFont:label.font];
CGFloat width = size.width + kWORD_SPACE;
label.frame = CGRectMake(xPos, 0.0, width, self.frame.size.height);
[self addSubview:label];
xPos += width;
}
self.messagesWidth = xPos;
self.contentSize = CGSizeMake(xPos, self.frame.size.height);
self.contentOffset = CGPointMake(-self.frame.size.width, 0.0);
}
- (void)go {
[self buildSubviews];
if (!self.period) self.period = self.messagesWidth / 100;
[UIView animateWithDuration:self.period
delay:0.0
options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionRepeat
animations:^{
self.contentOffset = CGPointMake(self.messagesWidth, 0.0);
} completion:^(BOOL finished){
[self buildSubviews];}];
}
#end
Related
I have a UILabel that I am animating the constraints for so that it drop down into view. I am using layer.cornerRadius to give the view rounded corners, but for whatever reason after the animation completes the corner radius is removed.
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.4 options:UIViewAnimationOptionCurveEaseInOut animations:^{
if (shouldShow) {
self.labelOverMapTopConstraint.constant = 16;
} else {
self.labelOverMapTopConstraint.constant = -40;
}
[self.view layoutIfNeeded];
} completion:nil];
cornerRadius is set in viewDidLoad.
Is there a way to prevent this from happening?
I suspect you're subclassing UILabel here since it looks like you have padding in there, is that correct?
There could be something going awry with any custom drawing/calculations you're doing in there, so it would probably be helpful to post that code for inspection as well.
A few questions:
Do you have masksToBounds set to YES?
If you're not using a custom UILabel subclass, are you wrapping the label in a view?
How is the animation being triggered? Is it by a button? A callback from a NSURLRequest? If it's triggered by an async callback are you jumping back on the main queue to perform the animation?
If the animation is triggered automatically within the lifecycle, which lifecycle method is it triggered in?
I wasn't able to reproduce the issue in a test project with a vanilla UILabel. I then tried it with a UILabel subclass which includes additional padding and still wasn't able to reproduce it there.
I've included example code snippets below:
#import "ViewController.h"
#import "R4NInsetLabel.h"
#interface ViewController ()
#property BOOL showingToast;
#property (strong, nullable) IBOutlet R4NInsetLabel *toastLabel;
#property (strong, nullable) IBOutlet NSLayoutConstraint *toastLabelTopConstraint;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBar.titleTextAttributes = #{NSForegroundColorAttributeName : [UIColor whiteColor]};
self.showingToast = NO;
// start with the label pushed off the top of the screen
self.toastLabelTopConstraint.constant = -40.0f;
self.toastLabel.layer.cornerRadius = 6.0f;
self.toastLabel.layer.masksToBounds = YES;
}
- (IBAction)toggleToast:(id)sender {
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.4 options:UIViewAnimationOptionCurveEaseInOut animations:^{
if (self.showingToast == NO) {
self.toastLabelTopConstraint.constant = 16;
self.showingToast = YES;
} else {
self.toastLabelTopConstraint.constant = -40;
self.showingToast = NO;
}
[self.view layoutIfNeeded];
} completion:nil];
}
#end
#import "R4NInsetLabel.h"
IB_DESIGNABLE
#interface R4NInsetLabel()
#property IBInspectable CGFloat contentPadding;
#property (nonatomic) UIEdgeInsets contentInsets;
- (CGSize)_addInsetsToSize:(CGSize)size;
#end
#implementation R4NInsetLabel
- (UIEdgeInsets)contentInsets {
return UIEdgeInsetsMake(self.contentPadding, self.contentPadding, self.contentPadding, self.contentPadding);
}
- (CGSize)_addInsetsToSize:(CGSize)size {
CGFloat width = size.width + self.contentInsets.left + self.contentInsets.right;
CGFloat height = size.height + self.contentInsets.top + self.contentInsets.bottom;
return CGSizeMake(width, height);
}
- (void)drawTextInRect:(CGRect)rect {
CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.contentInsets);
[super drawTextInRect:insetRect];
}
- (CGSize)intrinsicContentSize {
CGSize baseSize = [super intrinsicContentSize];
return [self _addInsetsToSize:baseSize];
}
- (CGSize)sizeThatFits:(CGSize)size {
CGSize baseSize = [super sizeThatFits:size];
return [self _addInsetsToSize:baseSize];
}
#end
And here's what it looks like:
You also need to set the corner radius on viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
label.layer.cornerRadius = yourValue
}
This is what I got but I am getting errors as you see in the picture.
I have the IBOutlets for UIImageView *bottomGroundContinue and one for *bottomGround
#implementation viewController
#property (nonatomic, assign) BOOL keepGoing;
#property (nonatomic, strong) UIImageView *bottomGround;
#property (nonatomic, strong) UIImageView *bottomGroundContinue;
- (void)getReady {
UIImage *theImage = [UIImage imageNamed:#"bottomGround.png"];
self.bottomGround = [[UIImageView alloc] initWithImage:theImage];
self.bottomGroundContinue = [[UIImageView alloc] initWithImage:theImage];
self.bottomGround.frame = self.view.bounds;
self.bottomGRoundContinue.frame = CGRectOffset(self.view.bounds, self.bottomGround.bounds.size.width, 0.0);
[self.view addSubview:self.bottomGround];
[self.view addSubview:self.bottomGroundContinue];
self.keepGoing = YES;
}
- (void)go {
CGFloat width = self.bottomGround.bounds.size.width;
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
self.bottomGround.frame = CGRectOffset(self.bottomGround.frame, -width, 0);
self.bottomGroundContinue.frame = CGRectOffset(self.bottomGroundContinue.frame, -width, 0);
} completion:^(BOOL finished) {
if (self.keepGoing) {
// now B is where A began, so swap them and reposition B
UIImageView *temp = self.bottomGround;
self.bottomGround = self.bottomGroundContinue;
self.bottomGroundContinue = temp;
self.bottomGroundContinue.frame = CGRectOffset(self.view.bounds, self.view.bounds.size.width, 0.0);
// recursive call, but we don't want to wind up the stack
[self performSelector:#selector(go) withObject:nil afterDelay:0.0];
}
}];
}
You are declaring properties inside of #implementation. You need to declare them inside of #interface.
I have a custom alterviewcontroller which subclasses UIViewController. It works great in apps that are portrait, but in landscape, it's running into issues with the frame orientation. I've tried a bunch of fixes, but none seem to be getting it right.
In the current version, it displays correctly but the main view is the wrong size, so the buttons that are outside of that view are not clickable.
In this image, the Next Turn button is not Clickable, the Go to Black button is. This is landscape left.
.h file
#import <UIKit/UIKit.h>
#import "SPRTitleFontLabel.h"
#protocol SPRAlertViewControllerDelegate;
#interface SPRAlertViewController : UIViewController
#property (nonatomic, weak) id <SPRAlertViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UIView *alertview;
#property (weak, nonatomic) IBOutlet SPRTitleFontLabel *titleLabel;
#property (weak, nonatomic) IBOutlet UITextView *messageLabel;
#property (weak, nonatomic) IBOutlet UIButton *yesButton;
#property (weak, nonatomic) IBOutlet UIButton *noButton;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
#property int tag;
-(void)setTitle:(NSString *)title message:(NSString *)message andButtonCount:(int)buttonCount;
- (IBAction)doDismiss:(id)sender;
- (void)show;
#end
#protocol SPRAlertViewControllerDelegate <NSObject>
- (void)alert:(SPRAlertViewController *)alert didDismissWithButtonClick:(int)buttonIndex;
#end
.m file
#import "SPRAlertViewController.h"
#import "SPRAudioHelper.h"
#interface SPRAlertViewController ()
{
UIWindow *alertWindow;
}
#end
#implementation SPRAlertViewController
+(instancetype)new
{
SPRAlertViewController *alvc = [super new];
return alvc;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
alertWindow = [self windowWithLevel:UIWindowLevelAlert];
if (!alertWindow)
{
//rotate the window frame to get the right orientation
CGRect tempFrame = [UIScreen mainScreen].bounds;
CGRect tempFrame2 = tempFrame;
tempFrame2.size.height = tempFrame.size.width;
tempFrame2.size.width = tempFrame.size.height;
alertWindow = [[UIWindow alloc] initWithFrame:tempFrame2];
alertWindow.windowLevel = UIWindowLevelAlert;
[alertWindow makeKeyAndVisible];
NSLog(#"window frame %#", NSStringFromCGRect(alertWindow.frame));
}
alertWindow.rootViewController = self;
}
return self;
}
- (void)setTitle:(NSString *)title message:(NSString *)message andButtonCount:(int)buttonCount
{
self.titleLabel.text = title;
self.messageLabel.text = message;
if (buttonCount == 1)
{
self.yesButton.hidden = YES;
self.noButton.hidden = YES;
self.okButton.hidden = NO;
}
else
{
self.yesButton.hidden = NO;
self.noButton.hidden = NO;
self.okButton.hidden = YES;
}
CGRect frame = self.messageLabel.frame;
CGSize size = [self.messageLabel sizeThatFits:CGSizeMake(self.messageLabel.frame.size.width, FLT_MAX)];
frame.size.height = size.height;
self.messageLabel.frame = frame;
[self.messageLabel setNeedsDisplay];
float buttonTop = frame.size.height + frame.origin.y;
self.yesButton.frame = CGRectMake(self.yesButton.frame.origin.x, buttonTop, self.yesButton.frame.size.width, self.yesButton.frame.size.height);
self.noButton.frame = CGRectMake(self.noButton.frame.origin.x, buttonTop, self.noButton.frame.size.width, self.noButton.frame.size.height);
self.okButton.frame = CGRectMake(self.okButton.frame.origin.x, buttonTop, self.okButton.frame.size.width, self.okButton.frame.size.height);
self.alertview.frame = CGRectMake(self.alertview.frame.origin.x, self.alertview.frame.origin.y, self.alertview.frame.size.width, buttonTop+55);
NSLog(#"parent frame %#", NSStringFromCGRect(self.parentViewController.view.frame));
NSLog(#"view frame %#", NSStringFromCGRect(self.view.frame));
NSLog(#"alert frame %#", NSStringFromCGRect(self.alertview.frame));
NSLog(#"button frame %#", NSStringFromCGRect(self.yesButton.frame));
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIInterpolatingMotionEffect* m1 =
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
m1.maximumRelativeValue = #0.0;
m1.minimumRelativeValue = #0.0;
UIInterpolatingMotionEffect* m2 =
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.y"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
m2.maximumRelativeValue = #20.0;
m2.minimumRelativeValue = #-20.0;
UIMotionEffectGroup* g = [UIMotionEffectGroup new];
g.motionEffects = #[m1,m2];
[self.alertview addMotionEffect:g];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)show
{
CGRect tempFrame = [UIScreen mainScreen].bounds;
CGRect tempFrame2 = tempFrame;
tempFrame2.size.height = tempFrame.size.width;
tempFrame2.size.width = tempFrame.size.height;
self.alertview.center = CGPointMake(CGRectGetMidX(tempFrame2), CGRectGetMidY(tempFrame2));
self.alertview.transform = CGAffineTransformMakeScale(1,1.6);
self.alertview.alpha = 0;
self.view.alpha = 0;
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 1;
}
completion:
^(BOOL finished){
[UIView animateWithDuration:0.25 animations:^{
self.alertview.alpha = 1;
self.alertview.transform = CGAffineTransformIdentity;
}];
}
];
}
- (UIWindow *)windowWithLevel:(UIWindowLevel)windowLevel
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for (UIWindow *window in windows) {
if (window.windowLevel == windowLevel) {
return window;
}
}
return nil;
}
- (IBAction)doDismiss:(id)sender
{
// [[SPRAudioHelper sharedHelper] playSoundFromTag:lightClick];
NSLog(#"check");
[UIView animateWithDuration:0.25 animations:^{
self.alertview.transform = CGAffineTransformScale(self.alertview.transform, 1,0.5);
self.alertview.alpha = 0;
}completion:^(BOOL finished)
{
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 0;
}completion:^(BOOL finished)
{
[alertWindow removeFromSuperview];
alertWindow = nil;
if (sender == self.yesButton)
[self.delegate alert:self didDismissWithButtonClick:1];
else
[self.delegate alert:self didDismissWithButtonClick:0];
}];
}];
}
#end
Here's the NSLog results:
2014-07-10 10:59:18.603 Damage Report Timer[5166:60b] window frame {{0, 0}, {568, 320}}
2014-07-10 10:59:18.720 Damage Report Timer[5166:60b] parent frame {{0, 0}, {0, 0}}
2014-07-10 10:59:18.722 Damage Report Timer[5166:60b] view frame {{0, 0}, {320, 568}}
2014-07-10 10:59:18.724 Damage Report Timer[5166:60b] alert frame {{0,51}, {568, 146.5}}
2014-07-10 10:59:18.726 Damage Report Timer[5166:60b] button frame {{101, 91.5}, {150, 50}}
I figured it out after doing more and more digging. I was really making the whole thing way more complicated than it is. 2 functions needed to be fixed, here's the updated functions:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
alertWindow = [self windowWithLevel:UIWindowLevelAlert];
if (!alertWindow)
{
alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.windowLevel = UIWindowLevelAlert;
[alertWindow makeKeyAndVisible];
}
alertWindow.rootViewController = self;
}
return self;
}
and
- (void)show
{
CGRect frame = self.view.bounds;
self.alertview.center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
self.alertview.transform = CGAffineTransformMakeScale(1,1.6);
self.alertview.alpha = 0;
self.view.alpha = 0;
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 1;
}
completion:
^(BOOL finished){
[UIView animateWithDuration:0.25 animations:^{
self.alertview.alpha = 1;
self.alertview.transform = CGAffineTransformIdentity;
}];
}
];
}
Originally since show was centering the view on mainscreen bounds, it was putting it in the wrong place, since mainscreen bounds doesn't get updated on rotation. Then I made the mistake of resizing the window to accommodate for that. Which made buttons no longer be in the window.
I am working with images in iOS. So when a user presses let's say a button, I want an image to appear to fill up from the bottom to the top.
In order be more precise, imagine a thermometer that is empty and when you press a button the thermometer fills up from the bottom to the top. But I want this with animation.
Do you have any idea how to implement that?
Thanks!
You can easily do it by having a second view acting as a mask over the image view with your image. Then you can apply simple scale transform and animate it revealing the image. Alternatively, you can animate the frame change. Effect will be the same. I'm attaching code for 2 ways of doind this.
Here is what I achieved in both cases:
Code I used for the case I:
#interface MyViewController ()
#property(nonatomic, strong) UIImageView *imageView;
#property(nonatomic, strong) UIView *maskView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Image"]];
_imageView.center = self.view.center;
_imageView.layer.anchorPoint = CGPointMake(0.5, 0.0);
[self.view addSubview:_imageView];
_maskView = [[UIView alloc] initWithFrame:_imageView.frame];
_maskView.backgroundColor = [UIColor whiteColor];
_maskView.center = self.view.center;
_maskView.layer.anchorPoint = CGPointMake(0.5, 0.0);
[self.view addSubview:_maskView];
}
- (IBAction)buttonTapped:(id)sender
{
[UIView animateWithDuration:1.0f
animations:^
{
_maskView.transform = CGAffineTransformMakeScale(1.0, 0.0001);
}];
}
#end
Code I used for the case II:
#interface MyViewController ()
#property(nonatomic, strong) UIImageView *imageView;
#property(nonatomic, assign) CGFloat height;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Image"]];
_imageView.center = self.view.center;
_imageView.contentMode = UIViewContentModeBottom;
_height = CGRectGetHeight(_imageView.bounds);
CGRect frame = _imageView.frame;
frame.size.height = 0.00001;
frame.origin.y += _height;
_imageView.frame = frame;
_imageView.clipsToBounds = YES;
[self.view addSubview:_imageView];
}
- (IBAction)buttonTapped:(id)sender
{
[UIView animateWithDuration:1.0f
animations:^
{
CGRect frame = _imageView.frame;
frame.size.height = _height;
frame.origin.y -= _height;
_imageView.frame = frame;
}];
}
#end
In both cases the end effect was the same.
You can do this by changing the frame of the view within the animate block. For example, if myView was a 100x100 sized view:
myView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height, 0, 0);
[UIView animateWithDuration:1.0f
animations:^
{
myView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height, 100, 100);
}];
This example should enlarge a 100x100 view in the bottom left corner of the screen, making it appear to reveal itself from the bottom up.
I have a view controller (vc) with a UIPickerView on it which needs to slide up from the bottom half of the screen.
Using:
ViewControlelr *vc = [self.storyboard instantiateViewControllerWithIdentifier: #"VC"];
[self presentViewController:vc animated:YES completion:nil]
makes the page display in fullscreeen
Ive read that putting UIPickerViews in activity sheets is not a good idea anymore, any suggestions?
Ive read that putting UIPickerViews in activity sheets is not a good
idea anymore, any suggestions?
In all honesty, it was never a good idea but it became kind of an underground standard. If you are displaying it after the user sets the text field as the firstResponder, you can set the UIPickerView as the UITextField's inputView. If you want a tool bar on top of it, create one and set it as the UITextField's inputAccessoryView.
If this is not the case, I would suggest creating a UIView container to hold the UIPickerView and set its y to the height of the screen. When you want to show it, use UIView animations to change the y to the height of screen minus the height of the container.
Alternatively you could just do the same thing without a UIView container but you may lose some control over how you want to present the UIPickerView since its size is fixed.
EDIT 1:
I created a container that holds a UIPickerView though it seems to only be presented correctly presentable in portrait mode. Feel free to expand upon it to make it orientation friendly.
//
// AbstractPickerPresenter.h
// ShowDatePickerView
//
// Created by Christopher John Morris on 10/10/13.
// Copyright (c) 2013 Christopher John Morris. All rights reserved.
//
#import <UIKit/UIKit.h>
#protocol PickerPresenterDelegate;
#protocol PickerPresenterButtonDelegate;
#interface PickerPresenter : UIView {
UIView *pickerContainer;
id<PickerPresenterButtonDelegate>buttonDelegate;
}
/*
* Helper constant used for measuring frames
*/
#property (nonatomic, readonly) CGFloat pickerHeight;
/*
* Helper constant used for measuring frames
*/
#property (nonatomic, readonly) CGFloat navBarHeight;
/*
* Automatically hides view when Cancel is clicked.
* Default is YES
*/
#property (nonatomic) BOOL hideOnDoneClicked;
/*
* Automatically hides view when Done is clicked.
* Default is YES
*/
#property (nonatomic) BOOL hideOnCancelClicked;
/*
* Raises the view into visible position
*/
- (void) show;
/*
* Removes the view out of visible position
*/
- (void) hide;
/*
* Sets titles for buttons. Size of buttons adujst based on textsize
*/
- (void)setCancelButtonTitle:(NSString *)title;
- (void)setDoneButtonTitle:(NSString *)title;
#end
/*
* Delegate that is used strictly by subclasses to react
* to button presses.
*/
#protocol PickerPresenterButtonDelegate
#required
- (void) didCancel;
- (void) didSave;
#end
...
//
// AbstractPickerPresenter.m
// ShowDatePickerView
//
// Created by Christopher John Morris on 10/10/13.
// Copyright (c) 2013 Christopher John Morris. All rights reserved.
//
#import "PickerPresenter.h"
#interface PickerPresenter()
/*
* Tint color of buttons
*/
#property (nonatomic, strong) UIColor *tintColor;
/*
* Cancel button
*/
#property (nonatomic, strong) UIButton *cancelButton;
/*
* Done button
*/
#property (nonatomic, strong) UIButton *doneButton;
/*
* Black facade that appears behind the picker container
* and is the size of the current UIWindow
*/
#property (nonatomic, strong) UIView *facadeView;
/*
* Helper constant used to make animations consistent
*/
#property (nonatomic, readonly) NSTimeInterval animationInterval;
#end
#implementation PickerPresenter
- (id)init {
self = [super init];
if (self) {
[self initializeView];
[self initializeViewContainer];
[self initializeCancelButton];
[self initializeDoneButton];
}
return self;
}
- (void) initializeView {
[self setHidden:YES];
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
self.frame = CGRectMake(0, height, [[UIScreen mainScreen] bounds].size.width, height);
self.backgroundColor = [UIColor clearColor];
self.tintColor = [[[[UIApplication sharedApplication] delegate] window] tintColor];
self.facadeView = [[UIView alloc] initWithFrame:self.frame];
self.facadeView.backgroundColor = [UIColor blackColor];
self.facadeView.alpha = 0.4;
[self addSubview:self.facadeView];
self.hideOnDoneClicked = YES;
self.hideOnCancelClicked = YES;
}
- (void) initializeViewContainer {
CGFloat containerHeight = (self.pickerHeight + self.navBarHeight);
pickerContainer = [[UIView alloc] initWithFrame:CGRectMake(0,
self.frame.size.height + containerHeight,
self.frame.size.width,
containerHeight)];
pickerContainer.backgroundColor = [UIColor whiteColor];
[self addSubview:pickerContainer];
}
- (void) initializeCancelButton {
self.cancelButton = [[UIButton alloc] init];
[self.cancelButton setTitleColor:self.tintColor forState:UIControlStateNormal];
[self.cancelButton setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted];
[self.cancelButton setTitle:#"Cancel" forState:UIControlStateNormal];
self.cancelButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:20];
CGSize size = [self.cancelButton.titleLabel.text sizeWithFont:self.cancelButton.titleLabel.font];
self.cancelButton.frame = CGRectMake(10, 0, size.width, self.navBarHeight);
[self.cancelButton addTarget:self action:#selector(cancel) forControlEvents:UIControlEventTouchUpInside];
[pickerContainer addSubview:self.cancelButton];
}
- (void) cancel {
[buttonDelegate didCancel];
}
- (void) done {
[buttonDelegate didSave];
}
- (void) initializeDoneButton {
self.doneButton = [[UIButton alloc] init];
[self.doneButton setTitleColor:self.tintColor forState:UIControlStateNormal];
[self.doneButton setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted];
[self.doneButton setTitle:#"Done" forState:UIControlStateNormal];
self.doneButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:20];
[self.doneButton addTarget:self action:#selector(done) forControlEvents:UIControlEventTouchUpInside];
CGSize size = [self.doneButton.titleLabel.text sizeWithFont:self.doneButton.titleLabel.font];
self.doneButton.frame = CGRectMake(self.frame.size.width - (size.width + 10), 0, size.width, self.navBarHeight);
[pickerContainer addSubview:self.doneButton];
}
- (void)setCancelButtonTitle:(NSString *)title {
[self.cancelButton setTitle:title forState:UIControlStateNormal];
CGSize size = [self.cancelButton.titleLabel.text sizeWithFont:self.cancelButton.titleLabel.font];
self.cancelButton.frame = CGRectMake(10,
0,
size.width,
self.cancelButton.frame.size.height);
}
- (void)setDoneButtonTitle:(NSString *)title {
[self.doneButton setTitle:title forState:UIControlStateNormal];
CGSize size = [self.doneButton.titleLabel.text sizeWithFont:self.doneButton.titleLabel.font];
self.doneButton.frame = CGRectMake(self.frame.size.width - (size.width + 10),
self.doneButton.frame.origin.y,
size.width,
self.doneButton.frame.size.height);
}
- (void) show {
self.alpha = 0.0;
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y - self.frame.size.height,
self.frame.size.width,
self.frame.size.height);
self.facadeView.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height);
[[[UIApplication sharedApplication] keyWindow] addSubview:self];
[UIView animateWithDuration:self.animationInterval animations:^{
[self setHidden:NO];
self.alpha = 1.0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:self.animationInterval animations:^{
pickerContainer.frame = CGRectMake(pickerContainer.frame.origin.x,
self.frame.size.height - pickerContainer.frame.size.height,
pickerContainer.frame.size.width,
pickerContainer.frame.size.height);
} completion:nil];
}];
}
- (void) hide {
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
[UIView animateWithDuration:self.animationInterval animations:^{
pickerContainer.frame = CGRectMake(0,
self.frame.size.height + pickerContainer.frame.size.height,
self.frame.size.width,
pickerContainer.frame.size.height);
} completion:^(BOOL finished) {
[UIView animateWithDuration:self.animationInterval animations:^{
self.alpha = 0.0;
} completion:^(BOOL finished) {
[self setHidden:YES];
[self removeFromSuperview];
self.frame = CGRectMake(self.frame.origin.x,
height,
self.frame.size.width,
self.frame.size.height);
self.facadeView.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height);
}];
}];
}
- (CGFloat)pickerHeight {
CGFloat const kPickerHeight = 216;
return kPickerHeight;
}
-(CGFloat)navBarHeight {
CGFloat const kNavBarHeight = 44;
return kNavBarHeight;
}
-(NSTimeInterval)animationInterval {
NSTimeInterval const kAnimationInterval = 0.2;
return kAnimationInterval;
}
#end
...
//
// DataPickerPresenter.h
// ShowDatePickerView
//
// Created by Christopher John Morris on 10/11/13.
// Copyright (c) 2013 Christopher John Morris. All rights reserved.
//
#import "PickerPresenter.h"
#protocol DataPickerPresenterDelegate;
#interface UIPickerViewPresenter : PickerPresenter
/*
* Singleton instance of class
*/
+ (id) defaultPresenter;
/*
* Calls back to register button clicks
*/
#property (nonatomic, strong) id<DataPickerPresenterDelegate>delegate;
- (void) setDataArrayWithArray:(NSArray *)array;
#end
/*
* Delegate that is used to send messages from subclasses
* to classes using the subclass.
*/
#protocol DataPickerPresenterDelegate
#required
- (void) dataPickerPresenerClickedCancel:(UIPickerViewPresenter *)picker;
- (void) dataPickerPresenerClickedDone:(UIPickerViewPresenter *)picker withString:(NSString *)string index:(NSInteger)index;
#end
...
//
// DataPickerPresenter.m
// ShowDatePickerView
//
// Created by Christopher John Morris on 10/11/13.
// Copyright (c) 2013 Christopher John Morris. All rights reserved.
//
#import "UIPickerViewPresenter.h"
#interface UIPickerViewPresenter() <PickerPresenterButtonDelegate, UIPickerViewDelegate, UIPickerViewDataSource>
#property (nonatomic, strong) UIPickerView *picker;
#property (nonatomic, strong) NSArray *pickerData;
#end
#implementation UIPickerViewPresenter
+(id)defaultPresenter {
static UIPickerViewPresenter *defaultPresenter = nil;
if (!defaultPresenter) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultPresenter = [[super allocWithZone:NULL] init];
});
}
return defaultPresenter;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self defaultPresenter];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init {
self = [super init];
if (self) {
[self initializePicker];
buttonDelegate = self;
}
return self;
}
- (void) initializePicker {
self.picker = [[UIPickerView alloc] init];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGRect frame = self.picker.frame;
frame.origin.y = frame.origin.y + self.navBarHeight;
frame.size.width = screenRect.size.width;
self.picker.frame = frame;
self.picker.delegate = self;
self.picker.dataSource = self;
[pickerContainer addSubview:self.picker];
}
-(void)didCancel {
if (self.hideOnCancelClicked) {
[self hide];
}
[self.delegate dataPickerPresenerClickedCancel:self];
}
-(void)didSave {
if (self.hideOnDoneClicked) {
[self hide];
}
NSInteger selectedRow = [self.picker selectedRowInComponent:0];
NSString *selection = [self.pickerData objectAtIndex:selectedRow];
[self.delegate dataPickerPresenerClickedDone:self withString:selection index:selectedRow];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.pickerData.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [self.pickerData objectAtIndex:row];
}
- (void)setDataArrayWithArray:(NSArray *)array {
self.pickerData = array;
[self.picker reloadAllComponents];
}
#end
To initialize it, write:
UIPickerViewPresenter *presenter = [UIPickerViewPresenter defaultPresenter];
[presenter setDataArrayWithArray:self.dataArray];
where 'self.dataArray' is the data you would like to use to populate the UIPickerView. To display it, call:
[presenter show];
When you have an UITableView perhaps you can use this: https://github.com/aberger/ABMExpandingTableViewCells