How to count subviews in Super View - iphone

I have one class in which i have two method _addView() and _removeView().
And these method i am using in another class for adding view and removing view but its not working.If i am using in the same then its working.
For Remove-
- (id)deleteBoxAtIndex:(int)boxIndex{
for (int i = 0; i < [[self subviews] count]; i++ ) {
[[[self subviews] objectAtIndex:boxIndex] removeFromSuperview];
}
return self;
}
Then how i count the subviews in that class or remove from that class.

You correct in trying to use [self.subviews count] to count the number of subviews, but there is an elegant way to remove all subviews from a view in Objective-C. Try this:
[self.subviews makeObjectsPerformSelector:#selector(removeFromSuperView)];

You should pass a pointer to the UIView instance (the one that has got the subviews) to the other object so that you can call:
myView.subviews
I don't know if this could work for you:
- (id)deleteBoxAtIndex:(int)boxIndex fromView:(UIView*)view {
for (int i = 0; i < [[view subviews] count]; i++ ) {
[[[view subviews] objectAtIndex:boxIndex] removeFromSuperview];
}
return self;
}
If you give more detail about the relationship between the two classes you mention (basically, the names and how one interact with the other), I could give you more hints.

Your problem not in retrieving the count but in fact that your relay on a count that changes dynamically inside of your for loop logic execution. Do cleanup of subviews in the following way:
while([[self subviews] count] > 0) {
UIView *view = [[self subviews] objectAtIndex:0];
[view removeFromSuperview];
}

You can use the superview property:
[[self superview] subviews]
to do the similar loop you are doing right row. However I'd strongly encourage you to use
[[self superview] viewWithTag:boxIndex]
instead of objectAtIndex: method

Just do a loop of all subViews u want to remove:
for (UIView *subs in [self.view subviews]){
[subs removeFromSuperview];
}

Related

Total number of subview in a view in iphone

I want to count total no of subview in a view and also release that subviews in my iphone application how i can do it? provide me suggestion.Thanks.
//count subviews
NSUInteger count = [[view subViews] count];
for (UIView *v in [view subViews])
{
[v removeFromSuperView];
//if 'v' was not autoreleased add next line as well
[v release];
}
NSArray * subviews = [view subviews];
int count = [subviews count];
for(UIView * v in subviews)
{
[v removeFromSuperview];
}
I want to count total no of subview in a view
NSUInteger count = view.subviews.count;
and also release that subviews in my iphone application
Don't. The view retains its subviews for you. Just add and remove views as needed, and let the view system handle reference counting correctly. If you need to retain a reference, hold that reference elsewhere (in your own variables).

Find all controls of a type in a UIView

I am looking for a way to automatically localize texts on buttons/textfields etc and for this method I need to find all (for example) UIButton's on a UIView.
I tried the following 2 methods, but they both do no work like I want them to work:
for (UIView* subView in self.view.subviews)
{
NSLog(#"object class : %#", [subView class]);
if ([subView isMemberOfClass:[UIButton class]])
NSLog(#"Button found!");
}
The problem with this piece of code is that a RoundedRectButton does not match the UIButton class, while it really is just a UIButton.
I also tried the following:
for (UIButton* button in self.view.subviews)
{
// Do my stuff
}
But the stupid thing is, is that cocoa-touch actually just lists all subviews in that for-loop (also the UITextFields etc).
Is there a way to actually just get all UIButtons from a view? Or do I really need to find controls by looking at their selectors.
Why write one-off code like this when you can dial up the awesomeness by adding a category method to UIView using blocks? Take a look at the code at the very bottom. Using this recursive method with blocks you can do things like disable all UITextFields in a view controller's view:
[self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
textfield.enabled = NO;
}];
Or fade out all UITextFields in a view controller's view:
[UIView animateWithDuration:0.25 animations:^{
[self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
textField.alpha = 0.0;
}];
}
completion:^(BOOL finished){
// nothing to do for now
}];
Blocks are pretty amazing in that you can even pass other methods inside a block. For example, the code below passes each UITextField found to my inserAdornmentImage:forTextView method, which adds a custom background image to each text view:
[self.view forEachUITextFieldDoBlock:^(UITextField *textField) {
[self insertAdornmentImage:textFieldBGImage forTextField:textField];
}];
Blocks make the method incredibly flexible so you aren't having to write a specialized method each time you want to do something new with the controls you find. Here's the magic sauce:
#implementation UIView (Helper)
- (void) forEachUITextFieldDoBlock:(void (^)(UITextField *textField))block
{
for (UIView *subview in self.subviews)
{
if ([subview isKindOfClass:[UITextField class]])
{
block((UITextField *)subview);
} else {
[subview forEachUITextFieldDoBlock:block];
}
}
}
#end
the first method is correct, except you need to change isMemberOfClass function to isKindOfClass:
isKindOfClass: Returns a Boolean value
that indicates whether the receiver is
an instance of given class or an
instance of any class that inherits
from that class.

Hide/Show iPhone Camera Iris/Shutter animation

I am not able to Hide the iphone Camera shutter opening animation for my app.
I am using UIImagePickerController to access iphone camera and using my own overlay controllers.
Is there a way to remove the initial shutter(also known as Iris) animation as the camera starts.
Thank You
[EDIT]
For those who wants to know the way to change the camera iris animation.
The below function is called before the camera iris animation starts.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Here is were I make the camera preview fit the entire screen.
// This might violate the "don't change the view hierarchy"-rule.
// So I am not sure if it is valid for App Store commitment.
// However, the NSLogs are used to
// figure out which subview is the actual Camera Preview which turns out
// to be the PLPreviewView. (uncomment to se the printouts).
// Change it's size to fit the entire screen (and scale it accordingly
// to avoid distorted image
NSLog(#"WillShowViewController called...");
NSLog(#"VC:view:subviews\n %#\n\n", [[viewController view] subviews]);
NSLog(#"VC:view:PLCameraView:subviews\n %#\n\n", [[[[viewController view] subviews] objectAtIndex: 0] subviews]);
NSLog(#"VC:view:PLCameraView:PLPreviewView:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 0] subviews]);
NSLog(#"VC:view:PLCameraView:PLCropOverLay:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 1] subviews]);
NSLog(#"VC:view:PLCameraView:UIImageView:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 2] subviews]);
}
In the above function you can go through each layer by using the normal NSMuatableArray syntax like objectAtIndex
hope this might help you.
Regards,
Ankur
Using this answer as a starting point, I've finally solved this problem:
NOTE: This is obviously not 3.3.1-compliant.
Listen for the UINavigationControllerDidShowViewControllerNotification on your UIImagePickerController, and the PLCameraViewIrisAnimationDidEndNotification globally.
Traverse the view hierarchy (starting at the main UIWindow) looking for the PLCameraView. Save the index of the view against the main UIWindow, as you'll need it later.
Remove the PLCameraView from its superView. If desired, insert your own view at global index 0.
When the iris animation is finished, remove your view and re-add the PLCameraView at its original index.
Came across a similar: I wanted to have the shutter appear when I take the picture triggered by a button in a self.cameraOverlayView of a UIImagePickerController. Arrived to this page, did some extra research and came to this solution.
Synopsis:
#interface MyController : UIImagePickerController
...
- (id) init {
...
self.cameraOverlayView = _my_overlay_;
self.showsCameraControls = NO;
...
}
...
- (void) onMyShutterButton {
[self takePicture];
// You want the shutter animation to happen now.
// .. but it does not.
}
Solution:
// Some constants for the iris view and selector
NSString* kIrisViewClassName = #"PLCameraIrisAnimationView";
SEL kIrisSelector = NSSelectorFromString(#"animateIrisOpen");
#implementation MyController {
...
UIView* iris_;
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Find the iris view in the siblings of your overlay view
for (UIView* view in self.cameraOverlayView.superview.subviews) {
if ([kIrisViewClassName isEqualToString:[[view class] description]]) {
// It will be hidden by 'self.showsCameraControls = NO'.
view.hidden = false;
// Extra precautions - as this is undocumented.
if ([view respondsToSelector:kIrisSelector]) {
iris_ = view;
}
break;
}
}
}
- (void) animateIrisOpen {
if (iris_) {
[iris_ performSelector:kIrisSelector];
}
}
...
- (void) onMyShutterButton {
[self takePicture];
[self animateIrisOpen]; // Voila - the shutter happens
}
I've messed around with this a bit, but sending various combinations of the view lifecycle methods to the image picker. (viewWillAppear, viewDidAppear, etc.) But I don't remember which ones ended up working.
Sorry for replying getting back so late. I found out the solution for that well I played around with the view hierarchy of the cameraView and added my own layer at the top of everything. The animation took place there and once the shutter was open the top most layer was removed. If someone need any further help with the code please let me know, I will provide with the exact steps and syntax.
-Ankur
joshwa's answer completely hides the entire camera view for the duration of the iris animation. For my purposes, I needed the camera view visible, just without the iris animation. I was able to accomplish this with a little tweaking of his method. As others have noted, this may or may not be allowed on the app store since we're messing with the view hierarchy as well as listening for undocumented notifications.
3 ivars are needed:
UIImagePickerController *imagePickerController;
UIView *plCameraIrisAnimationView; // view that animates the opening/closing of the iris
UIImageView *cameraIrisImageView; // static image of the closed iris
Hide the closed iris image and remove the animation view. I tried simply hiding the animation view as well, but the animation was still visible:
- (void)receivedNavigationControllerDidShowViewControllerNotification:(NSNotification *)notification {
UIView *view = imagePickerController.view;
[plCameraIrisAnimationView release];
plCameraIrisAnimationView = nil;
cameraIrisImageView = nil;
while (view.subviews.count && (view = [view.subviews objectAtIndex:0])) {
if ([[[view class] description] isEqualToString:#"PLCameraView"]) {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIImageView class]]) {
cameraIrisImageView = (UIImageView *)subview;
}
else if ([[[subview class] description] isEqualToString:#"PLCropOverlay"]) {
for (UIView *subsubview in subview.subviews) {
if ([[[subsubview class] description] isEqualToString:#"PLCameraIrisAnimationView"]) {
plCameraIrisAnimationView = [subsubview retain];
}
}
}
}
}
}
cameraIrisImageView.hidden = YES;
[plCameraIrisAnimationView removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UINavigationControllerDidShowViewControllerNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedPLCameraViewIrisAnimationDidEndNotification:) name:#"PLCameraViewIrisAnimationDidEndNotification" object:nil];
}
When the animation is over, unhide the iris image and re-add the animation view:
- (void)receivedPLCameraViewIrisAnimationDidEndNotification:(NSNotification *)notification {
cameraIrisImageView.hidden = NO;
UIView *view = imagePickerController.view;
while (view.subviews.count && (view = [view.subviews objectAtIndex:0])) {
if ([[[view class] description] isEqualToString:#"PLCameraView"]) {
for (UIView *subview in view.subviews) {
if ([[[subview class] description] isEqualToString:#"PLCropOverlay"]) {
[subview insertSubview:plCameraIrisAnimationView atIndex:1];
[plCameraIrisAnimationView release];
plCameraIrisAnimationView = nil;
break;
}
}
}
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"PLCameraViewIrisAnimationDidEndNotification" object:nil];
}
The below function is called before the camera iris animation starts.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Here is were I make the camera preview fit the entire screen.
// This might violate the "don't change the view hierarchy"-rule.
// So I am not sure if it is valid for App Store commitment.
// However, the NSLogs are used to
// figure out which subview is the actual Camera Preview which turns out
// to be the PLPreviewView. (uncomment to se the printouts).
// Change it's size to fit the entire screen (and scale it accordingly
// to avoid distorted image
NSLog(#"WillShowViewController called...");
NSLog(#"VC:view:subviews\n %#\n\n", [[viewController view] subviews]);
NSLog(#"VC:view:PLCameraView:subviews\n %#\n\n", [[[[viewController view] subviews] objectAtIndex: 0] subviews]);
NSLog(#"VC:view:PLCameraView:PLPreviewView:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 0] subviews]);
NSLog(#"VC:view:PLCameraView:PLCropOverLay:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 1] subviews]);
NSLog(#"VC:view:PLCameraView:UIImageView:subviews\n %#\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 2] subviews]);
}
In the above function you can go through each layer by using the normal NSMuatableArray syntax like objectAtIndex
hope this might help you.
Regards,
Ankur
To expound on Catalin's answer, (which was great btw), I found if you change the method "animateIrisOpen" slightly, the presentation is a fraction better... but noticeable.
- (void) animateIrisOpen {
if (iris_) {
iris_.hidden = NO;
[iris_ performSelector:kIrisSelector];
}
}

How to align messages in UIAlertView?

i want to know how to set the alignment of delegate message of alert view. anyone has solution, plz reply with some code.
This is just a slightly simplified version of the previous answer but I like to keep things simple. :)
for (UIView *view in alert.subviews) {
if([[view class] isSubclassOfClass:[UILabel class]]) {
((UILabel*)view).textAlignment = NSTextAlignmentLeft;
}
}
You need to get alertView's subViews. Iterate through the array of subview's, it will be having one item of type UILable. Get that UILabel from subview array and for that you can set textAlignment property.
NSArray *subViewArray = alertView.subviews;
for(int x=0;x<[subViewArray count];x++){
if([[[subViewArray objectAtIndex:x] class] isSubclassOfClass:[UILabel class]])
{
UILabel *label = [subViewArray objectAtIndex:x];
label.textAlignment = UITextAlignmentCenter;
}
}
the code below is not work on iOS7, only before iOS7.
for (UIView *view in alert.subviews) {
if([[view class] isSubclassOfClass:[UILabel class]]) {
((UILabel*)view).textAlignment = NSTextAlignmentLeft;
}
}
the question Align message in UIAlertView to left in iOS 7 solve this problem.

Ignore touches to background (super) view

How should I be handling, or rather NOT handling (ignoring), touches to my background view? It happens to be the view of my View Controller which has subviews (objects) that I DO want to respond to touch events. Setting userInteractionEnabled = NO for the view seems to turn off ALL interaction for the subviews as well.
I'm currently testing for
if ([[touch view] superview] == self.view)
in touchesBegan/Moved/Ended. But I'm trying to eliminate some conditional testing so looking for a better way...
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; recursively calls -pointInside:withEvent:. point is in frame coordinates
If you override that in your background view, you can specify which subview gets hit - you can be lazy and just ask each of your subviews if they would return yes to it, and never return yourself (return nil if they all return nil). Something like:
UIView *hitView = nil;
NSArray *subviews = [self subviews];
int subviewIndex, subviewCount = [subviews count];
for (int subviewIndex = 0; !hitView && subviewIndex < subviewCount; subviewIndex++) {
hitView = [[subviews objectAtIndex:subviewIndex] hitTest:point withEvent:event];
}
return hitView;
Thanks for the answer Dan, great question too.
However the accepted answer has an error: hitTesting subviews should be done with the point converted to the subview.
Also, subviewIndex is already defined before 'for'.
Since subviews are ordered according to their z index, iterating should go from the last to the first one (see Event handling for iOS - how hitTest:withEvent: and pointInside:withEvent: are related? and How to get UIView hierarchy index ??? (i.e. the depth in between the other subviews)).
Here's the updated code:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = nil;
NSArray *subviews = [self subviews];
int subviewIndex, subviewCount = [subviews count];
for (subviewIndex = subviewCount-1; !hitView && subviewIndex >= 0; subviewIndex--) {
UIView *subview = [subviews objectAtIndex:subviewIndex];
hitView = [subview hitTest:[self convertPoint:point toView:subview] withEvent:event];
}
return hitView;
}