从ViewController初始化一直谈到强制横屏

2024-01-13 17:08

本文主要是介绍从ViewController初始化一直谈到强制横屏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文字罗嗦,篇幅较长,只需营养可直接看红字部分。转自:http://blog.sina.com.cn/s/blog_76264a170101e5lb.html

一个viewController的初始化大概涉及到如下几个方法的调用: 

initWithNibName:bundle:

viewDidLoad

viewWillAppear:animated:

viewDidAppear:animated:

viewWillLayoutSubviews

viewDidLayoutSubviews

通常情况几个方法是依次被调用的,我们会在init方法中初始化一些成员变量,做一些与view无关的事情。而后在viewDidLoad中进行view布局相关的属性调整,比如改变一下背景颜色,增加一些subview之类的。不知道大家有没有想过,这样不在init中写view相关代码是为了什么?难道仅仅是为了代码结构清晰?如果我非要在init做一些与view相关的初始化工作,能不能实现?有什么问题?

@implementation  testViewController

- ( void) printFrame:( CGRect) frame  name:( NSString  *) name
{
     NSLog( @"%@ :(%f, %f, %f, %f)" ,  name ,  frame . origin . x ,  frame . origin . y ,  frame . size . width , frame . size . height);
}

- ( id) initWithNibName:( NSString  *) nibNameOrNil  bundle:( NSBundle  *) nibBundleOrNil
{
     self  =  [ super  initWithNibName: nibNameOrNil  bundle: nibBundleOrNil ];
     if ( self{
           // Custom initialization
           self . view . backgroundColor  =  [ UIColor  yellowColor ];         
           [ self  printFrame: self . view . frame  name: @"initFrame" ];
     }
     return  self;
}

- ( void) viewDidLoad
{
     [ super  viewDidLoad ];
     // Do any additional setup after loading the view.
     [ self  printFrame: self . view . frame  name: @"didloadFrame" ];
}

- ( void) viewWillLayoutSubviews
{
     [ super  viewWillLayoutSubviews ];
     [ self  printFrame: self . view . frame  name: @"willLayoutFrame" ];    
}

-( void) viewDidLayoutSubviews
{
     [ super  viewDidLayoutSubviews ];
     [ self  printFrame: self . view . frame  name: @"didLayoutFrame" ]; 
}

- ( void) viewWillAppear:( BOOL) animated
{
     [ super  viewWillAppear: animated ];
     [ self  printFrame: self . view . frame  name: @"willAppearFrame" ];
}

-( void) viewDidAppear:( BOOL) animated
{
     [ super  viewDidAppear: animated ];
     [ self  printFrame: self . view . frame  name: @"didappearFrame" ];
}

这段代码在init方法中设置了一下view的backgroundColor。运行结果很正常,view的背景色被成功地设定为黄色,但是看控制台的log输出,出现了一个不符合预期的现象:

didloadFrame  :( 0.000000 ,  20.000000 ,  768.000000 ,  1004.000000)
initFrame  :( 0.000000 ,  20.000000 ,  768.000000 ,  1004.000000)
willAppearFrame  :( 0.000000 ,  0.000000 ,  768.000000 ,  960.000000)
didappearFrame  :( 0.000000 ,  0.000000 ,  768.000000 ,  960.000000)
willLayoutFrame  :( 0.000000 ,  0.000000 ,  768.000000 ,  960.000000)
didLayoutFrame  :( 0.000000 ,  0.000000 ,  768.000000 ,  960.000000)

viewDidLoad竟然先于init给出了输出,经过跟踪发现,原来当程序第一次调用self.view的时候,viewDidLoad方法就会被执行,而不一定非要等到init之后willAppear之前。这给我们敲响了警钟,这样的代码就隐藏了问题:

- ( id) initWithNibName:( NSString  *) nibNameOrNil  bundle:( NSBundle  *) nibBundleOrNil
{
     self  =  [ super  initWithNibName: nibNameOrNil  bundle: nibBundleOrNil ];
     if ( self{
           // Custom initialization
           self . view . backgroundColor  =  [ UIColor  yellowColor ];
           aInstanceVariable_ =  0 // Custom initialization of an instance variable
    }
     return  self;
}

- ( void) viewDidLoad
{
     [ super  viewDidLoad ];
     // Do any additional setup after loading the view.
     aInstanceVariable_  =  10086;
}

这段代码执行完后的aInstanceVariable_是0而不是10086,可能会为一些bug深深地埋下一颗种子。

搞清楚了代码执行顺序,下面我们来关注一下frame和bounds的问题。frame和bounds的定义和区别在这篇blog里讲的很清楚,总结起来要点就是,frame是相对于父view参照系(是父view而不是父viewController)的,bounds是本地参照系,改frame的时候center和bounds联动,但改bounds的时候center不动。

把上面的程序稍微修改一下,来看一组值得研究一下的结果(此viewController由带导航条的navigationController推送),实际上不用navigationController而直接加载这个vc,结果又不一样,viewDidAppear会在最后viewDidLayoutSubviews之后才调用,其他顺序不变,乱吧……

didLoadFrame  :( 0.000000 20.000000 768.000000 1004.000000direction:( 1 1)
didLoadBounds  :( 0.000000 0.000000 768.000000 1004.000000direction:( 1 1)
initFrame  :( 0.000000 20.000000 768.000000 1004.000000direction:( 1 1)
initBounds  :( 0.000000 0.000000 768.000000 1004.000000direction:( 1 1)
willAppearFrame  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
willAppearBounds  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
didAppearFrame  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
didAppearBounds  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
willLayoutFrame  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
willLayoutBounds  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
didLayoutFrame  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)
didLayoutBounds  :( 0.000000 0.000000 768.000000 960.000000direction:( 1 1)

刚才这个是竖屏的,再来个横屏的:

didLoadFrame  :( 0.000000 ,  0.000000 ,  748.000000 ,  1024.000000direction:( 3 ,  3)
didLoadBounds  :( 0.000000 ,  0.000000 ,  748.000000 ,  1024.000000direction:( 3 ,  3)
initFrame  :( 0.000000 ,  0.000000 ,  748.000000 ,  1024.000000direction:( 3 ,  3)
initBounds  :( 0.000000 ,  0.000000 ,  748.000000 ,  1024.000000direction:( 3 ,  3)
willAppearFrame  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
willAppearBounds  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
didAppearFrame  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
didAppearBounds  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
willLayoutFrame  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
willLayoutBounds  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
didLayoutFrame  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)
didLayoutBounds  :( 0.000000 ,  0.000000 ,  1024.000000 ,  704.000000direction:( 3 ,  3)

总结一下不难发现其特征:1. 在viewWillAppear之前,无论横屏还是竖屏,view的frame和bounds都是按竖屏方式计算的;2. 在viewWillAppear之前,navigationController(而非父view,实际上这个vc的superview是navigationController的view的一个subview)的导航条并没有计算在frame和bounds中,但电池条的宽度是一直计算了的;3. 在转屏时,触发的是viewWillLayoutSubview及viewDidLayoutSubview(data not shown)。

由此结论,我们继续往下想,如果我们要改变self.view的frame值,我们应当在哪个方法中修改呢?很容易想到的是,init和viewDidLoad中是不行的,实践证明,在viewWillAppear中也是不行的,要在viewDidAppear/viewWillLayoutSubviews/viewDidLayoutSubviews方法中修改才能产生效果。

看起来越来越复杂了……对了,以上的结论对iOS5和6是通用的。下面开始研究转屏,转屏对iOS5和6来说,差别就大了。

先看iOS5

iOS5的时候,转屏函数主要是这几个:(补:其实还有一个willAnimationRotationToInterfaceOrientation:duration:,调用时机在viewDidLayoutSubviews之后,didRotation之前)

-( BOOL) shouldAutorotateToInterfaceOrientation:( UIInterfaceOrientation) toInterfaceOrientation
{
     NSLog( @"shouldRotate");
     return  YES;
} //以下简称shouldRotate

-( void) willRotateToInterfaceOrientation:( UIInterfaceOrientation) toInterfaceOrientation duration:( NSTimeInterval) duration
{
     NSLog( @"willRotate");
}

-( void) didRotateFromInterfaceOrientation:( UIInterfaceOrientation) fromInterfaceOrientation
{
     NSLog( @"didRotate");
}

初始化一个正常viewController时转屏函数的调用过程如下:

2012 - 11 - 18  16 : 40 : 58.090  testRotation [ 1874 : c07 ]  init
2012 - 11 - 18  16 : 40 : 58.091  testRotation [ 1874 : c07 ]  shouldRotate
2012 - 11 - 18  16 : 40 : 58.092  testRotation [ 1874 : c07 ]  didLoad
2012 - 11 - 18  16 : 40 : 58.092  testRotation [ 1874 : c07 ]  shouldRotate
2012 - 11 - 18  16 : 40 : 58.093  testRotation [ 1874 : c07 ]  willappear
2012 - 11 - 18  16 : 40 : 58.093  testRotation [ 1874 : c07 ]  shouldRotate
2012 - 11 - 18  16 : 40 : 58.094  testRotation [ 1874 : c07 ]  willlayout
2012 - 11 - 18  16 : 40 : 58.095  testRotation [ 1874 : c07 ]  didlayout
2012 - 11 - 18  16 : 40 : 58.096  testRotation [ 1874 : c07 ]  didappear

我的妈呀,初始化一个vc怎么调用了三次shouldRotate方法……(别着急,三次算什么,这种情况下调用几次都有可能……)

如果初始化vc是在一个navigationController下,看起来还比较正常:

2012 - 11 - 19  20 : 42 : 42.037  testRotation [ 462 : c07 ]  init
2012 - 11 - 19  20 : 42 : 42.039  testRotation [ 462 : c07 ]  didload
2012 - 11 - 19  20 : 42 : 42.040  testRotation [ 462 : c07 ]  willappear
2012 - 11 - 19  20 : 42 : 42.041  testRotation [ 462 : c07 ]  shouldRotate
2012 - 11 - 19  20 : 42 : 42.042  testRotation [ 462 : c07 ]  didappear
2012 - 11 - 19  20 : 42 : 42.042  testRotation [ 462 : c07 ]  willlayout
2012 - 11 - 19  20 : 42 : 42.043  testRotation [ 462 : c07 ]  didlayout

shouldRotate在willAppear之后调用一次。

无论有navigationController与否,再转一下屏后,方法调用过程是一样的:

2012 - 11 - 19  20 : 51 : 00.729  testRotation [ 527 : c07 ]  shouldRotate
2012 - 11 - 19  20 : 51 : 00.730  testRotation [ 527 : c07 ]  willRotate
2012 - 11 - 19  20 : 51 : 00.731  testRotation [ 527 : c07 ]  willlayout
2012 - 11 - 19  20 : 51 : 00.731  testRotation [ 527 : c07 ]  didlayout
2012 - 11 - 19  20 : 51 : 00.732  testRotation [ 527 : c07 ]  shouldRotate
2012 - 11 - 19  20 : 51 : 01.133  testRotation [ 527 : c07 ]  didRotate

注意,shouldRotate方法依然被调用了两次。

为了把shouldRotate方法的调用次数以及这几次调用的返回值有什么用搞明白,我做了个实验,详细过程不赘述,只说结论。结论是一个坏消息和一个好消息:坏消息是,shouldRotate方法可能调用很多次(只出现在非navigationController方式直接将vc作为rootViewController的情况),我最多遇到过连续调用6次的,弄的我一头雾水,具体原因尚不详;好消息是,无论在哪个阶段调用多少次,起决定作用的只有willAppear调用后,willLayoutSubviews调用前shouldRotate的最后一次调用,其余阶段返回yes还是no都不重要。

再看iOS6

iOS6对转屏逻辑做了修改,去掉了原来的shouldRotate方法,代之以新的几个方法,具体可看这篇blog,介绍很详细,不再赘述,做一些补充:

-( BOOL) shouldAutorotate
{
     return  YES;
}
-( NSUInteger) supportedInterfaceOrientations
{
     return  UIInterfaceOrientationMaskAll;
}
-( UIInterfaceOrientation) preferredInterfaceOrientationForPresentation
{
     return  UIInterfaceOrientationLandscapeRight;
}

这3个方法代替了原来的shouldRotate方法,但并不是换汤不换药。

iOS6把转屏的逻辑判断放到了rootViewController里,也就是说,无论当前显示的是那个子vc,都是rootViewController响应转屏事件(或者present出来的modalViewController也可以,总之是最根部的vc才行),而且不向下传递。直接在一个childViewController里写这几个方法,是根本不会被调用到的。这就带来一个问题,根vc的转屏逻辑直接决定了子vc的转屏逻辑,如果老子说这个方向支持,那儿子只好支持,而且所有的儿子还都得支持。解决这个专制问题,可以在根vc里这样写:

-( BOOL) shouldAutorotate
{
     return  [ self . topViewController  shouldAutorotate ];
}
-( NSUInteger) supportedInterfaceOrientations
{
     return  [ self . topViewController  supportedInterfaceOrientations ];
}

让老子每次转屏被问到的时候,都亲自问下他现在正在活跃的子孙。

转屏时调用顺序跟iOS5一样,不过shouldRotate被顺序拆分为shouldAutoRotate和supported。并且如果shouldAutoRotate返回了NO,则转屏过程中断,不再继续执行supported。

最后说到强制横屏。

iOS5和6都有这个问题,如果我们采用presentViewController的方式展示一个vc,那么我们是可以在进入vc的时候控制present的方向的。但是如果我们采用的是pushViewController的方式,问题就出现了,无论我们用何种方式设置这个vc支持的屏幕方向,都只能在转屏的时候进行调整,而无法在第一次进入这个vc的时候调整。也就是说,竖屏push进入一个只支持横屏的vc,显示依然是竖屏,但当转横屏之后,就转不回竖屏了。

这显然不对,解决这个问题,要么用私有API setOrientation: 这个显然是风险太大的。比较好的解决方式就是检测屏幕方向,然后用view.transform去人工转view,setStatusBarOrientation。这里面要注意几个要点:

1. view.transform的makeRotation方式转view是中心点center不动,view旋转。

2. 旋转过后view的frame会改变,所以要人工调整,这里计算frame的新位置和尺寸是重点。由于是人工转屏,改变电池条的方向并不会改变view的坐标系,所以一切要在原坐标系里算。

3. view转屏退出后要记得用identity恢复之前view转过的状态。

4. 最坑爹的一点是,用setStatusBarOrientation:animated:方法来设置电池条方向时,在iOS5下没有问题,但在iOS6下,这个方法会调用rootvc的shouldAutoRotate(相当于一次转屏判断),如果shouldAutoRotate返回YES(无论supported返回什么),电池条方向都不会被设定!非常坑,所以逻辑要想好,比如可以通过一个bool值判断是在改变电池条方向还是系统转屏,如果是前者,返回个NO骗骗它……

5. 在哪个方法里处理转屏,设置电池条方向,以及在哪个方法里调整view的frame,都是很重要的,要视你的view是怎么push进来的(有rootvc还是本身就是),要具体情况具体分析。中心思想是:比如强制要求横屏,则在横屏进入的时候,直接用系统转屏逻辑限制方向即可;而在竖屏进入时,禁用系统转屏逻辑,人工将view旋转至需要的方向,而后再转为横屏时,可采用两种方式,一是恢复原本view方向后重新开启系统转屏逻辑,二是继续根据方向人工转屏。设计这个过程代码时,明确之前研究的frame尺寸应该什么时候重设以及各个view方法的执行顺序,是必须的。

5. iOS5和6要区分处理。总之,强制横屏绝对不是网上随处可见的transform一下然后重设一下bound就ok了的事情。

附上一种强制横屏实现的代码:

// 强制横屏的一种实现
// 使用方法:
// 在vc的init方法中调用initLogic
// 在vc关闭之前调用cleanRotateTrace方法

-( void) initLogic
{
     isPortraitIn_  =  NO;
     isSettingStatusBar_  =  NO;
}
-( BOOL) shouldAutorotate
{
     if ( isSettingStatusBar_)
     {
           return  NO;
     }
     return  YES;
}

-( NSUInteger) supportedInterfaceOrientations
{
     return  UIInterfaceOrientationMaskLandscape;
}

-( BOOL) shouldAutorotateToInterfaceOrientation:( UIInterfaceOrientation) toInterfaceOrientation
{
     return (( toInterfaceOrientation  ==  UIInterfaceOrientationLandscapeLeft)||( toInterfaceOrientation  ==  UIInterfaceOrientationLandscapeRight));
}

- ( void) willRotateToInterfaceOrientation:( UIInterfaceOrientation) toInterfaceOrientation duration:( NSTimeInterval) duration
{
     if ( isPortraitIn_)
     {
           self . view . transform  =  CGAffineTransformIdentity;
           isPortraitIn_  =  NO;
     }
}

- ( void) cleanRotationTrace
{
     if ( isPortraitIn_)
     {
           self . view . transform  =  CGAffineTransformIdentity;
           isPortraitIn_  =  NO;
           UIInterfaceOrientation  orientation  =  [ UIApplication sharedApplication ]. statusBarOrientation;
           if ( orientation  ==  UIInterfaceOrientationLandscapeRight)
           {
                 isSettingStatusBar_  =  YES;
                 [[ UIApplication  sharedApplication ] setStatusBarOrientation: UIInterfaceOrientationPortrait  animated: NO ];
                 isSettingStatusBar_  =  NO;
           }
           else
           {
                 isSettingStatusBar_  =  YES;
                 [[ UIApplication  sharedApplication ] setStatusBarOrientation: UIInterfaceOrientationPortraitUpsideDown  animated: NO ];
                 isSettingStatusBar_  =  NO;
           }
           [ self . view  setFrame: CGRectMake( 0 ,  0 ,  self . view . frame . size . height  +  20 , self . view . frame . size . width  -  20 )];
     }
}

-( void) viewDidAppear:( BOOL) animated
{
     [ super  viewDidAppear: animated ];
     UIInterfaceOrientation  orientation  =  [ UIApplication sharedApplication ]. statusBarOrientation;
     if ( UIInterfaceOrientationIsPortrait( orientation))
     {
           isPortraitIn_  =  YES;
           self . view . transform  =  CGAffineTransformMakeRotation( M_PI_2);
           if ( orientation  ==  UIInterfaceOrientationPortrait)
           {
                 isSettingStatusBar_  =  YES;
                 [[ UIApplication  sharedApplication ] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight  animated: NO ];
                 isSettingStatusBar_  =  NO;
           }
           else
           {
                 isSettingStatusBar_  =  YES;
                 [[ UIApplication  sharedApplication ] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft  animated: NO ];
                 isSettingStatusBar_  =  NO;
           }
           [ self . view  setFrame: CGRectMake( 0 ,  - 20 ,  self . view . frame . size . height  -  20 , self . view . frame . size . width  +  20 )];
     }
}

虽然强制横屏的中心思想都差不多,但具体实现方式可以有很多种,我自己写过两种,效果都差不多,代码简洁程度不同。这些实现目前我都没有解决的问题是转屏的动画,用系统逻辑的部分没有问题,但如果是竖屏进入强制横屏的,在第一次转到真正横屏的时候,电池条的转动与view的转动是不同步的,动画很难看,之后再转就又是系统转屏没有问题了。

这个动画问题我至今能够想到的唯一解决方法是完全不用系统转屏,而是所有的转屏都自己写。求更好解决方案。

到此为止。

这篇关于从ViewController初始化一直谈到强制横屏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/602180

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

@postconstruct初始化的操作

从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion);@PostConstruct和@PreDestroy。这两个注解被用来修饰一个非静态的void()方法 。写法有如下两种方式: @PostConstruct Public void someMethod() {}

spring和tomcat初始化的类和注解

1.InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。 spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用 实

LabVIEW环境中等待FPGA模块初始化完成

这个程序使用的是LabVIEW环境中的FPGA模块和I/O模块初始化功能,主要实现等待FAM(Field-Programmable Gate Array Module,FPGA模块)的初始化完成,并处理初始化过程中的错误。让我们逐步分析各部分的功能: 1. Wait for FAM Initialization框架 此程序框架用于等待I/O模块成功初始化。如果在5秒钟内模块没有完成配

dp(背包问题) 恰好、至少、至多初始化

状态表示的初始化(一般情况) f[i][j] i:前i件物品 体积至少为j 枚举体积时可以是负数(体积为负数时等价于体积为0) max f[i][j] = {-0x3f} f[i][0] = 0min f[i][j] = { 0x3f} f[i][0] = 0cnt f[0][0] = 1 体积至多为j 枚举体积时不能是负数 max f[i][j] = 0min f[i][j]

selenium的webdriver三种等待方式(显式等待WebDriverWait+implicitly_wait隐式等待+sleep强制等待)

隐式等待是等页面加载,不是等元素!!! 1、显式等待  一个显式等待是你定义的一段代码,用于等待某个条件发生然后再继续执行后续代码。显式等待是等元素加载!!! from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import

Windows11上使用WSL2,提示:系统尚未使用systemd作为初始化系统(PID 1)启动

前言 略 报错信息 System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down 解决方法 使用如下命令 # windows终端,执行如下命令wsl --update# 登录ubuntu系统,执行如下命令s

[Android] [SnapdragonCamera] 单摄(横屏)阶段总结

在研高通平台的单摄项目中遇到了很多适配问题,做一下初步的总结,为今后遇到相似的问题,提供参考方案。          1. 横屏设置相机预览显示不正常               1.1问题现象                       1.2分析与解决              骁龙相机默认的预览方向是“portrait”。在横屏设备上显示的时候就会出现上面效果。实际