0

I've been looking at the same problem for so long I'm probably missing a simple solution here.

I created a small library to provide a custom UIView that sticks to the keyboard like the one for iMessage does (aka doesn't hide with keyboard): https://github.com/oseparovic/MessageComposerView

Basically the problem I'm experiencing is that when the user init's custom view I want a view with the following default rect initialized:

CGFloat defaultHeight = 44.0;
CGRect frame = CGRectMake(0,
                          [self currentScreenSize].height-defaultHeight,
                          [self currentScreenSize].width,
                          defaultHeight)

This requires that the currentScreenSize is calculated within the UIView. I've tried multiple implementations all of which have their downsides. There doesn't seems to be a good solution due to this breaking principles of MVC.

There are lots of duplicate questions on SO but most assume you have access to the rest of the code base (e.g. the app delegate) which this custom view does not so I'm looking for a self contained solution.

Here are the two leading implementations I'm using:


NextResponder

This solution seems to be fairly successful in a wide variety of scenarios. All it does is get the next responder's frame which very conveniently doesn't include the nav or status bar and can be used to position the UIView at the bottom of the screen.

The main problem is that self.nextResponder within the UIView is nil at the point of initialization, meaning it can't be used (at least not that I know) to set up the initial frame. Once the view has been initialized and added as a subview though this seems to work like a charm for various repositioning uses.

- (CGSize)currentScreenSize {
    // return the screen size with respect to the orientation
    return ((UIView*)self.nextResponder).frame.size;
}

ApplicationFrame

This was the solution I was using for a long time but it's far more bulky and has several problems. First of all, by using the applicationFrame you have to deal with the nav bar height as it will otherwise offset the position of your view. This means you have to determine if it is visible, get its height and subtract it from your currentSize.

Getting the nav bar unfortunately means you need to access the UINavigationController which is not nearly as simple as accessing the UIViewController. The best solution I've had so far is the below included currentNavigationBarHeight. I recently found an issue though where this will fail to get the nav bar height if a UIAlertView is present as [UIApplication sharedApplication].keyWindow.rootViewController will evaluate to _UIAlertShimPresentingViewController

- (CGSize)currentScreenSize {
    // there are a few problems with this implementation. Namely nav bar height
    // especially was unreliable. For example when UIAlertView height was present
    // we couldn't properly determine the nav bar height. The above method appears to be
    // working more consistently. If it doesn't work for you try this method below instead.
    return [self currentScreenSizeInInterfaceOrientation:[self currentInterfaceOrientation]];
}

- (CGSize)currentScreenSizeInInterfaceOrientation:(UIInterfaceOrientation)orientation {
    // http://stackoverflow.com/a/7905540/740474

    // get the size of the application frame (screensize - status bar height)
    CGSize size = [UIScreen mainScreen].applicationFrame.size;

    // if the orientation at this point is landscape but it hasn't fully rotated yet use landscape size instead.
    // handling differs between iOS 7 && 8 so need to check if size is properly configured or not. On
    // iOS 7 height will still be greater than width in landscape without this call but on iOS 8
    // it won't
    if (UIInterfaceOrientationIsLandscape(orientation) && size.height > size.width) {
        size = CGSizeMake(size.height, size.width);
    }

    // subtract the height of the navigation bar from the screen height
    size.height -= [self currentNavigationBarHeight];

    return size;
}

- (UIInterfaceOrientation)currentInterfaceOrientation {
    // Returns the orientation of the Interface NOT the Device. The two do not happen in exact unison so
    // this point is important.
    return [UIApplication sharedApplication].statusBarOrientation;
}

- (CGFloat)currentNavigationBarHeight {
    // TODO this will fail to get the correct height when a UIAlertView is present
    id nav = [UIApplication sharedApplication].keyWindow.rootViewController;
    if ([nav isKindOfClass:[UINavigationController class]]) {
        UINavigationController *navc = (UINavigationController *) nav;
        if(navc.navigationBarHidden) {
            return 0;
        } else {
            return navc.navigationBar.frame.size.height;
        }
    }
    return 0;
}

Does anyone have suggestion about how I can best calculate the UIViewController size from within this UIView. I'm totally open to other suggestions on how to stick the UIView to the bottom of the screen upon initialization that I may have overlooked. Thank you!

alexgophermix
  • 4,189
  • 5
  • 32
  • 59

1 Answers1

0
+ (id) getCurrentUIViewController : (id)res {
   if([res isKindOfClass:[UIViewController class]]) {
      return res;
   }
   else if ([res isKindOfClass:[UIView class]]) {
      return [Function getCurrentUIViewController:[res nextResponder]];
   }
   else {
      return nil;
   }
}