I've placed a UIButton inside a custom UIView, CustomButtonView. The UIButton is not responding to tap/click events, and its appropriate selector method is not being triggered. Here's my code below; what have I done wrong?
As well, the custom view has a background color of red. When I add the custom view to my view controller's view, the red rectangle displays. However, when I add my UIButton to my custom view (with the background color of red), only the UIButton displays, not the red rectangle. Why is this happening?
//
// CustomButtonView.h
//
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface CustomButtonView : UIView
#property (strong, nonatomic) UIImageView *buttonImageView;
#property (strong, nonatomic) UIButton *button;
#end
NS_ASSUME_NONNULL_END
//
// CustomButtonView.m
//
#import "CustomButtonView.h"
#implementation CustomButtonView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor redColor];
_button = [UIButton buttonWithType:UIButtonTypeSystem];
_button.backgroundColor = [UIColor greenColor];
[_button setTitle:#"john doe" forState:UIControlStateNormal];
[_button setTitle:#"john doe" forState:UIControlStateHighlighted];
_button.translatesAutoresizingMaskIntoConstraints = NO;
_button.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
_button.titleLabel.textColor = [UIColor whiteColor];
self.userInteractionEnabled = NO;
self.button.userInteractionEnabled = YES;
//[self addSubview:_button];
//[_button.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
//[_button.leadingAnchor constraintEqualToAnchor:self.leadingAnchor].active = YES;
}
return self;
}
#end
//
// ViewController.m
// TestApp
//
//
#import "ViewController.h"
#import "CustomButtonView.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor yellowColor];
_customButtonView = [[CustomButtonView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
_customButtonView.backgroundColor = [UIColor redColor];
_customButtonView.translatesAutoresizingMaskIntoConstraints = NO;
[_customButtonView.button addTarget:self action:#selector(customButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_customButtonView];
}
- (void)customButtonTapped:(id)sender{
NSLog(#"Button tapped!");
}
#end
Here are some screenshots, below. Please click on the links.
When I display the UIButton inside the custom-view, only the button displays. The red rectangle of the custom-view doesn't display.
When I simply add the custom-view to my view-controller (ie with no UIButton added to the custom view), the red rectangle of the custom-view displays.
When you disable user interaction for a specific view then this user interaction is not forwarded to its subviews. Or in other words; for user interaction to work you need to make sure that all superviews in chain (superview of superview of superview) do have user interaction enabled.
In your case you have self.userInteractionEnabled = NO; which means that any subview of self (including your button) will not accept any user interaction.
Next to this issues it is still possible that other issues are present. For instance: A subview will accept user interaction only within physical area of all of its superviews. That means that if a button with frame { 0, 0, 100, 100 } is placed on a superview of size { 100, 50 } then only the top part of the button will be clickable.
As for the size changes this is a completely different story...
You decided to disable translation of autoresizing mask on your custom view by writing _customButtonView.translatesAutoresizingMaskIntoConstraints = NO;. That means that you will not use frame in conjunction with other constraints. It means that whatever frame you set (in your case CGRectMake(0, 0, 100, 100)) this frame will be overridden by anything else that may effect the frame of your custom view.
In case where your button code is commented out there is no such thing that "may effect the frame of your custom view" and the view stays as size of { 100, 100 }. But as soon as you added a button you added additional constraints which most likely lead to resizing your button to sizeToFit and with it its superview which is your custom view.
To avoid this you need to either remove disabling of translation of autoresizing mask into constraint on your custom view. Or you need to define custom view size by adding constraints to it so that they are set to { 100, 100 }. You can simply constrain heightAnchor and widthAnchor to 100.
Related
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 wondering how one would go about programming the kind of dropdown tableview that the Vine app uses. If you have never used Vine, I've provided a picture below which depicts the UI Design I'm talking about. Essentially, when you press the left hand UIBarButton, this tableview drops down. When you touch anywhere again, it drops down a little further (5 or 10 pixels) and then leaves the screen with a nice animation.
Just looking for some feedback on how I might go about implementing this. Thanks in advance.
Not sure a UITableView is the way to go about it.
Perhaps you can use REMenu available on Github to get inspired or fork it to customize to your needs.
The REMenu is as close to an exact copy as you can get. I did notice though that it wasn't clipping the top of the menu when it slid down, it slid underneath the status / nav bar which to me didn't look right. Without having looked at the sliding logic (and with my impressive SE reputation of "8"), this is my quick take on how you make the menu appear.
create a view for the contents of the menu (the table view etc)
put it in an enclosing menu collapsed to a zero height, with the content sticking off the top of the collapsed menu view
set the menu view to clip the contents so the top of the menu is not visible, then animate the contents down, as you animate the menu height larger.
This sample uses a simple gradient for the contents of the menu.
#interface BackgroundLayer : NSObject
+(CAGradientLayer*) redBlueGradient;
#end
#implementation BackgroundLayer
+ (CAGradientLayer*) redBlueGradient
{
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors =
#[(id) [UIColor redColor].CGColor, (id) [UIColor blueColor].CGColor];
headerLayer.locations = nil;
return headerLayer;
}
#end
#interface ViewController ()
#property (nonatomic, strong) UIButton* doIt;
#property (nonatomic, strong) UIView* menu;
#property (nonatomic, strong) UIView* nestedView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create simple toggle button to test the menu
self.doIt = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.doIt.frame = CGRectMake(50, 50, 50, 44);
[self.doIt setTitle:#"Doit!" forState:UIControlStateNormal];
[self.doIt sizeToFit];
[self.doIt addTarget:self action:#selector(doIt:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.doIt];
// menu
self.menu = [[UIView alloc] initWithFrame:CGRectMake(20, 200, 280, 0)];
self.menu.layer.borderColor = [UIColor blackColor].CGColor;
self.menu.layer.borderWidth = 3.0;
self.menu.clipsToBounds = YES;
// menu contents
self.nestedView = [[UIView alloc] initWithFrame:CGRectMake(0, -100, 280, 100)];
CAGradientLayer *background = [BackgroundLayer redBlueGradient];
background.frame = self.nestedView.bounds;
[self.nestedView.layer addSublayer:background];
[self.nestedView clipsToBounds];
[self.menu addSubview:self.nestedView];
[self.view addSubview:self.menu];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) doIt:(id) sender
{
if (!CGRectEqualToRect(self.nestedView.frame, CGRectMake(0, 0, 280, 100)))
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 100);
self.nestedView.frame = CGRectMake(0, 0, 280, 100);
}];
}
else
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 0);
self.nestedView.frame = CGRectMake(0, -100, 280, 100);
}];
}
}
#end
Cheers.
The Problem with the REMenu is, it creates the viewControllers every time the user taps on particular section, which should not be the case. It should persist the state of each screen attached there.
I have a UITextField subclass object and leftView with leftViewMode set to UITextFieldViewModeUnlessEditing. However, the left view remains visible during editing until the user enters at least one character.
Before entering text:
After entering Text:
Questions:
Is this behavior expected and documented? How can I ensure that the
left view is invisible during editing, before any character is
entered?
In answer to your questions:
Yes, that appears to be expected UITextField behavior, though it's not clearly spelled out in any official documentation that I could find.
You can further customize the default behavior by implementing UITextFieldDelegate methods to, for example, remove the overlay view as soon as editing begins, even if the text field is empty.
For example, let's assume you defined a property to hold a strong reference to a custom overlay view:
#interface ViewController () <UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *textField;
#property (strong, nonatomic) UIView *customView;
#end
You could then configure the overlay view and register for delegate notifications in viewWillAppear:, as shown below:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 30.0, 30.0)];
self.customView.backgroundColor = [UIColor orangeColor];
self.textField.leftView = self.customView;
self.textField.leftViewMode = UITextFieldViewModeUnlessEditing;
self.textField.delegate = self;
}
To make the overlay view disappear as soon as the user touches the text field, set the leftView (or rightView) property to nil when editing begins. Just make sure to set it to point to your custom view again when editing ends.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.textField.leftView = nil;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.textField.leftView = self.customView;
}
Edit
A more generalized approach would be to simply toggle the mode on editing beginning and ending, which would eliminate the need to maintain a reference to the overlay view.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.textField.leftViewMode = UITextFieldViewModeNever;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.textField.leftViewMode = UITextFieldViewModeUnlessEditing;
}
Please try this code with UITextFieldViewModeAlways and assign padding with as par star image width.
Put this code in viewDidLoad method
UIImageView *iView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#" "]];
[UITextField appearanceWhenContainedIn:[UISearchBar class], nil].leftView = iView;
It will manage place holder text as well as textField text with left padding.
I hope it will help you.
You need to set Mode of textfield after that it will work perfect. So use this one
leftViewMode = UITextFieldViewModeAlways;
instead of
leftViewMode = UITextFieldViewModeUnlessEditing;
create leftview
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;
and you can add UIImageView on this paddingView for your star.png
UIImageView *img=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"star.png"]];
img.frame=CGRectMake(0, 0,5, 20);
[paddingView adsubview:img];
I have a UIView that draws a pie chart and I would like to put 3 or maybe 4 charts in a UIScrollView programatically. How can I do that?
my h file is like this
#class PieChart;
#interface ViewController : UIViewController {
}
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
and m file
- (void)viewDidLoad
{
[super viewDidLoad];
[scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setCanCancelContentTouches:NO];
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
PieChart *chart = [[PieChart alloc]initWithFrame:CGRectZero];
[chart setFrame:CGRectMake(100, 100, 200, 200)];
[scrollView addSubview:chart];
}
If you need to add subview to the view you need to use [view addSubview:subview]. This is common practice.
Read about frames and bounds (coordinate systems for view and sub views):
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaViewsGuide/Coordinates/Coordinates.html
As I understand you have a problem due to content size of scroll view. Read more here:
http://developer.apple.com/library/ios/#DOCUMENTATION/WindowsViews/Conceptual/UIScrollView_pg/Introduction/Introduction.html
Hope it helps.
You can just add it with:
[scrollView addSubview:singleView];
You add multiple subviews to a UIScrollView just like you add a single subview; with addSubview:. What problems are you running into?
I have a little bit specific question. It might not matter for most people but I have had to deal with it and I had to solve the issue described below. I tried to find some information about it using Google and the Apple SDK documentation but did not succeed.
I was a designing a screen where there were many images in horizontal scrolls. There three three same scrolls. Every scroll had title. I have implemented custom class derived from UIView and placed there UIScrollView for scroll and UILabel for title text:
#interface MyView : UIView {
UIScrollView *iScrollView;
UIView *iTitleView;
}
I then put objects of this class on the view of a UIViewController:
#implementation MyViewController
- (void) viewDidLoad {
[super viewDidLoad];
...
iScrollViewTop = [[MyView alloc] init];
[self.view addSubview:iScrollViewTop];
...
}
#end
When I filled the internal scroll view with images and ran my application it looked OK. But there was some strange behavior. First, scroll did not have bounces as if I had set
iScrollView.bounces = NO;
and second, when I swiped to scroll, after the scroll stopped, the scroll bar did not disappear within one second. It was strange for me, because when I usually create a UIScrollView and add it to the UIViewController's view it has bounces and scroll bar disappears immediately when it stops. I tried to change UIScrollView's properties, such as directionalLockEnabled, pagingEnabled, canCancelContentTouches, delaysContentTouches, decelerationRate and others. In fact, I have tried to change almost all properties of UIScrollView but I could not get the scroll bars to immediately disappear.
If I try to add UIScrollView instead MyView to the UIViewController.view, it bounces and scroll bar disappears immediately after it stops. Also I get correct behavior if I subclass MyView from UIScrollView but in this case I cannot manage the title label because it scrolls together with other content.
So here are my questions:
Do you know why I am seeing this behavior?
How can I get "usual" behavior for scroll encapsulated by UIView?
ok, hacky code follows, so ignore all my other issues, but follow this pattern (westie.jpg = image that was 360x200)
#interface MyView : UIView
{
UIScrollView *sv;
UILabel *l;
}
-(MyView*)initWithFrame:(CGRect)frame;
#end
#implementation MyView
-(MyView*)initWithFrame:(CGRect)frame;
{
self = [super initWithFrame:frame];
sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,360,200)];
sv.scrollEnabled = YES;
sv.contentSize = CGSizeMake(360*3,200);
[self addSubview:sv];
UIImage *i1 = [UIImage imageNamed:#"westie.jpg"];
UIImageView *iv1 = [[UIImageView alloc] initWithImage:i1];
iv1.frame = CGRectMake(360*0, 0, 360, 200);
[sv addSubview:iv1];
UIImageView *iv2 = [[UIImageView alloc] initWithImage:i1];
iv2.frame = CGRectMake(360*1, 0, 360, 200);
[sv addSubview:iv2];
UIImageView *iv3 = [[UIImageView alloc] initWithImage:i1];
iv3.frame = CGRectMake(360*2, 0, 360, 200);
[sv addSubview:iv3];
l = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
l.text = #"Hello World";
l.backgroundColor = [UIColor blueColor];
[self addSubview:l];
return self;
}
#end
later, in your outer view creation:
[window addSubview:[[MyView alloc] initWithFrame:CGRectMake(0, 20, 360, 200)]];