pick up custom views in UIView hierarchy - ios

There is a situation that should select all custom view(not system view type such as UILabel or UIButton etc.) like XXButton or XXView. How can I iterate a UIView's subviews to figure out all the custom views? In other words, how to distinguish between unknown class custom views and Apple system views?

Try Following,
for viw in self.view.subviews
{
if viw.classForCoder == yourCustomViewClass
{
// do your required operation
}
}
In above case first we have used for in loop to iterate on all sub views of particular view.
Then we have checked the class for the view from the subview's array

When you created a XXButton or XXView, they basically inherited from the UIButton and UIView respectively. Thus you have to explicitly checks for your custom class only.
//Loop through all the views in your superview.
for(UIView *anyView in self.view.subviews) {
if([anyView isKindOfClass:[XXButton class]]) {
// It's a XXButton. Need to cast it.
XXButton *btn = (XXButton *)anyView;
} else if([anyView isKindOfClass:[XXView class]]) {
// It's a XXView. Need to cast it.
XXView *view = (XXView *)anyView;
}
// You can multiple else if conditions for your custom UI classes.
}

Related

iOS: Sorting an array of UIButtons based on their position in relation to their siblings?

I have a NSMutableArray, called self.tappableButtons, of UIButtons which all share the same parent view. At times they overlap, and I would love to sort my array based on their order in relation to each other (starting with the topmost button to the bottommost one in the hierarchy). What's the easiest way to achieve this?
I'm basically using fast enumeration to check if the current touch in my custom pan gesture recognizer falls within a button's bounds. The problem is that if two buttons overlap it returns the one that came first in my array when enumerating, rather than the topmost button within an overlap.
//Search through all our possible buttons
for (UIButton *button in self.tappableButtons) {
//Check if our tap falls within a button's view
if (CGRectContainsPoint(button.bounds, tapLocation)) {
return button;
}
}
The easiest way (without knowing more of your code) is probably to use the subviews order to determine the topmost button on your touch.
UIView* buttonParentView = ....
NSEnumerator* topToBottom = [buttonParentView.subviews reverseObjectEnumerator];
for (id theView in topToBottom) {
if (![self.tappableButtons containsObject: theView]) {
continue;
}
UIButton* button = (UIButton*)theView;
//Check if our tap falls within a button's view
if (CGRectContainsPoint(button.bounds, tapLocation)) {
return button;
}
}
If this function is executed a lot and your self.tappableButtons remains relatively stable, or your parentview has a -lot- of subviews that are not in self.tappableButtons it's probably better to simply use your function but sort the tappableButtons first based on where they appear in their parents subviews.

Automatic view id's like in android

I need to write some gui for IOS, yet i am so used to the automatic view id's that android has, that i am at a loss when it comes to identifying views without putting explicit tags in the interface builder
is there some way to automatically identify iOS views from interface builder or otherwise ?
maybe a library that does that ?
Thats why you have IBOutlets. Create an IBOutlet for your views, like so IBOutlet UIView *view1; etc etc then in link the IBOutlet to your view in Interface Builder. Now you can progammatically use the view1 variable which will modify your view associated to that IBOutlet in interface builder\
UPDATE
Progammatically create all your views like so:
for(int i = 0; i < 20; i++){
//set your x and y coordinates with some awesome maths, maybe you want to create a grid, so update the x coordinate accordingly
UIView *view = [UIView alloc] initWithFrame:CGRectMake(whateverframe)];
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
[imageView addGestureRecognizer:singleTapGesture];
[singleTapGesture release]; //remove line if you have an ARC project
view.tag = i; //this is just for you since you like to handle your views with id tags
}
Then your one method that will handle the taps for all your code
- (void) viewTapped:(UIGestureRecognizer*)recognizer
{
UIView *viewTapped = recognizer.view; // This is the view associated with gesture recognizer.
int id = viewTapped.tag; //heres the tag id do whatever you like
//do something with either that view variable which will return the view that was tapped
//maybe you wanted to change the color of it or something or change the contents of it
//or you can do something with the id tag of that view.
//or maybe you just want to handle it with if else statements like so
//if(viewTapped.tag == 1) //do this
//elseif(viewTapped.tag == 2) // etc etc
}
Set Tag for each and every view using property setTag and retrieve the corresponding view using – viewWithTag:. [abaseView subViews] returns an array of subviews. Filter the subviews using Class and Tag.

Maps and legal mention

