How do I tell if a UIView is visible and on screen? - iphone

If I have a UIView (or UIView subclass) that is visible, how can I tell if it's currently being shown on the screen (as opposed to, for example, being in a section of a scroll view that is currently off-screen)?
To maybe give you a better idea of what I mean, UITableView has a couple of methods for determining the set of currently visible cells. I'm looking for some code that can make a similar determination for any given UIView.

Not tried any of this yet. But CGRectIntersectsRect(), -[UIView convertRect:to(from)View] and -[UIScrollView contentOffset] seem to be your basic building blocks here.

Here's what I used to check which UIViews were visible in a UIScrollView:
for(UIView* view in scrollView.subviews) {
if([view isKindOfClass:[SomeView class]]) {
// the parent of view of scrollView (which basically matches the application frame)
CGRect f = self.view.frame;
// adjust our frame to match the scroll view's content offset
f.origin.y = _scrollView.contentOffset.y;
CGRect r = [self.view convertRect:view.frame toView:self.view];
if(CGRectIntersectsRect(f, r)) {
// view is visible
}
}
}

if you are primarily worried about releasing an object that is not in the view hierarchy, you could test to see if it has a superview, as in:
if (myView.superview){
//do something with myView because you can assume it is on the screen
}
else {
//myView is not in the view hierarchy
}

I recently had to check whether my view was onscreen. This worked for me:
CGRect viewFrame = self.view.frame;
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
// We may have received messages while this tableview is offscreen
if (CGRectIntersectsRect(viewFrame, appFrame)) {
// Do work here
}

Related

iOS textviews not expanding in scrollview in landscape

I had a view with some textviews in it that were working as I wanted. The expanded to a the same right-margin whether in landscape or portrait.
I recently have tried changing the normal view to a scrollview. I've had no luck getting these text views to expand as they once did, though. When in landscape mode everything stays huddled over on the left side with the same width as a portrait phone.
Here is some code.
- (void)viewDidLoad {
CGSize screen = [self handleScreenOrientation];
[(UIScrollView *)self.view setContentSize:CGSizeMake(screen.width, screen.height)];
}
- (CGSize)handleScreenOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect screenRect = [[UIScreen mainScreen] bounds];
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown ) {
return CGSizeMake(screenRect.size.width, screenRect.size.height);
}
else {
return CGSizeMake(screenRect.size.height, screenRect.size.width);
}
}
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
CGSize screen = [self handleScreenOrientation:toInterfaceOrientation];
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.frame = CGRectMake(0, 0, screen.width, screen.height);
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, scrollView.frame.size.height);
[scrollView setNeedsDisplay];
}
The method handleScreenOrientation with the passed orientation is the same as the one w/ no parameters, just it uses the passed orientation instead of the current orientation of the status bar.
I've checked and my scrollview is set to autoresize subviews.
Any ideas would be much appreciated. Thanks.
Update: I have added
self.view.autoresizesSubviews = true;
for (UIView *view in self.view.subviews) {
view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;
}
to viewdidload and willrotatetointerfaceorientation. No change.
I think the problem is that you are overriding the autoresizingMasks by forcing a change to the scrollView's contentSize. Although you have correctly noted that the screenSize is always in portrait orientation, so you reverse its dimensions for landscape, you are determining which orientation to use before the device actually rotates. So you're forcing the scrollView to maintain portrait dimensions in landscape.
Keep your autoresizeMask routine and discard your willRotateToInterfaceOrientation routine. Does it work now?
If not, try putting the willRotate code into *did*RotateToInterfaceOrientation.
Another idea:
I notice that in viewDidLoad you cast the container view (self.view) to a UIScrollView. I wonder if it might be better to leave the container view as a generic UIView, add a UIScrollView as a subview, and then add the textviews to the scrollview.
I might be out in left field here, but I've never used a scrollview directly at the container-view level, and I wonder if that might be the problem.

Custom UIView layoutSubviews with orientation and animations?

