Half of screen is unclickable - ios

I have a very perplexing problem. I have a UIView with a few buttons spread across it. For whatever reason when you rotate the view(simulator or device) the buttons on the right half of the screen become unclickable. The unclickable area resembles the size of the orientation that was just rotated from. For example, if you have an iPhone in portrait mode and turn it to landscape the unclickable area is about the width of the iPhone screen in portrait. At first I thought that their must be an invisible element on top of that portion of the screen that was intercepting the clicks, but then I set the alpha of everything on screen to 1 and set every elements border color (via CALayer) so I could inspect. Surprisingly everything was where it was supposed to be and their was nothing(that I could find) covering the buttons. I even tried bringing the buttons to the front via [self.view bringSubviewToFront:self.BUTTON]
I have one function that handles setting the frames of everything when the view is either loaded or rotated. This function call is the ONLY thing in my rotation function and my viewWillAppear function. The unclickable buttons problem only happens when the device is rotated. For example, if you rotate the device from portrait to landscape and the buttons become unclickable, then all you have to do is select another tab from the tab bar at the bottom to load another view, then when you click back on the tab with the view with this weird problem(not having rotated anything), the buttons are all clickable and working as expected. The thing I just cant comprehend about this problem is that the EXACT same code is called in viewWillAppear and didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation yet the buttons are only unclickable when the device is rotated and the buttons are clicked. If the user clicks on another tab and then returns the the tab with this problem, the buttons now work as expected.
To complicate things further, this problem ONLY occurs on ios7. Everything is all fine and dandy on ios6.
I have tried everything I can think of and I just don't have a clue as to why this is happening.
Here is some of the code:
-(void)loadCorrectElements{
if(self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown){
//portrait
int amountToSubtract = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)?20:0;
int distanceBetweenLabels = (self.view.bounds.size.height - amountToSubtract) / 5;
self.viewForLabels.frame = CGRectMake(0, distanceBetweenLabels, self.view.bounds.size.width, distanceBetweenLabels*3);
int heightOfLabels = (self.viewForLabels.bounds.size.height / 3) - 20;
int ycoordOfLongitudeLabel = (self.viewForLabels.bounds.size.height / 3) + 10;
int ycoordOfAltitudeLabel = ((self.viewForLabels.bounds.size.height / 3) * 2) + 10;
self.latitudeLabel.frame = CGRectMake(10, 10, self.view.bounds.size.width-20, heightOfLabels);
self.longitudeLabel.frame = CGRectMake(10, ycoordOfLongitudeLabel, self.view.bounds.size.width-20, heightOfLabels);
self.altitudeLabel.frame = CGRectMake(10, ycoordOfAltitudeLabel, self.view.bounds.size.width-20, heightOfLabels);
self.optionsButton.frame = CGRectMake(self.view.bounds.size.width-36, self.view.bounds.size.height-36, 26, 26);
int ycoordForSpeakButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + 10;
self.speakButton.frame = CGRectMake(self.view.bounds.size.width - 40, ycoordForSpeakButton, 30, 25);
int heightOfRecordLabel = ((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height))) / 2;
int yCoordForRecordButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + (((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height)) / 2) - (heightOfRecordLabel / 2));
self.recordButton.frame = CGRectMake((self.view.bounds.size.width / 2) - (self.view.bounds.size.width / 4) / 2, yCoordForRecordButton, self.view.bounds.size.width / 4, heightOfRecordLabel);
int ycoordForOldCoordsButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + (((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height))) / 2 - 12);
self.oldCoordsButton.frame = CGRectMake(10, ycoordForOldCoordsButton, 24, 24);
}else{
//landscape
int amountToSubtract = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)?20:0;
self.viewForLabels.frame = CGRectMake(5, 5 + amountToSubtract, self.view.bounds.size.width-10, self.view.bounds.size.height - 60);
int heightOfLabels = (self.viewForLabels.bounds.size.height - amountToSubtract) / 3;
int ycoordOfLongitudeLabel = (self.viewForLabels.bounds.size.height / 3);
int ycoordOfAltitudeLabel = ((self.viewForLabels.bounds.size.height / 3) * 2);
self.latitudeLabel.frame = CGRectMake(5, 0, self.view.bounds.size.width-20, heightOfLabels);
self.longitudeLabel.frame = CGRectMake(5, ycoordOfLongitudeLabel, self.view.bounds.size.width-20, heightOfLabels);
self.altitudeLabel.frame = CGRectMake(5, ycoordOfAltitudeLabel, self.view.bounds.size.width-20, heightOfLabels);
int yCoordForOptionsButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + (((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height)) / 2) - 13);
self.optionsButton.frame = CGRectMake(self.view.bounds.size.width - 31, yCoordForOptionsButton, 26, 26);
self.speakButton.frame = CGRectMake(self.optionsButton.frame.origin.x - 40, self.optionsButton.frame.origin.y, 30, 25);
int heightOfRecordLabel = ((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height))) / 2;
int yCoordForRecordButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + (((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height)) / 2) - (heightOfRecordLabel / 2));
self.recordButton.frame = CGRectMake((self.view.bounds.size.width / 2) - (self.view.bounds.size.width / 4) / 2, yCoordForRecordButton, self.view.bounds.size.width / 4, heightOfRecordLabel);
int ycoordForOldCoordsButton = self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height + (((self.view.bounds.size.height - (self.viewForLabels.frame.origin.y + self.viewForLabels.frame.size.height))) / 2 - 12);
self.oldCoordsButton.frame = CGRectMake(10, ycoordForOldCoordsButton, 24, 24);
}//end if
[self setLabelText:self.latitudeLabel text:self.latitudeLabel.text];
[self setLabelText:self.longitudeLabel text:self.longitudeLabel.text];
[self setLabelText:self.altitudeLabel text:self.altitudeLabel.text];
self.mainContainer.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
}//end method
-(void)viewWillAppear:(BOOL)animated{
[self loadCorrectElements];
}//end function
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[self loadCorrectElements];
}//end function
Here are the button declarations inside ViewDidLoad
//create options button
self.optionsButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.optionsButton setImage:[UIImage imageNamed:#"Share.png"] forState:UIControlStateNormal];
[self.optionsButton addTarget:self action:#selector(shareButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.mainContainer addSubview:self.optionsButton];
//create speak button
self.speakButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.speakButton setImage:[UIImage imageNamed:#"Sound.png"] forState:UIControlStateNormal];
[self.speakButton addTarget:self action:#selector(speakButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[self.mainContainer addSubview:self.speakButton];
//create record button
self.recordButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.recordButton.backgroundColor = self.blueColor;
[self.recordButton setTitle:NSLocalizedString(#"RECORD", nil) forState:UIControlStateNormal];
self.recordButton.layer.cornerRadius = 3.0f;
[self.recordButton addTarget:self action:#selector(recordButtonClicked) forControlEvents:UIControlEventTouchUpInside];
[self.recordButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted];
[self.mainContainer addSubview:self.recordButton];
//set up old coord button
self.oldCoordsButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.oldCoordsButton setImage:[UIImage imageNamed:#"Clock.png"] forState:UIControlStateNormal];
[self.oldCoordsButton addTarget:self action:#selector(oldCoordsButtonClicked) forControlEvents:UIControlEventTouchUpInside];
[self.mainContainer addSubview:self.oldCoordsButton];

It looks like the viewController's view or the mainWindow is not resizing on rotation.
As Jesse Rusak mentions in his answer, it really makes no sense to do so much calculations in your code to setup the buttons to handle rotation.
Use AutoResizingMasks. It can take out a zillion lines of code. Once you have mastered it, you can move to Auto Layout.
your autoresizing masks can be set somewhat like this
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFixedBottomMargin |
UIViewAutoresizingFixedLeftMargin |
UIViewAutoresizingFixedRightMargin |
UIViewAutoresizingFixedTopMargin;
similarly, you can set the autoresizing masks for the buttons. And it'll take care of positioning on rotation. Try a few different options, you'll get the hang of it soon.

It seems likely to me that a superview of your buttons is not being resized correctly. (Views can still be visible outside of their superview, but they won't be interactive.)
I would suggest using a tool like Reveal or Spark Inspector to see where your views are after rotation and so let you figure out which one isn't being resized correctly.
As an aside, you might want to look into using nibs and auto layout for your layouts in the future, as it would eliminate all of the above code.

Related

Horizontally distribute three buttons evenly ios

I have three UIButtons and a holder UIView I want all my three UIButton to be vertically centred inside the UIView and also I want them to be distributed equal horizontally
float ycentre = CGRectGetMidY(self.buttonHolderView.bounds);
float xposition = (self.buttonHolderView.frame.size.width - 135.0f)/4.0f;
self.button1 = [[UIButton alloc]initWithFrame:CGRectMake(xposition,0, 45, 30)];
self.button1.center = CGPointMake(self.button1.frame.origin.x, ycentre);
self.button2 = [[UIButton alloc]initWithFrame:CGRectMake((xposition*2)+45,0, 45, 30)];
self.button2.center = CGPointMake(self.button2.frame.origin.x, ycentre);
self.button3 = [[UIButton alloc]initWithFrame:CGRectMake((xposition*3)+90,0, 45, 30)];
self.button3.center = CGPointMake(self.button3.frame.origin.x, ycentre);
[self.button3 addTarget:self action:#selector(ShareButtonClicked) forControlEvents:UIControlEventTouchUpInside];
[self.buttonHolderView addSubview:self.button1];
[self.buttonHolderView addSubview:self.button2];
[self.buttonHolderView addSubview:self.button3];
Each button has height 30 and width 45 , but my code doesn't distribute them equally what is wrong?
Place them in a UIStackView. It makes spacing views evenly quite easy.
Set the frame of your buttons as follows and do Not set center
float thirdOfWidth = (self.buttonHolderView.frame.width/3.0);
float buttonWidth = 45.0;
float buttonHeight = 30.0;
self.button1 = [[UIButton alloc]initWithFrame:CGRectMake(thirdOfWidth*0 + (thirdOfWidth - buttonWidth)/2, (self.buttonHolderView.frame.height - buttonHeight)/2, 45, 30)];
self.button2 = [[UIButton alloc]initWithFrame:CGRectMake(thirdOfWidth*1 + (thirdOfWidth - buttonWidth)/2, (self.buttonHolderView.frame.height - buttonHeight)/2, 45, 30)];
self.button3 = [[UIButton alloc]initWithFrame:CGRectMake(thirdOfWidth*2 + (thirdOfWidth - buttonWidth)/2, (self.buttonHolderView.frame.height - buttonHeight)/2, 45, 30)];
It can be done also in loop with arbitrary count of buttons
NSInteger count = 3;
CGFloat buttonWidth = 45;
CGFloat buttonHeight = 30;
CGFloat x = (self.bounds.size.width - buttonWidth) / 2;
CGFloat verticalSpacing = ((self.bounds.size.height - buttonHeight * count) / count) - buttonHeight / 2;
CGFloat y = verticalSpacing;
for (int i = 0; i < count; i++) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(x, y, buttonWidth, buttonHeight)];
[self addSubview:button];
y += buttonHeight + verticalSpacing;
}

iOS, create custom popover style UIView

I know there is alot of documentation on this topic, and from what i've read i've learned a lot. My issue here is that i began creating a custom UIView within a viewcontroller & now when i'm trying to make it the UIView its own class, so as to be called & used within a working app, i'm pretty confused as to what i need to do to achieve it.
The overall aim of the class is to be able to select a button one a view controller, which will instantiate the custom view, then display. The visual effect for the user is that they click the button, then a window will animate-appear from the bounds of the button (although the code below uses the positioning of top left currently), then display a combination of switches & labels, along with a back & save button.
You can see from my code that what i need to achieve this process, i've managed to implement into a single function (again, this was from creating it within a viewcontroller). What i'm after now, and can't quite fathom out is for the custom view to be imported, then on the action of the main view controller's button, add the view to self.view.
- (void)makeBox{
//view bounds
CGFloat viewWidth = CGRectGetWidth(self.view.frame);
CGFloat viewHeight = CGRectGetHeight(self.view.frame);
//red box x, y & size
CGFloat startx = (viewWidth / 100) * 10;
CGFloat starty = (viewHeight /100) * 20;
CGFloat width = (viewWidth / 100) * 80;
CGFloat height = (viewHeight /100) * 70;
CGRect view1Frame = CGRectMake(startx, starty, width, height);
//label & switch frame
CGRect labelMR = CGRectMake(10, ((height / 100) * 12), ((width / 100) * 80) - 7.5, 25);
CGRect switchMR = CGRectMake(((width / 100) * 80) + 7.5, ((height / 100) * 11), ((width / 100) * 10), 15);
//this is repeated for 6 other labels & switches
CGRect backButtonR = CGRectMake(5, 5, 50, 35);
CGRect saveButtonR = CGRectMake((width - 50), 5, 50, 35);
if (!self.view1) {
self.switchM = [[UISwitch alloc] initWithFrame:switchMR];
self.switchM.tag = 10;
if(self.monday){ //self.monday refers to a BOOL property of the viewcontroller that this was originally made in
self.switchM.on = true;
} else{
self.switchM.on = false;
}
[self.switchM addTarget:self action:#selector(switched:) forControlEvents:UIControlEventTouchUpInside];
// this switch instantiation process is repeated 6 other times
self.backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.backButton addTarget:self
action:#selector(hideBox)
forControlEvents:UIControlEventTouchUpInside];
[self.backButton setTitle:#"Back" forState:UIControlStateNormal];
self.backButton.frame = backButtonR;
self.saveButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.saveButton addTarget:self
action:#selector(daysChanged)
forControlEvents:UIControlEventTouchUpInside];
[self.saveButton setTitle:#"Save" forState:UIControlStateNormal];
self.saveButton.frame = saveButtonR;
self.labelM = [[UILabel alloc] initWithFrame:labelMR];
self.labelM.layer.masksToBounds = YES;
self.labelM.layer.cornerRadius = 5;
self.labelM.layer.borderColor = [UIColor blackColor].CGColor;
self.labelM.layer.borderWidth = .5;
self.labelM.text = #" Monday";
self.labelM.backgroundColor = [UIColor whiteColor];
//again - repeated 6 times
//use this starting frame to make the 'popover' appear small at first
CGRect startFrame = CGRectMake(10, 10, 10, 10);
self.view1 = [[UIView alloc] initWithFrame:startFrame];
[self.view addSubview:self.view1];
[UIView animateWithDuration:0.5 animations:^(void){
self.view1.layer.cornerRadius = 10;
self.view1.layer.borderColor = [UIColor blackColor].CGColor;
self.view1.layer.borderWidth = .5;
[self.view1 setBackgroundColor:[UIColor lightGrayColor]];
self.view1.frame = view1Frame;
} completion:^(BOOL finished){
[self.view1 addSubview:self.labelM];
[self.view1 addSubview:self.switchM];
//repeat 6 times for other labels & switches
}];
}
else {
[UIView animateWithDuration:0.3 animations:^() {
self.view1.frame = view1Frame;
} completion:^(BOOL finished) {
self.labelM.frame = labelMR;
self.switchM.frame = switchMR;
//repeated for the other 6
self.backButton.frame = backButtonR;
self.saveButton.frame = saveButtonR;
}];
}
}
-(void) hideBox{
//back button was selected
[UIView animateWithDuration:0.5 animations:^(void){
[self.view1 removeFromSuperview];
self.view1 = nil;
}];
}
I understand that as i'm doing/calling this via code, i need to override either init or initWithFrame:. Do i pass the initWithFrame: the containing view controllers bounds, of which the custom view can be calculated on, and how do i handle the animation?
I've managed to set up the delegate protocol which will notify when the save button is pressed. that part i understand, its just the rest of the parts i'm unclear on.
First some background.
Typically for a popover controller, you have a single transparent root view which contains the popover view itself. This root view is the size of the containing window and usually higher in z-order than all other views. There are two reasons for this:
1) The popover needs to draw on top of all other views. If the root view wasn't on top of all other views, the popover could be clipped and not draw all of its contents.
2) The user usually needs some place to tap and dismiss the popover (effectively a cancel action).
UIPopoverController does this as do some 3rd party popovers. Some even go so far as to use their own UIWindows.
To answer your specific questions:
1) initWithFrame: / setFrame: should be passed a CGRect that is in the coordinate space of the view that will contain the view. In other words, the superview. If you decide to use a transparent root view, the popover (the part that draw on screen) will be passed a CGRect in the coordinate space of the transparent root view.
2) Usually a popover is animated like so:
// set initial frame
popover.frame = CGRectMake(x, y, width, 0);
popover.alpha = 0.0;
[rootView addSubview:popover];
[UIView animateWithDuration:0.5
animations:^{
CGRect frame = popover.frame;
frame.size.height = some-height;
popover.frame = frame;
popover.alpha = 1;
}
];
I actually got it to work! (although i can't promise this is the cleanest or most resourceful way - we'll work on that).
Here's what i did:
Split the makeBox function into 3 function, initWithWidth:andHeight:forView, openBox & updateBoxWithWidth:andHeight.
I used a saveButton delegate method that informs the parent that save button has been pressed.
.h
#protocol BoxDelegate
-(void) daysChanged;
#end
__weak id <BoxDelegate> delegate;
#interface aBox : UIView
#property (nonatomic) BOOL monday;
#property (nonatomic, weak) id <BoxDelegate> delegate;
//property values for the custom view
-(id) initWithWidth: (CGFloat) width andHeight: (CGFloat) height withView: (UIView*) theView;
-(void) updateViewWithWidth: (CGFloat) width andHeight: (CGFloat) height;
-(void) openBox;
#end
.m
#synthesize delegate;
-(id) initWithWidth:(CGFloat)inWidth andHeight:(CGFloat)inHeight withView: (UIView*) theView{
self = [super init];
self.mainView = theView;
//super view bounds
self.viewWidth = inWidth;
self.viewHeight = inHeight;
//red box x, y & size
CGFloat startx = (self.viewWidth / 100) * 10;
CGFloat starty = (self.viewHeight /100) * 20;
self.width = (self.viewWidth / 100) * 80;
self.height = (self.viewHeight /100) * 70;
self.view1Frame = CGRectMake(startx, starty, self.width, self.height);
//label & switch frame
self.labelMR = CGRectMake(10, ((self.height / 100) * 12), ((self.width / 100) * 80) - 7.5, 25);
self.switchMR = CGRectMake(((self.width / 100) * 80) + 7.5, ((self.height / 100) * 11), ((self.width / 100) * 10), 15);
//repeated for the other 6
self.backButtonR = CGRectMake(5, 5, 50, 35);
self.saveButtonR = CGRectMake((self.width - 50), 5, 50, 35);
self.switchM = [[UISwitch alloc] initWithFrame:self.switchMR];
self.switchM.tag = 10;
if(self.monday){
self.switchM.on = true;
} else{
self.switchM.on = false;
}
[self.switchM addTarget:self action:#selector(switched:) forControlEvents:UIControlEventTouchUpInside];
self.labelM = [[UILabel alloc] initWithFrame:self.labelMR];
self.labelM.layer.masksToBounds = YES;
self.labelM.layer.cornerRadius = 5;
self.labelM.layer.borderColor = [UIColor blackColor].CGColor;
self.labelM.layer.borderWidth = .5;
self.labelM.text = #" Monday";
self.labelM.backgroundColor = [UIColor whiteColor];
//repeated for the other 6
self.saveButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.saveButton addTarget:delegate
action:#selector(daysChanged)
forControlEvents:UIControlEventTouchUpInside];
[self.saveButton setTitle:#"Save" forState:UIControlStateNormal];
self.saveButton.frame = self.saveButtonR;
return self;
}
-(void) openBox{
CGRect startFrame = CGRectMake(10, 10, 10, 10);
self.view1 = [[UIView alloc] initWithFrame:startFrame];
[self.mainView addSubview:self.view1];
[UIView animateWithDuration:0.5 animations:^(void){
self.view1.layer.cornerRadius = 10;
self.view1.layer.borderColor = [UIColor blackColor].CGColor;
self.view1.layer.borderWidth = .5;
[self.view1 setBackgroundColor:[UIColor lightGrayColor]];
// repeated for the other 6
self.view1.frame = self.view1Frame;
} completion:^(BOOL finished){
[self.view1 addSubview:self.labelM];
[self.view1 addSubview:self.switchM];
[self.view1 addSubview:self.backButton];
[self.view1 addSubview:self.saveButton];
}];
}
-(void) updateViewWithWidth: (CGFloat) width andHeight:(CGFloat) height{
//re-calculate
self.viewWidth = width;
self.viewHeight = height;
CGFloat startx = (self.viewWidth / 100) * 10;
CGFloat starty = (self.viewHeight /100) * 20;
self.width = (self.viewWidth / 100) * 80;
self.height = (self.viewHeight /100) * 70;
self.view1Frame = CGRectMake(startx, starty, self.width, self.height);
//label & switch frame
self.labelMR = CGRectMake(10, ((self.height / 100) * 12), ((self.width / 100) * 80) - 7.5, 25);
self.switchMR = CGRectMake(((self.width / 100) * 80) + 7.5, ((self.height / 100) * 11), ((self.width / 100) * 10), 15);
self.backButtonR = CGRectMake(5, 5, 50, 35);
self.saveButtonR = CGRectMake((self.width - 50), 5, 50, 35);
[UIView animateWithDuration:0.3 animations:^() {
self.view1.frame = self.view1Frame;
} completion:^(BOOL finished) {
self.labelM.frame = self.labelMR;
self.switchM.frame = self.switchMR;
self.backButton.frame = self.backButtonR;
self.saveButton.frame = self.saveButtonR;
}];
}
And then in our parent viewcontroller
#property aBox *theBox;
...
- (void)viewDidLoad {
[super viewDidLoad];
self.theBox = [[aBox alloc] initWithWidth:self.view.frame.size.width andHeight:self.view.frame.size.height withView:self.view];
[self.theBox openBox];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[self.theBox updateViewWithWidth:self.view.frame.size.width andHeight:self.view.frame.size.height];
}
- (void) daysChanged{
NSLog(#"Days Changed!");
}

UIButton extend touch area like apple keyboard

I have a view with three small custom UIButtons (w:25,h:35) and I feel its really hard to get the touch events.
I have added target for touchUpInside event.
[button addTarget:self action:#selector(keyPressed:) forControlEvents:UIControlEventTouchUpInside];
But as I said I myself seem to miss it easily. Then I looked into similar buttons in apple's keyboard. They seem to have a much bigger touch area, specially the keys with alphabet "A" and "L". I am wondering how they have managed to do the same.
Any help and code sample would be greatly appreciated.
make a new class that enherits UIButton and override this function:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
int errorMargin = 8;
if ([self.titleLabel.text isEqualToString:#"A"] || [self.titleLabel.text isEqualToString:#"a"]) {
CGRect largerFrame = CGRectMake(0 - 20, 0 - errorMargin, self.frame.size.width + 22, self.frame.size.height + errorMargin*2);
return (CGRectContainsPoint(largerFrame, point) == 1) ? self : nil;
}
if ([self.titleLabel.text isEqualToString:#"L"] || [self.titleLabel.text isEqualToString:#"l"]) {
CGRect largerFrame = CGRectMake(0 - 2, 0 - errorMargin, self.frame.size.width + 20, self.frame.size.height + errorMargin*2);
return (CGRectContainsPoint(largerFrame, point) == 1) ? self : nil;
}
CGRect largerFrame = CGRectMake(0 - 2, 0 - errorMargin, self.frame.size.width + 2, self.frame.size.height + errorMargin*2);
return (CGRectContainsPoint(largerFrame, point) == 1) ? self : nil;
}
you will get the same visual output and the touchable area that you want
You shouldn´t create a button smaller than 40x40 points. You can put a smaller image in it. You can have an image with is 25,35 (b.e. myImage#2x.png).
An example:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 40, 40)];
[button setImage:[UIImage imageNamed:#"myImage"] forState:UIControlStateNormal];
// Other option
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(myPosition.x-15, myPosition.y-15, 40, 40)];
[button setImage:[UIImage imageNamed:#"myImage"] forState:UIControlStateNormal];
// ......
button.layer.borderColor = [[UIColor clearColor] CGColor];
button.layer.borderWidth = 15.0;
button.layer.masksToBounds = YES;
Have you tried using Edge Insets?
This basically let's you set inset or outset margins for the rectangle around the button.
https://developer.apple.com/library/ios/documentation/uikit/reference/uibutton_class/index.html#//apple_ref/doc/uid/TP40006815-CH3-SW31

Add buttons to UIView

I would like to add buttons to an UIView. The number of buttons varies from view to view.
I want to achieve the following:
I tried to add the Buttons (in a foreach construct) like this:
UIButton *but=[UIButton buttonWithType:UIButtonTypeRoundedRect];
but.frame= CGRectMake(200, 15, 15, 15);
[but setTitle:#"Ok" forState:UIControlStateNormal];
[but addTarget:self action:#selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:but];
But I don't know how to calculate the CGRectMake when it comes to an new line. How can I check the end of the line (UIView)? Or is it kinda stupid to add Buttons like this?
Thanks
Florian
You need to add calculate buttons size after you set the title. Then keep track of the current X and Y of the previous buttons and figure out if the newly created button can fit on the current "line". Otherwise go to the next one.
I suggest you to look at existing components and see how they do it, below are a couple links.
https://github.com/domness/DWTagList
https://github.com/andreamazz/AMTagListView
With the following code , you can place multiple buttons in one row as long as their total width and the gap is smaller then the view's width.
UIView *view = nil;
NSArray *allBtns = nil;
float viewWidth;
float currentY = 0;
float currentX = 0;
float btnGapWidth = 10.0;
float btnGapHeight = 2.0;
for (UIButton *btn in allBtns) {
CGRect rc = btn.frame;
BOOL shouldAddToNewLine = viewWidth - currentX - btnGapWidth < rc.size.width ? YES : NO;
if (shouldAddToNewLine) {
rc.origin = CGPointMake(0, currentY + rc.size.height + btnGapHeight);
currentX = 0;
currentY += rc.size.height + btnGapHeight;
} else {
//another row
rc.origin = CGPointMake(currentX + rc.size.width + btnGapWidth, currentY);
currentX += rc.size.width + btnGapWidth;
}
btn.frame = rc;
[view addSubview:btn];
}

Fill view with buttons at random positions

i want to fill area (UIView) with buttons(UIButton) so that they do not intersect with each others.
My idea:
create initial button at random position in view;
fill view with other buttons from initial button (count < 20) that they do not intersect ~10 pixels from each other.
What have i done so far:
I created method:
-(void)generateButtonsForView:(UIView *)view buttonCount:(int)count
{
//get size of main view
float viewWidth = view.frame.size.width;
float viewHeight = view.frame.size.height;
//set button at random position
UIButton *initialButton = [[UIButton alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth,
arc4random() % (int)viewHeight,
buttonWidth, buttonHeight)];
[initialButton setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
[view addSubview:initialButton];
// set count to 20 - max number of buttons on screen
if (count > 20)
count = 20;
//fill view with buttons from initial button +- 10 pixels
for (int i=0;i<=count;i++)
{
//button
UIButton *otherButtons = [[UIButton alloc] init];
[otherButtons setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
...//have no idea what to do here
}
}
So i'm confused on the place where i want to generate the position of the other buttons, depending on the initial button. I do not know how to generate theirs position that they were at a distance of 5-10 pixels from each other... Any ideas how to realise this? Thanks!
Here's an example with views rather than buttons, but the concept is the same. I use CGRectInset to give the new potential view a buffer of 10 points around it, then look for whether the new view intersects any of the other views. If there's no intersection, add the subview, if there is, try again with a new random location.
-(void)generateButtonsForView {
float viewWidth = self.view.frame.size.width;
float viewHeight = self.view.frame.size.height;
UIView *initialView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
initialView.backgroundColor = [UIColor redColor];
[self.view addSubview:initialView];
int numViews = 0;
while (numViews < 19) {
BOOL goodView = YES;
UIView *candidateView = [[UIView alloc] initWithFrame:CGRectMake(arc4random() % (int)viewWidth, arc4random() % (int)viewHeight, 50, 30)];
candidateView.backgroundColor = [UIColor redColor];
for (UIView *placedView in self.view.subviews) {
if (CGRectIntersectsRect(CGRectInset(candidateView.frame, -10, -10), placedView.frame)) {
goodView = NO;
break;
}
}
if (goodView) {
[self.view addSubview:candidateView];
numViews += 1;
}
}
}

Resources