How to search for UIView inside many levels of UIViews? - ios

Is there a better way to search for UIViews children inside 3-4+ levels of UIViews without using tag? Is there a much better way to go directly into the child UIView without having to traverse the tree every time? The tag is really not helping.
Thanks!

if you want to access a view directly you may use
[randomChildView.window.subviews objectAtIndex:0]
in order to avoid looping all subviews you can use of enumerateObjectsUsingBlock
- (NSArray*) allSubviews {
__block NSArray* allSubviews = [NSArray arrayWithObject:self];
[self.subviews enumerateObjectsUsingBlock:^( UIView* view, NSUInteger idx, BOOL*stop) {
allSubviews = [allSubviews arrayByAddingObjectsFromArray:[view allSubviews]];
}];
return allSubviews;
}
Hope it helps!

Related

Is there any simple way to change UIPageViewControllerNavigationOrientationHorizontal scroll animation

I want the UIPageViewControllerNavigationOrientationHorizontal to scroll from the left instead of the right (the opposite way of the default) in a page view controller.
Can anyone help?
I've been in this situation before and couldn't figure it out using the UIPageViewController. I'm not sure what your intent on your project is but this might help.
Use a UIScrollView and place the amount of UIView's you need inside. At the point you could just create a CGPoint and set your UIScrollView's content offset to your CGPoint. This way you would start at the last page(view) and can scroll to the left instead of the right. Something like this below. You can perform this in your viewDidLoad method.
CGPoint nameYourPoint = CGPointMake(640.0,568,0);
self.yourScrollViewName.contentOffset = nameYourPoint;
I was able to figure it out fairly simply:
When adding objects into the array of images, I added them in backwards like so:
for (int i = 1; i <= 81; i++) {
NSString *imageName = [NSString stringWithFormat:#"image%i.gif", i];
[self.mArray insertObject:[UIImage imageNamed:imageName] atIndex:0];
After that, in the root view controllers viewDidLoad method, I simply started the images at the end of the array
ModelController *objModelController = [ModelController new];
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:[objModelController.mArray count]-1 storyboard:self.storyboard];
note:for some reason the length wasnt working, so I had to just add 2 empty objects to the beginning of the array and use
[objModelController.mArray count]-1
Hope this helps everyone out!

How to find number of subviews?

I have a Xcode project and in it i have dragged two views and both of them inherit from a class LabelsView. However when I try and run the code to find out number of subviews, I get 4. Can anyone explain why is this happening.
The code is
NSLog(#"no. of subviews:%#",[NSString stringWithFormat:#"%d",[self.superview.subviews count]]);
You're probably getting a weird subview count because you're accessing self.superview.subviews. You likely just want self.subviews.
If, like you said, you only care about subviews of type LabelsView, you can filter those out like this:
int labelViewCount = 0;
for(LabelsView *subview in self.subviews) {
if([subview isKindOfClass:[LabelsView class]]) {
labelViewCount++;
}
}
NSLOG(#"label count: %d", labelViewCount);
If you want the amount of all subviews in swift, you can just go with
self.subviews.count

how can i differentiate and refer to various UITextViews that are created programmatically?

A UITextView is created each time i click on ADD button. Y-axis value is altered(say, y+=100) every time i click ADD and so a set of UITextViews are created one below the other. I cant figure out how to differentiate and access a particular UITextView. Thanks for any help!
EDIT:
-(IBAction)access:(id)sender
{
int tg=[sender superview].tag;
UIView *view=(UIView *)[textView viewWithTag:tg-1];
}
tg-1 because im trying to access the previous UITextView and when i do this it returns NULL.
Store them on a NSMutableArray:
NSMutableArray * views = [[NSMutableArray alloc] init]
Your IBAction
-(IBAction)access:(id)sender{
int tg=[sender superview].tag;
UIView *view=(UIView *)[textView viewWithTag:tg-1];
[views addObject: views];
}
Then you can get all the references with a integer index with:
UIView * storedView = [views objectAtIndex: 1];
Use a view tag to differentiate the views and access them.
You don't say how you're creating the new views, but something like this should work:
UIView* new_view = [UITextView initWithFrame(...)];
new_view.tag = generate_tag()
Where the generate_tag() function generates whatever naming scheme makes sense for your application.

Searching class in a lot of subviews. Is there a better way?

I'm adding image in subviews of a scrollView, but only to a certain custom class AsyncImageView. This takes some time because there are a lot of subviews and the application has lost it's smooth scrolling.
NSArray *subviewsArray = [[NSArray alloc] initWithArray:[scrollView subviews]];
for (int j = 0; j < [subviewsArray count]; j++) {
for (AsyncImageView *checkView in [[subviewsArray objectAtIndex:j] subviews]) {
if ([checkView isKindOfClass:[AsyncImageView class]]) {
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:checkView];
NSString* urlTextEscaped = [[imageInfo objectAtIndex:0] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:urlTextEscaped];
if (URL)
{
checkView.imageURL = URL;
}
else
{
NSURL *defaultURL = [NSURL URLWithString:#"http://www.domain.com/user_avatar_default.jpg"];
checkView.imageURL = defaultURL;
}
}
}
}
Is there a better way to access the view that I need and add image to it? Some other logic that I haven't think of.
This is the hierarchy of the scrollView
scrollView
|
|
MaseterView
|
|
AsyncImageView - TitleView - OtherImageView
You don't need to create a new array with the subviews of the scroll view before you iterate it. You have 2 general options:
A. Still looping, but not done by you. Only meaningful if you have a single image view or have multiple image views that you want to update individually.
Set the tag of the image view, then use [scrollView viewWithTag:...];
B. If you have multiple image views and you want to update all of them at the same time.
Hold a mutable array property and add the image views to it when you create them.
Other things to think about:
Do you need to update all the image views - are they all on display?
Can you observe the scrolling of the scroll view and only start loads for images when they're on display (and cancel incomplete loads when scrolled off display)?
You can use
UIView *v = [self.containerView viewWithTag:uniqueTagvalue];
set unique tag value for the views you need and fetch it

How to remove all subviews?

When my app gets back to its root view controller, in the viewDidAppear: method I need to remove all subviews.
How can I do this?
Edit: With thanks to cocoafan: This situation is muddled up by the fact that NSView and UIView handle things differently. For NSView (desktop Mac development only), you can simply use the following:
[someNSView setSubviews:[NSArray array]];
For UIView (iOS development only), you can safely use makeObjectsPerformSelector: because the subviews property will return a copy of the array of subviews:
[[someUIView subviews]
makeObjectsPerformSelector:#selector(removeFromSuperview)];
Thank you to Tommy for pointing out that makeObjectsPerformSelector: appears to modify the subviews array while it is being enumerated (which it does for NSView, but not for UIView).
Please see this SO question for more details.
Note: Using either of these two methods will remove every view that your main view contains and release them, if they are not retained elsewhere. From Apple's documentation on removeFromSuperview:
If the receiver’s superview is not nil, this method releases the receiver. If you plan to reuse the view, be sure to retain it before calling this method and be sure to release it as appropriate when you are done with it or after adding it to another view hierarchy.
Get all the subviews from your root controller and send each a removeFromSuperview:
NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
In Swift you can use a functional approach like this:
view.subviews.forEach { $0.removeFromSuperview() }
As a comparison, the imperative approach would look like this:
for subview in view.subviews {
subview.removeFromSuperview()
}
These code snippets only work in iOS / tvOS though, things are a little different on macOS.
If you want to remove all the subviews on your UIView (here yourView), then write this code at your button click:
[[yourView subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
This does only apply to OSX since in iOS a copy of the array is kept
When removing all the subviews, it is a good idea to start deleting at the end of the array and keep deleting until you reach the beginning. This can be accomplished with this two lines of code:
for (int i=mySuperView.subviews.count-1; i>=0; i--)
[[mySuperView.subviews objectAtIndex:i] removeFromSuperview];
SWIFT 1.2
for var i=mySuperView.subviews.count-1; i>=0; i-- {
mySuperView.subviews[i].removeFromSuperview();
}
or (less efficient, but more readable)
for subview in mySuperView.subviews.reverse() {
subview.removeFromSuperview()
}
NOTE
You should NOT remove the subviews in normal order, since it may cause a crash if a UIView instance is deleted before the removeFromSuperview message has been sent to all objects of the array. (Obviously, deleting the last element would not cause a crash)
Therefore, the code
[[someUIView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
should NOT be used.
Quote from Apple documentation about makeObjectsPerformSelector:
Sends to each object in the array the message identified by a given
selector, starting with the first object and continuing through the
array to the last object.
(which would be the wrong direction for this purpose)
Try this way swift 2.0
view.subviews.forEach { $0.removeFromSuperview() }
view.subviews.forEach { $0.removeFromSuperview() }
Use the Following code to remove all subviews.
for (UIView *view in [self.view subviews])
{
[view removeFromSuperview];
}
Using Swift UIView extension:
extension UIView {
func removeAllSubviews() {
for subview in subviews {
subview.removeFromSuperview()
}
}
}
In objective-C, go ahead and create a category method off of the UIView class.
- (void)removeAllSubviews
{
for (UIView *subview in self.subviews)
[subview removeFromSuperview];
}
In order to remove all subviews Syntax :
- (void)makeObjectsPerformSelector:(SEL)aSelector;
Usage :
[self.View.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
This method is present in NSArray.h file and uses NSArray(NSExtendedArray) interface
If you're using Swift, it's as simple as:
subviews.map { $0.removeFromSuperview }
It's similar in philosophy to the makeObjectsPerformSelector approach, however with a little more type safety.
For ios6 using autolayout I had to add a little bit of code to remove the constraints too.
NSMutableArray * constraints_to_remove = [ #[] mutableCopy] ;
for( NSLayoutConstraint * constraint in tagview.constraints) {
if( [tagview.subviews containsObject:constraint.firstItem] ||
[tagview.subviews containsObject:constraint.secondItem] ) {
[constraints_to_remove addObject:constraint];
}
}
[tagview removeConstraints:constraints_to_remove];
[ [tagview subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
I'm sure theres a neater way to do this, but it worked for me. In my case I could not use a direct [tagview removeConstraints:tagview.constraints] as there were constraints set in XCode that were getting cleared.
In monotouch / xamarin.ios this worked for me:
SomeParentUiView.Subviews.All(x => x.RemoveFromSuperview);
In order to remove all subviews from superviews:
NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
id object = [oSubView objectAtIndex:iCount];
[object removeFromSuperview];
iCount--;
}

Resources