How do I customize a UIButton to behave like a table view cell?
I've tried make it in Interface Builder, but since the button is invisible and have no image, it's never gets highlighted when you press it. I want this grayed out behavior like on a table view cell.
Does anyone knows how to do it?
Simple way ever:
Make a 10x10px gray square png image, save it and name it like "greayButtBkg.png" and drag it into SupportingFiles in XCode.
Declare an Outlet of your button into your .h file and an IBAction method into your .m file.
yourFile.h:
#property (weak, nonatomic) IBOutlet UIButton *myButton;
yourFile.m:
- (IBAction)pressButton:(id)sender {
[_myButton setBackgroundImage:[UIImage imageNamed:#"greyButtBkg"] forState:UIControlStateHighlighted];
}
Your Button will get a grey background color when you'll tap it, and go back to its Default clearColor when you'll release your finger.
Hope this helps!
I have created a Custom Control that would behave like UITableViewCell. See code below
MyButton.h
#import <UIKit/UIKit.h>
#interface MyButton : UIView
- (id)initWithFrame:(CGRect)frame selector:(SEL)sel target:(id)target;
#end
MyButton.m
#import "MyButton.h"
#import <QuartzCore/QuartzCore.h>
#interface MyButton()
#property(assign)SEL selector;
#property(assign)id target;
#end
#implementation MyButton
- (id)initWithFrame:(CGRect)frame selector:(SEL)sel target:(id)target
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.selector=sel;
self.target=target;
self.backgroundColor=[UIColor darkGrayColor];
self.layer.cornerRadius=5.0;
UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
[self addGestureRecognizer:tapGesture];
}
return self;
}
-(void)tapAction:(id)sender{
[UIView animateWithDuration:.2 animations:^{
[self setBackgroundColor:[UIColor lightGrayColor]];
}];
[self performSelector:#selector(deSelect) withObject:nil afterDelay:.2];
}
-(void)deSelect{
[UIView animateWithDuration:.2 animations:^{
[self setBackgroundColor:[UIColor darkGrayColor]];
} completion:^(BOOL finished) {
[_target performSelector:_selector withObject:self];
}];
}
/*- (void)drawRect:(CGRect)rect
{
}*/
#end
Since, It uses UIView you can easily add, or draw anything on this control, and easily customize this.
Hope this helps.
Cheers.
Related
I am showing an alert on my UITableView.
This is the code
-(void)showAlertMaintenance{
AppDelegate * appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
[RMUniversalAlert showAlertInViewController:self withTitle:nil message:appDelegate.maintenanceStr cancelButtonTitle:#"OK" destructiveButtonTitle:nil otherButtonTitles:nil tapBlock:^(RMUniversalAlert * _Nonnull alert, NSInteger buttonIndex) {
if(buttonIndex == alert.cancelButtonIndex){
}
}];
}
The button is a subviews of one section header in my UITableview
UIButton *sendInviteCodeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[sendInviteCodeButton addTarget:self
action:#selector(shareInvitationCode)
forControlEvents:UIControlEventTouchUpInside];
[sendInviteCodeButton setTitle:#"招待コードをシェアする" forState:UIControlStateNormal];
[sendInviteCodeButton sizeToFit];
[sendInviteCodeButton setTitleColor:[UIColor colorWithRed:215.0f/255.0f green:116.0f/255.0f blue:52.0f/255.0f alpha:1.0f] forState:UIControlStateNormal];
sendInviteCodeButton.translatesAutoresizingMaskIntoConstraints = NO;
sendInviteCodeButton.frame = CGRectMake(20, 155.0f, SCREEN_BOUNDS_SIZE_PORTRAIT_WIDTH - 40, 30.0f);
[sendInviteCodeButton.layer setMasksToBounds:YES];
[sendInviteCodeButton.layer setCornerRadius:5.0f];
[sendInviteCodeButton.layer setBackgroundColor:[UIColor whiteColor].CGColor];
[sendInviteCodeButton setBackgroundColor:[UIColor whiteColor]];
[sendInviteCodeButton.titleLabel setFont:[UIFont boldSystemFontOfSize:13]];
[invitationView insertSubview:sendInviteCodeButton atIndex:0];
Before
After
When the alert is showed, the button is being pushed to top left corner of the screen.
Please help!
If you already calculate specific frame for your button, you should remove [sendInviteCodeButton sizeToFit];.
Just remove [sendInviteCodeButton sizeToFit];
Hope it help. :)
Since you are relying on frames to setup the layout you should have a dedicated UIView subclass for your tableview header.You need to override layoutSubviews method because at this point the header view will have it's own frame correctly set so your relative calculations for subviews (including the button) will make sense. Here's a quick sample code:
#import <UIKit/UIKit.h>
#interface MyTableViewHeader : UIView
#end
#implementation MyTableViewHeader
-(instancetype)init
{
self = [super init];
if (self) {
//setup button here (omit setting frames)
}
return self;
}
-(void)layoutSubviews
{
//set the button frame here
[super layoutSubviews];
}
#end
#interface ViewController : UIViewController<UITableViewDelegate>
#property (nonatomic, strong) MyTableViewHeader *header;
#end
#implementation ViewController
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
//You will probably want some logic here
//to decide if you want to display the header
if (!self.header) {
self.header = [MyTableViewHeader new];
}
return self.header;
}
#end
As an alternative you could also setup autolayout for your table header view.
I found out the problem caused by this
[sendInviteCodeButton.layer setMasksToBounds:YES];
setMasksToBounds - is a Boolean indicating whether sublayers are clipped to the layer’s bounds
So I have a few different view controllers that I want to have login screens over, which are just a simple text box over a blurred screen. Thus, I thought the best idea would be to make a superclass called Login that all the different view controllers could use. Here's the code for Login.h:
#import <UIKit/UIKit.h>
#interface Login : UIViewController
#property (strong, nonatomic) UIVisualEffectView *blurEffectView;
#property (strong, nonatomic) UITextField *pass;
- (void) enter;
#end
Login.m:
#import "Login.h"
#interface Login () {
NSString *password;
}
#end
#implementation Login
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"In the Login viewDidLoad");
[self presentLogin];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) presentLogin {
NSLog(#"Presenting the login screen");
[self.view setBackgroundColor:[UIColor whiteColor]];
self.pass = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[self.pass setCenter:self.view.center];
[self.view addSubview: self.pass];
[self.pass addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
if (!UIAccessibilityIsReduceTransparencyEnabled()) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
self.blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
self.blurEffectView.frame = self.view.bounds;
self.blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view insertSubview:self.blurEffectView belowSubview:self.pass];
}
}
- (void) textFieldDidChange: (UITextField *) t {
if ([self.pass.text isEqualToString:[NSString stringWithFormat:#"17"]]) {
[self.blurEffectView removeFromSuperview];
[self.pass removeFromSuperview];
[self enter];
}
NSLog(#"You're changing the text.");
}
- (void) enter {
//to implement in the subclasses individually
}
#end
The subclass (I am just trying to make one so far) is empty except for a definition of "enter" which simply prints out "login successful". The only output I am getting when I run this is:
In the Login viewDidLoad
Presenting the login screen
Nothing shows up on the screen: just white. I assume this is because I am trying to modify the self.view of the subclass, not the superclass, since the subclass is the thing that is actually getting presented. Is there a better way of doing this? Some other design pattern that I am not thinking of? Or is there an easy way to get around this?
Edit: I just realized that the code I was running was slightly different from what I pasted here. I now updated it, but only the blur shows up, not the text field. Also I realized I had the CGRect wrong, it should be something like CGRectMake(0,0,100,20); so I fixed that, and the text field still doesn't show. Is there a reason that might be happening.
Set your height and width and also set your background color to white . for now it is taking transparent text view
self.pass = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[self.pass setCenter:self.view.center];
[self.pass setBackgroundColor:[UIColor whiteColor]]; // default taking clear color for now
I'm am trying to delete an image while it is moving on the screen using UITapGestureRecognizers and UIViewAnimation. I have used the UIViewAnimationOptionAllowUserInteraction, but I still get the SIGBART error. I have been looking through the internet and couldn't find a solution.
This is my .m file
#import "AbcViewController.h"
#interface AbcViewController ()
#end
#implementation AbcViewController
//single tap gesture action /deletes person
-(void) tapped:(UITapGestureRecognizer *)recognizer{
[recognizer.view removeFromSuperview];
}
//double tap gesture action
-(void) tapped2:(UITapGestureRecognizer *)recognizer{
[recognizer.view removeFromSuperview];
}
- (void)viewDidLoad
{
[super viewDidLoad];
//this makes a zombie/person
UIImageView *zombieView =[[UIImageView alloc] initWithFrame:CGRectMake(135 , 300, 50, 75)];
UIImage *zombie=[UIImage imageNamed:#"free-vector-stick-figure-clip-art_105575_Stick_Figure_clip_art_hight.png"];
[zombieView setImage:zombie];
[self.view addSubview:zombieView];
[self.view insertSubview:zombieView belowSubview:_Railing];
[zombieView setUserInteractionEnabled:TRUE];
// double tap gesture recognizer
UITapGestureRecognizer *touch2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped2:)];
[touch2 setDelegate: self];
[touch2 setNumberOfTapsRequired: 2];
[zombieView addGestureRecognizer:touch2];
//tap gesture recognizer
UITapGestureRecognizer *touch = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[touch setDelegate:self];
[touch setNumberOfTapsRequired: 1];
[zombieView addGestureRecognizer:touch];
//This makes the Person move down until he is behind the railing
[UIView animateWithDuration:8.5
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
animations:^{
CGAffineTransform transform= CGAffineTransformMakeTranslation(0,-200);
zombieView.transform = transform;
}
completion:nil];
};
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This is my .h file:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface AbcViewController : UIViewController <UIGestureRecognizerDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *Railing;
//#property (strong, nonatomic) IBOutlet UIImageView *Person;
#property (strong, nonatomic) IBOutlet UIImageView *zombieView;
#property (strong, nonatomic) IBOutlet UIImage *zombie;
-(void) tapped:(UITapGestureRecognizer *)recognizer;
-(void) tapped2:(UITapGestureRecognizer *)recognizer;
#end
Every time I click it in mid-animation I get an error but when it's complete it works fine. I am fairly new to this so I'm sure it's simple, it always is. But, thank you for your patience and answers!
The object during the animation is not where you see it. It has already moved to its final position. Animation is an illusion: the "presentation layer" is animated, but the underlying "model layer" (the real object) stands still and has already moved when the animation starts. The model layer is the one inside a view so it is the only one that can be tapped.
Tapping an object while it is being animated is therefore a very tricky problem. I discuss it here:
http://www.apeth.com/iOSBook/ch18.html#_hit_testing_during_animation
Perhaps that discussion will give you some ideas. But no matter what, this is not going to be easy.
(Consider that perhaps you should not be using view animation for this kind of game. Sprite Kit was invented for just this sort of thing.)
Simple code is here:
Custom MyButton.h
#interface PaiLifeReadingListButton : UIButton
#property (strong, nonatomic) UIImageView *logoImageView;
#end
Custom MyButton.m
#implementation PaiLifeReadingListButton
#synthesize logoImageView = _logoImageView;
-(id)init
{
_logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(33.0, 20.0, 80.0, 80.0)];
[_logoImageView setImage:[UIImage imageNamed:#"pic"]];
[self addSubview: _logoImageView];
}
#end
Custom MyView.h:
#property (strong, nonatomic) Mybutton *mybutton;
Custom MyView.m:
-(id) init
{
myButton = [[MyButton alloc] init];
[self addSubview:myButton];
}
ViewController.m:
#synthesize myView;
- (void)viewDidLoad
{
myView = [[Myview alloc] init];
[myView.myButton addTarget:self action:#selector(pressed:) forControlEvents:UIControlEventTouchDown];
self.view = myView;
}
-(void)pressed : (MyButton *)button
{
UIImageView *newImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"other-pic"] highlightedImage:nil];
myView.myButton.logImageView = newImageView;
[self.view setNeedsDisplay]; //----this does not work
}
When I press the button, the imageview does not change. How can I fix this?
Thank you very much!
First thing , you don't need setNeedsDisplay here. setNeedsDisplay is used for redrawing the view (it internally calls the drawRect: method of the view if required) which is not what you want.
Second, you have given the class of your button as MyButton,but in the actual .h and .m files, it is given as #interface PaiLifeReadingListButton and #implementation PaiLifeReadingListButton respectively.
Another thing, the view and button is not initialized with a frame. But I assume you have done that already, since your only problem is the image view not changing.
In your code in pressed method you should change views in the hierarchy, but you change only the object member. You can do something like
myView.myButton.logImageView.image = [UIImage imageNamed:#"other-pic"];
I want to animation change button title. example, the title of button is "one", after I click that button, fade out "one" and then fade in "NewTitle".
Rather than messing around with duplicate views, simple fade transitions can be made with the CATransition class.
// CATransition defaults to fade
CATransition *fade = [CATransition animation];
// fade.duration = ...
[button.layer addAnimation:fade];
[button setTitle:#"New title" forControlState:UIControlStateNormal];
The button will fade to its new state. This works for labels, entire view hierarchies, whatever.
So creating two buttons was not a good Idea so I have created a simple project to test code for your question and here is what I come up with
viewcontroller.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UIButton *myButton;
}
#property(nonatomic,strong) IBOutlet UIButton *myButton;
-(IBAction)animateFadeOutButtonTitle:(id)sender;
-(void)animateFadeInButtonTitle;
#end
viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myButton=_myButton;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[_myButton setTitle:#"One" forState:UIControlStateNormal];
}
-(IBAction)animateFadeOutButtonTitle:(id)sender
{
[UIView animateWithDuration:0.25 animations:^{_myButton.titleLabel.alpha = 0.0;}];
[self performSelector:#selector(animateFadeInButtonTitle) withObject:self afterDelay:1.0];
}
-(void)animateFadeInButtonTitle;
{
[_myButton setTitle:#"New Title" forState:UIControlStateNormal];
[UIView animateWithDuration:2.0
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:^{_myButton.titleLabel.alpha = 1.0;}
completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end