With iOS < 6.0 we were able to re-position the "Google" link over map view (by browsing the subviews of map view). Now with iO6, there's a "legal" link and this is a MKAttributeLabel. A private class that we can't manipulate ...
My problem is that I must add a footer subview to my map and it'll hide the legal link ...
How can I solve this problem without any App Store rejection ?
Can I create another legal button my self and add it where I want in my map view ?
I have no idea what I'm able to do...
There's a few answers recommending you move the legal label in the viewDidAppear of your view controller, however this doesn't work if you then resize your map view (like I am).
The best way is to subclass the MKMapView and override the layoutSubviews method. In my example I just needed to nudge the legal label above a semi-transparent toolbar.
-(void)layoutSubviews
{
[super layoutSubviews];
UILabel *legalLabel;
for(UIView *view in self.subviews)
{
if([view isKindOfClass:[UILabel class]])
{
legalLabel = (UILabel *)view;
break;
}
}
legalLabel.center = CGPointMake(legalLabel.center.x, self.bounds.size.height - 55.0f);
}
Does the footer view have to be inside the map boundary, why not put the map and the footer into the same super view?

iOS: setting Exclusive Touch to all buttons in a view

My app has many buttons in a Window and I want to set Exclusive Touch all of them together. Do you have any suggestion about this? Thanks
There is a way to set exclusive touch to all buttons in your app, may be helpful.
#import </usr/include/objc/objc-class.h>
static IMP gOringinalWillMoveToSuperview = nil;
static id newMoveToSuperviewPlusSettingExclusiveTouch(id self,SEL selector,...)
{
va_list arg_list;
va_start( arg_list,selector);
gOringinalWillMoveToSuperview(self,selector,arg_list);
[self setExclusiveTouch:YES];
return nil;
}
-(void)addSettingExclusiveTouchToAllUIViewMethodWillMoveToSuperview
{
gOringinalWillMoveToSuperview = class_getMethodImplementation([UIButton class], #selector(willMoveToSuperview:));
class_replaceMethod([UIButton class], #selector(willMoveToSuperview:), &newMoveToSuperviewPlusSettingExclusiveTouch, "v#:");
}
if you don't understand this, you can refer to this and this.
Are you just looking for an easy way to set them all at once?
If you have all the buttons in an array (e.g. they're all connected to the same IBOutletCollection) you can use key value coding to set the exclusiveTouch property of the array:
[buttonArray setValue:[NSNumber numberWithBool:YES] forKey:#"exclusiveTouch"];
NSArray will then invoke the same method on every item in the array.
-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * v in [myView subviews]) {
if([v isKindOfClass:[UIButton class]])
[((UIButton *)v) setExclusiveTouch:YES];
else if ([v isKindOfClass:[UIView class]]){
[self setExclusiveTouchForButtons:v];
}
}
}
then call this function at viewDidAppear
If these buttons are all in the same view, you can loop through the view's subviews, test for whether the particular subview is a button (or test for a tag if you have one set) and set exclusiveTouch on each.
I just found an answer for this:
#pragma mark Set Buttons Exclusive Touch Yes
-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * button in [myView subviews]) {
if([button isKindOfClass:[UIButton class]])
[((UIButton *)button) setExclusiveTouch:YES];
}
}
Source
If you want to set exclusiveTouch for ALL UIButtons in your whole application method swizzling will be the perfect solution for you.
This answer explains the way very well : https://stackoverflow.com/a/24534814/976246 , and it works perfectly for me.
Also go through this article to know how this (http://nshipster.com/method-swizzling/) tecknique can be used for various purposes.
If you are adding buttons pragmatically, then send a message to the button [button setExclusiveTouch:YES]; for each buttons before adding to its super view. Else if you are using xib, you have to send the same message to the button in viewDidLoad or in loadView.
Here is some code in swift that will set exclusive touch to all buttons in your viewcontroller's view
for button in self.view.subviews {
if(button.isKindOfClass(UIButton)){
(button as! UIButton).exclusiveTouch = true
}
}
Even though the question was asked many years ago, here is a simple way to set isExclusiveTouch for all subviews that are buttons. This example shows code for subviews of self.view. Change self.view to your view of interest.
Seems like cleaner code for anyone with questions on this in the present.
for subview in self.view.subviews
{
if subview is UIButton {
subview.isExclusiveTouch = true
}
}

ios custom navigation (vertical) advice

I have an idea for an ios5 navigation I'm doing on an app and I thought it wise to get some constructive criticism from SOF about my idea.
Idea:
UIView containing 6 or so buttons stacked vertically
UIButtons have a selected state.
Buttons static/global keeps track of last touched button and always resets the last touched button when a new UIButton is touched.
Question:
Can you read and access the children of the UIView?
eg. (pseudocode)
for (i in [myView children]) {
[[myView getChildAt:i] doSomethingToThisButton];
}
Thanks all!
Yes. Here's the non-pseudocode (well, mostly):
for (UIView *subview in [myView subviews]) {
[subview doSomethingToThisButton];
}
Or, if you prefer
for (int i = 0; i < [myView.subviews count]; i++) {
[[myView.subviews objectAtIndex:i] doSomethingToThisButton];
}
Don't make your last touched button a static variable because then you can only have one such control in your whole app. Make a UIView subclass to act as the container for your buttons, and have the last selected view be a property of that class.
You may also want to make your containing view a subclass of UIControl instead of UIView, then you can make it send events and bind to it using drag and drop in interface builder, just like a regular control (e.g. a button).

Resources