I'm in a dilemma which method to use for setting frames of custom UIViews with many subviews in it and still have animations and automatically adjust to rotations. What I usually do when I create a new viewcontroller is alloc my custom view in loadView or viewDidLoad, e.g:
-(void)viewDidLoad
{
[super viewDidLoad];
detailView = [[DetailView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
detailView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.view = detailView;
}
Normally this width & height is not correct for an iPhone5-screen (the actual view-frame is not set until viewWillAppear) but because of the autoresizingmask it all works out.
Then in the initWithFrame of the custom UIView DetailView, I alloc all subviews with CGRectZero, e.g:
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
label = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:label];
}
}
Then I override layoutsubviews to set all frames of all subviews. This works perfectly for any screen size and any orientation, e.g:
-(void)layoutSubviews
{
[super layoutSubviews];
label.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
However, I just found out that layoutSubviews is not so great when you use animations, because when you use animations in an animationblock, layoutsubviews is called in the middle of the animation and it completely breaks the animation, e.g:
-(void)animateLabel
{
[UIView animateWithDuration:0.4f animations:^
{
label.transform = CGAffineTransformMakeTranslation(100, 100);
}];
}
I believe there are ugly workarounds this by using flags for each animation and in layoutsubviews use those flags to set the correct start or endframe of the animated block but I don't think I should have to create a flag for each animation I want to do.
So my problem is now: how am I supposed to have a custom UIView WITH animations that also automatically adjusts itself to rotations?
The only solution I can come up with right now (that I don't like):
Don't use layoutSubviews but use the setFrame/setBounds method of the custom UIView to set the frames of all subviews. Then check in the viewController every time a rotation occurs and then use the setFrame/setBounds method of the custom UIView to change all frames of all subviews. I don't like this solution because the rotation methods are different in iOS5 and iOS6 and I don't want to have to do this in every UIViewController with it's own custom UIView.
Any suggestions?
I have recently started overriding viewDidLayoutSubviews (many times instead of viewWillAppear) in my UIViewControllers.
Yes viewDidLayoutSubviews is called on rotations. (from comment)
The method fires after all the internal layouts have already been completed so all finalized frames should be setup, but still give you the time you need to make adjustments before the the view is visible and shouldn't have any issues with animations because you are not already inside an animation block.
viewcontroller.m
- (void)viewDidLayoutSubViews {
// At this point I know if an animation is appropriate or not.
if (self.shouldRunAnimation)
[self.fuView runPrettyAnimations];
}
fuView.m
- (void)runPrettyAnimations {
// My animation blocks for whatever layout I'd like.
}
- (void)layoutSubviews {
// My animations are not here, but non animated layout changes are.
// - Here we have no idea if our view is visible to the user or may appear/disappear
// partway through an animation.
// - This also might get called far more than we intend since it gets called on
// any frame updates.
}

Resize UICollectionView while scrolling

My collection view begins life short, at the bottom of it's view controller's view. When the user scrolls up a little bit, I'd like the collection to fill the view. I'd like it to shrink again when the user pulls down into the bounce area.
Here's the code that I'd like to make work (and I think should work):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.collectionView) {
CGFloat offsetY = self.collectionView.contentOffset.y;
if (offsetY > 20.0) {
[self setCollectionViewExpanded:YES];
} else if (offsetY < -10.0) {
[self setCollectionViewExpanded:NO];
}
}
}
- (void)setCollectionViewExpanded:(BOOL)expanded {
// avoid starting a do-nothing animation
BOOL currentlyExpanded = self.collectionView.frame.origin.y == 0.0;
if (expanded == currentlyExpanded) return;
// note: the collection view is the uppermost subview of the vc's view
// expanding to view bounds makes it cover every other view
CGRect newFrame = (expanded)? self.view.bounds : CGRectMake(0.0, 240.0, 320.0, self.view.bounds.size.height-240.0);
[UIView animateWithDuration:0.3 animations:^{
self.collectionView.frame = newFrame;
}];
}
But after the expand happens, the cells remain in the same position relative to the parent view (still near the bottom) and the collection view stops responding to panning touches and sends no further didScroll notification.
I can get the content repositioned properly by doing a reloadData in an animation completion block, but the scrolling touches stop working either way.
A while back, I tried something like this with a table view (or another kind of scroll view) and ran into a similar hangup. Much obliged if anyone has this solved...
Can you describe your view hierarchy a bit better? Is the UICollectionView a subview of the UIScrollView?
Is self.view a UIScrollView?
In that case, the bounds origin is the scrollview's contentOffset (not 0,0). That could give your UICollectionView an unexpected frame.
If the UICollectionView is a subview of the scrollview, the frame origin is the origin of the view in the scrollview-space. So don't change the frame's origin (just its size) and set the contentOffset of the scrollView to make it visible.

