In my Flutter Application, I am using a OrientationBuilder widget to change my layout based on the current orientation.
But the animation occuring when the widgets change position doesn't look smooth, and the scaling on UI elements temporarely becomes weird.
Is there a way to implement an orientation change, such that the elements just stay in their positions, and rotate themselves?
Current behavior:
Preferred Behavior:
I have left out the camera screen in the flutter App, as this is only about the onscreen controlls. The camera view itself isn't the issue!
I've been having the same issue, the camera preview rotation works weird. What I ended up doing is using this package native orientation which provides the following API: Stream<NativeDeviceOrientation> onOrientationChanged(useSensor: false) which will trigger orientation changes even if you lock the screen using: SystemChrome.setPreferredOrientations()
Basically in the init of your component: _orientation = NativeDeviceOrientationCommunicator() .onOrientationChanged(useSensor: true) .listen((event) { switch (event) { case NativeDeviceOrientation.portraitUp: case NativeDeviceOrientation.unknown: _actualOrientation = DeviceOrientation.portraitUp; break; case NativeDeviceOrientation.portraitDown: _actualOrientation = DeviceOrientation.portraitDown; break; case NativeDeviceOrientation.landscapeLeft: _actualOrientation = DeviceOrientation.landscapeLeft; break; case NativeDeviceOrientation.landscapeRight: _actualOrientation = DeviceOrientation.landscapeRight; break; } }); and then use the actual orientation to animate the icons or know the actual orientation of the captured image.
Remember to close the subscription on dispose _orientation.cancel();
Related
Every time I build, it changes back to Portrait.
I can't find where to disable this automatic change.
Is it possible to do?
Go into Player Settings, set the orientation to anything other than Auto Rotation and it won’t be changed.
You can also change the orientation programmatically. Just make sure it’s set back to a fixed orientation prior to loading your AR Scene. For example, if you want auto-rotate to be enabled in your Non-AR Scenes, you could set Screen.orientation = ScreenOrientation.AutoRotation, and then prior to loading your AR Scene, just set it back to either Portrait or Landscape.
If you want to get fancy, you could also auto detect the orientation of the device at the time the user presses whatever button that is used to launch the AR scene by first checking Input.deviceOrientation and then setting the Screen.orientation to that.
Here is an example - Run() function launches your scene (after first checking device orientation and setting the screen orientation based on that):
void Run(String scene) {
// Lock orientation to current device orientation prior to loading AR scene
switch (Input.deviceOrientation) {
case DeviceOrientation.Portrait:
Screen.orientation = ScreenOrientation.Portrait;
break;
case DeviceOrientation.PortraitUpsideDown:
Screen.orientation = ScreenOrientation.PortraitUpsideDown;
break;;
case DeviceOrientation.LandscapeLeft:
Screen.orientation = ScreenOrientation.LandscapeLeft;
break;;
case DeviceOrientation.LandscapeRight:
Screen.orientation = ScreenOrientation.LandscapeRight;
break;;
default:
// if Unknown, just set to Portrait
Screen.orientation = ScreenOrientation.Portrait;
break;
}
SceneManager.LoadScene(scene);
}
I recently stumbled upon a problem where I have a view that can be rotated by the user. During the rotation it should, however, be 10% larger (scaled up).
I want that scaling to be animated but the rotation to be be visible immediately since I set it without animation in the gesture recognizer's callback.
Question: Is it possible to update an CGAffineTransform's rotation without intercepting the scale being animated, or is there no way around creating a wrapping view that gets scaled instead?
Edit:
I think a wrapper view for scaling would be the least error prone way.
If you desperately want to avoid that, you could try to create the scaling animation manually with an NSTimer. Maybe if you query the current transform value first and then modify it instead of replacing it by an independently created one (for the rotation and the scale), it could work.
I think an implicit UIView animation calculates all subsequent values in the beginning, so that would mess up your rotation.
Another way to go would be to lock the rotation while the scaling occurs. The downside is, that scaling and rotation wouldn't be simultaneously. Anyway you could create an iVar or property let's say rotationLocked and do sth. like this:
- (void)handlePan:(UIPanGestureRecognizer *)gr
{
if (gr.state == UIGestureRecognizerStateBegan)
{
self.rotationLocked = YES;
[UIView animateWithDuration:.2 animations:^{
[self scaleView];
}completion:^{ self.rotationLocked = NO; };
}
if (gr.state == UIGestureRecognizerStateChanged)
{
if (!self.rotationLocked){
// do the rotation
}
if (gr.state == UIGestureRecognizerStateEnded)
{
// do something else
}
I am doing a app for iPad in which we have added two images one for Landscape and the other for portrait mode, I have written code for.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// in this method we have called landscape and portrait frames basing on the
// status bar rotation if it is turned to landscape then the landscape image
// is called and the textfields frames are set else portrait image is set and
// then the textfield frames are set on to it.
}
Can somebody help me out by saying is the right way of using 2 different images, or we should use only 1 image for both landscape and portrait.If so, how can we use those only single image? My app is crashing due to this. Kindly do help me.
Thanks in advance.
There is no harm in using two different images it just increases the size of the image bundle.. i have done this for an app on appstore.. an MNC's banks ipad app. So no harm go ahead and use it. I personally it is better this way than to process the image and resize it.
Hello kinthali As per the documentation
Handling View Rotations
By default, the UIViewController class displays views in portrait mode
only. To support additional orientations, you must override the
shouldAutorotateToInterfaceOrientation: method and return YES for any
orientations your subclass supports. If the autoresizing properties of
your views are configured correctly, that may be all you have to do.
However, the UIViewController class provides additional hooks for you
to implement additional behaviors as needed.
To temporarily turn off features that are not needed or might
otherwise cause problems during the orientation change, you can
override the willRotateToInterfaceOrientation:duration: method and
perform the needed actions there. You can then override the
didRotateFromInterfaceOrientation: method and use it to reenable those
features once the orientation change is complete.
As per your question all you need to do is to return Yes for supported orientation in shouldAutorotateToInterfaceOrientation and you can call to a function to set the background image (Two different images for Portrait and landscape orientation)as follows
[self updateBackgroundImageForOrientation:PROBABLE_INTERFACE_ORIENTATION];
In the method implementation you can update the background image.This way you can implement the desired behavior. Since you have not actually posted any functional code it is hard to point out the cause of the crash. You can debug and see the console logs for root cause of the crash.
You are probably best having two images and you can do the following:
EDIT: I've added some code which will make sure that your image fills the entire space available.
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if((self.interfaceOrientation == UIDeviceOrientationLandscapeLeft) || (self.interfaceOrientation == UIDeviceOrientationLandscapeRight)){
[landscapeimage setHidden:NO];
[portraitimage setHidden:YES];
CGRect landscapeframe = landscapeimage.frame;
landscapeframe.origin.x = 0; // new x coordinate
landscapeframe.origin.y = 0; // new y coordinate
landscapeframe.size.width = 1024;
lanscapeframe.size.height = 768;
}
else if((self.interfaceOrientation == UIDeviceOrientationPortrait) || (self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)){
[landscapeimage setHidden:YES];
[portraitimage setHidden:NO];
CGRect portraitframe = portraitimage.frame;
portraitframe.origin.x = 0; // new x coordinate
portraitframe.origin.y = 0; // new y coordinate
portraitframe.size.width = 768;
portraitframe.size.height = 1024;
}
Hope this helps you!
I had started a project using UIImagePickerController to capture still images. I had abandoned using this simple mechanism because of the poor interaction between overlays and just-captured images. The problem was as follows: If you have an overlay active, and snap a photo, the just snapped image rotates around to do an aspect fill in the portrait orientation, when the user is presented the option to use or retake the image. When this occurs, there is no way to dismiss the overlay, or rotate the overlay to correspond to the new still image preview orientation. Not good.
I decided to attempt to re-implement the still image capture behavior of UIImagePickerController using the various mechanisms from AV Foundation. This has been surprisingly easy to set up, but I still have some lingering issues:
1) I want to re-orienting mechanism to be similar to how the UI works in the standard UIImagePickerController. In this controller, as you know, the button bar always stays fixed to the bottom of the portrait orientation, regardless of how the phone is rotated. Within this button bar, the glyphs in the buttons themselves rotate, but everything else remains fixed. The overlay controls, for flash set, camera toggle, and options, does reorient. How can I get this tab bar to remain pinned while rotating these other elements? Is there a way to exclude certain elements from rotation?
2) Upon doing the automatic reorientation, I want the AVVideoCaptureLayer to always remain live and occupy the entire screen. When the device performs the reorientation rotation, this layer gets sized strangely (shrunk down) and the video feed itself is off by 90 degrees. So apparently this layer is being rotated but not to the full screen dimension, and the video feed itself isn't behaving as expected. I want the video to remain fixed and occupying the full screen through the full rotation, with up always being up, and no resizing of the preview during rotation. But I also want to be able to do as in item 1, with certain elements reorienting.
Any sample code illustrating how to achieve this is greatly appreciated.
just found out that using the orientation property on AVCaptureVideoPreviewLayer is deprecated. here is an updated solution (although the previous solution shared by #Jaret Ward was much less work :) :
use the recommend approach of changing it with setVideoOrientation in AVCaptureConnection. I would add a call to this in viewDidAppear and in one of your rotation callbacks.
//setup a connection to the preview layer
AVCaptureConnection *connection;
connection = [self.videoPreviewLayer connection];
[connection setVideoOrientation:[self avOrientationForDeviceOrientation:[[UIDevice currentDevice] orientation]]];
//translate the orientation
- (AVCaptureVideoOrientation)avOrientationForDeviceOrientation:(UIDeviceOrientation)deviceOrientation {
AVCaptureVideoOrientation result = deviceOrientation;
if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
result = AVCaptureVideoOrientationLandscapeRight;
else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
result = AVCaptureVideoOrientationLandscapeLeft;
else if( deviceOrientation == UIDeviceOrientationPortrait)
result = AVCaptureVideoOrientationPortrait;
else if( deviceOrientation == UIDeviceOrientationPortraitUpsideDown)
result = AVCaptureVideoOrientationPortraitUpsideDown;
return result;
}
By setting the orientation property on the video preview layer's connection.
This is just a cleaner version of #CocoaEv's answer. It uses the newer dot syntax in addition to making the orientation fix a function instead of a method.
self.videoPreviewLayer.connection.videoOrientation = AVOrientationFromDeviceOrientation([UIDevice currentDevice].orientation);
AVCaptureVideoOrientation AVOrientationFromDeviceOrientation(UIDeviceOrientation deviceOrientation) {
if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
return AVCaptureVideoOrientationLandscapeRight;
else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
return AVCaptureVideoOrientationLandscapeLeft;
else if( deviceOrientation == UIDeviceOrientationPortrait)
return AVCaptureVideoOrientationPortrait;
else if( deviceOrientation == UIDeviceOrientationPortraitUpsideDown)
return AVCaptureVideoOrientationPortraitUpsideDown;
else
return deviceOrientation;
}
I encountered a similar issue in my own efforts and finally let go of the idea of trying to affect the video stream. Instead, I focused on the AVCaptureVideoPreviewLayer itself and came up with this solution:
- (void)orientationChangedMethod {
self.previewLayer.orientation = [[UIDevice currentDevice] orientation];
self.previewLayer.frame = self.view.bounds;
}
Where orientationChangedMethod is a fill in to the (very helpful) code at the.ichibod.com and self.previewLayer is a reference to my AVCaptureVideoPreviewLayer instance.
This answer is deprecated since iOS 6!
I'm just trying to write a view based Application, which only uses the landscape Orientation.
Using this Code:
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
in the applicationDidFinishedLaunching method the Application starts in landscape orientation.
The problem is, that if I create a landscape view in InterfaceBuilder, the view does not get shown up the way I created it... I need to create the view in portrait mode and imagine how it looks when it is in portrait mode (hope you understand what I mean?) - I insert already rotated images.
So, I got everything work now, but the InfoButton doesn't rotate (as the other buttons, but the others are already rotated images)...
Whats my fault? How to I tell the Application to use my View, which I designed in Landscape orientation?
Thanks so much.
regards
You may have to rotate your view (button) dependent on orientation:
UIInterfaceOrientation orientation = [UIDevice currentDevice].orientation;
CGAffineTransform transform;
switch (orientation) {
case UIDeviceOrientationPortrait:
transform = CGAffineTransformIdentity;
break;
case UIDeviceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(180*M_PI/180);
break;
case UIDeviceOrientationLandscapeLeft:
transform = CGAffineTransformMakeRotation(90*M_PI/180);
break;
case UIDeviceOrientationLandscapeRight:
transform = CGAffineTransformMakeRotation(-90.0*M_PI/180);
break;
default:
transform = CGAffineTransformIdentity;
break;
}
[[myView layer] setAffineTransform:transform];
You don't need to create the view in portrait mode and imagine how it looks when it is in portrait mode. Just you need to create your view in your needed mode, then set your project setting to your needed mode. In Xcode on the left view you can see Project Navigator click on the blue icon of project name. Now on the middle view (editor view) you can see the summary tab, on this tab set your needed mode on the iPhone Development Orientations.