How to detect rotation for a programatically gener

2019-04-27 00:00发布

问题:

I have a UIView that's programmatically being created:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    if (self) {
        self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.5];

        UIImageView* closeButton = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"modal_close.png"]];

        closeButton.frame = CGRectMake(self.frame.size.width*.89, self.frame.size.height*.09, closeButton.frame.size.width, closeButton.frame.size.height);

        UIButton* button = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width*.89, self.frame.size.height*.09,20, 20)];

        [button addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside];

        UIView* mainView = [[UIView alloc] initWithFrame:CGRectMake(self.frame.size.width*.1,self.frame.size.height*.1,self.frame.size.width*.8,self.frame.size.height*.8)];

        mainView.backgroundColor = [UIColor whiteColor];
        _displayMainView = mainView;        

        [self addSubview:_displayMainView];
        [self addSubview:closeButton];
        [self addSubview:button];

        mainView = nil;
        closeButton = nil;
    }
    return self;
}

How can I detect rotation? This is to act as a modal on top of another existing view. This is an iPad only app, if that matters.

回答1:

I think you have two options:

  1. You put the view in a custom UIViewController and override the shouldAutorotateToInterfaceOrientation: method to return YES for all the orientations you want to support. The view controller will automatically rotate and resize the content (your view). You just have to make sure to have the correct autoresizeMask (or constrains, if you're using autolayout).
  2. You directly observe changes to the devices orientation with the accelerometer. Then you can adjust your view when needed.

It's really the first approach you should choose if you can.



回答2:

You could get the device to start generating rotation notifications:

  [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

Add your own selector as an observer for a device rotation notification:

      [[NSNotificationCenter defaultCenter] addObserver: self selector:   @selector(deviceOrientationDidChange:) name: UIDeviceOrientationDidChangeNotification object: nil];

And then write your notification handler

- (void)deviceOrientationDidChange:(NSNotification *)notification {
     //Obtain current device orientation
     UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

      //Do my thing
 }

Remember to remove the observer when the dealloc method of you custom UIView is called (or when you're done) and stop generating the device change notifications as well.

-(void) dealloc{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

Does this help at all with your question?



回答3:

I'm rewriting @clearwater82's answer for Swift 3 since his approach is the one I ended up using:

I added the following code in my view's init method (for a controller it would be in it's viewDidLoad method):

// Handle rotation
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(
    self,
    selector: #selector(self.orientationChanged(notification:)),
    name: NSNotification.Name.UIDeviceOrientationDidChange,
    object: nil
)


I then added the orientationChanged method to my view. You can rename this method as you wish, just remember to also rename it in the code snippet above:

// Called when device orientation changes
@objc func orientationChanged(notification: Notification) {
    // handle rotation here
}


Lastly I added the deinit method to remove unnecessary notifications when the view is removed:

deinit {
    NotificationCenter.default.removeObserver(self)
    UIDevice.current.endGeneratingDeviceOrientationNotifications()
}

What this last snippet does is to remove the view itself from the notification observers and then stop the device from generating other rotation notifications. This approach was what I needed but you may not want to stop the notifications from being generated when the view is removed.

I hope this is useful, cheers