Uitext Content SIze in UI Scroll View , the text in bottom is cut off

I have UIScroll that have uitextview inside it.
My apps look like this:
rootviewcontroller -> PhoneContentController (UIScrollView and PageControl) -> DetailController (UITextView, UIWebView, UILabel).
My Flow is like this:
Set UIScrollView default size -> call DetailController (calculate UITextView and other page inside it in viewDidLoad) -> PhoneContentController get the total height size from DetailController-> Change the height of UIScrollView
This is the code in PhoneContentController (loadSCrollViewWithPage):
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, [detailController.getTotalHeight]);
The code in Detail Controller (viewDidLoad):
summaryBlogParsed = [summaryBlogParsed stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[self.itemSummaryText setText:summaryBlogParsed];
CGRect frameTextView = self.itemSummaryText.frame;
frameTextView.size.height = self.itemSummaryText.contentSize.height;
self.itemSummaryText.frame = frameTextView;
This code in DetailController getTotalHeight:
CGRect cgRect = [[UIScreen mainScreen] bounds];
CGFloat scrollFrame = (cgRect.size.height/2) + itemSummaryText.frame.size.height;
return scrollFrame;
I can scroll the scrollview but the textview not load all of the string. like only half of it, but i can scroll down but with blank pages :(.
Anyone can help me?
Thank you
set content size of scroll view using this function.
-(void)setContentSizeOfScrollView:(UIScrollView*)scroll
{
CGRect rect = CGRectZero;
for(UIView * vv in [scroll subviews])
{
rect = CGRectUnion(rect, vv.frame);
}
[scroll setContentSize:CGSizeMake(rect.size.width, rect.size.height)];
}
after adding all controls to your scroll view call this function and pass yourScrollView as argument. Hope this will help you.........
According to Apple... When debugging issues with text views, watch for this common pitfall:
Placing a text view inside of a scroll view. Text views handle their own scrolling. You should not embed text view objects in scroll views. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.

Need UIView to autoresize

I have made a custom UIView which is shown when the user hits a button in the navigationbar. I make my view's in code. In my loadview I set the autoresizing masks and the view loads correct on screen. However the UIView which is shown when the user taps the button does not resize even when I have set the autoresizing masks.
UIView *blackView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 416.0)];
blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Do I need to use self.view.frame.size.width and self.view.frame.size.height instead? And if I do why? Does not resizing masks work outside of loadView?
Thank you for your time:)
the autoresizingMask affects how a view will behave when its superviews frame changes. if all you are doing is showing theblackViewwhen you tap a button, thenblackView` will have whatever frame you initially set for it.
If this isn't enough info, please post some more code around how you are configuring and displaying blackView and it's superview and explain more about what situations you are expecting blackView to resize in. Rotation is one of them, if that's what you're concerned with.
First things first, I hope you've done this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Let's say the view that needs resizing is: view2
The view that has view2 as a subview is: view1
While creating view1 you would declare it as:
view1 = [[UIView alloc] init];
[view1 setNeedsLayout];
Now in view1's .m file you need to overload the layoutSubviews method as shown:
- (void)layoutSubviews
{
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
In case view1 is a view controller's view, you need to do that same thing as above in the willRotate method as shown
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
This is a tried and tested method that I use to handle orientation changes.