iPad横竖屏下的代码适配,详解iPad横竖屏切换解决
分类:美高梅游戏官网网站

详解iPad横竖屏切换解决方案是本文要介绍的内容,不多说,先来看内容。由于ipad横竖屏不同,所以好的应用,横竖屏的页面布局也不一样。那么就需要横竖屏的整体解决方案。先看一个横竖屏布局不一样的界面。

layoutSubviews何时调用的问题(转),layoutsubviews调用

原文地址在我的个人主页

图片 1 图片 2

今天跟旺才兄学习了一下UIView的setNeedsDisplay和setNeedsLayout方法。首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样可以拿到UIGraphicsGetCurrentContext,就可以画画了。而setNeedsLayout会默认调用layoutSubViews,就可以处理子视图中的一些数据。

宗上所诉,setNeedsDisplay方便绘图,而layoutSubViews方便出来数据。

ipad横竖屏切换解决方案
2011年08月01日 星期一 10:09
由于ipad的横竖屏不同,所以好的应用,横竖屏的页面布局也不一样。那么就需要横竖屏的整体解决方案。先看一个横竖屏布局不一样的界面。

上面两张图是来自同一个界面的横竖版的截屏。可以看出,横竖版显示的内容相同,但是界面布局不同。要实现上述布局,主要是运用UIView中 layoutSubviews方法。当UIView设置为自动适配屏幕时,当用户旋转设备的时候,会调用layoutSubviews方法,我们只需重写 这个方法,然后判断用户屏幕的方向。在调整每个空间的位置即可。

图片 3

图片 4

下面是实现上述界面的最简单的原型:

首先分析可以知道左面是图片,右面是一个图片加文字的视图。下面就实现一个左面视图右面是一个图加一段字的事例。
事例的截图如下:

图片 5

图片 6

其中右面的文字和绿色部分是用一个子视图封装的。
整个布局是我在主视图中添加了一个ContentView视图,在ContentView视图中添加了一个ArticleView视图。
其中ArticleView和ContentView的xib文件都打开了

图片 7

在ContentView中重写layoutSubviews方法,然后根据stausbar的方向判断当前视图的横竖屏。具体代码:
-(void)layoutSubviews{ 
[super layoutSubviews]; 
UIDeviceOrientation interfaceOrientation=[[UIApplication sharedApplication] statusBarOrientation]; 
if (interfaceOrientation == UIDeviceOrientationPortrait || interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) { 
//翻转为竖屏时 
[self setVerticalFrame]; 
}else if (interfaceOrientation==UIDeviceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationLandscapeRight) { 
//翻转为横屏时 
[self setHorizontalFrame]; 

}

-(void)setVerticalFrame 

NSLog(@"竖屏"); 
[titleLable setFrame:CGRectMake(283, 0, 239, 83)]; 
[leftView setFrame:CGRectMake(38, 102, 384, 272)]; 
[rightView setFrame:CGRectMake(450, 102, 282, 198)]; 
}

-(void)setHorizontalFrame 

NSLog(@"横屏"); 
[titleLable setFrame:CGRectMake(183, 0, 239, 83)]; 
[leftView setFrame:CGRectMake(168, 122, 384, 272)]; 
[rightView setFrame:CGRectMake(650, 122, 282, 198)]; 
}

在具体的横竖屏方法中,从新设置各个组件的坐标即可。

接下来在ContentView中添加ArticleView视图。
-(id)initWithCoder:(NSCoder *)aDecoder 

if ((self = [super initWithCoder:aDecoder])) {

NSArray *arrayContentView =[[NSBundle mainBundle] loadNibNamed:@"ArticleView" owner:self options:nil]; 
rightView=[arrayContentView objectAtIndex:0]; 
[self addSubview:rightView]; 

return self; 
}

由于我用的是xib,所以初始化方法为initWithCoder,在这个中添加新的视图。

同样在ArticleView中设置横竖屏相应空间的坐标即可。
-(void)layoutSubviews{ 
[super layoutSubviews]; 
UIDeviceOrientation interfaceOrientation=[[UIApplication sharedApplication] statusBarOrientation]; 
CGRect rect=self.frame; 
rect.size.width=282; 
rect.size.height=198; 
[self setFrame:rect]; 
if (interfaceOrientation == UIDeviceOrientationPortrait || interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) { 
//翻转为竖屏时 
[self setVerticalFrame]; 
}else if (interfaceOrientation==UIDeviceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationLandscapeRight) { 
//翻转为横屏时 
[self setHorizontalFrame]; 

}

-(void)setVerticalFrame 

NSLog(@"竖屏"); 
[contentView setFrame:CGRectMake(12, 6, 250, 125)]; 
[textLable setFrame:CGRectMake(50, 139, 182, 39)]; 
}

-(void)setHorizontalFrame 

NSLog(@"横屏"); 
[contentView setFrame:CGRectMake(12, 6, 106, 158)]; 
[textLable setFrame:CGRectMake(135, 11, 147, 39)]; 
}

 

layoutSubviews何时调用的问题

layoutSubviews何时调用的问题,这个方法是当你需要在调整subview的大小的时候需要重写(我这个翻译不严谨,以下是原文:You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.),但有时候经常指望它被调用的时候没被调用,不希望它被调用的时候被调用了,搞的很上火。根据国外社区一个人帖子,做了总结性翻译。

layoutSubviews在以下情况下会被调用:

1、init初始化不会触发layoutSubviews
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

今天跟旺才兄学习了一下UIView的setNeedsDisplay和setNeedsLayout方法。首先两个方法都是异步执行...

你可能非常了解用不同的方式去适配不同尺寸的iPhone屏幕,在适配iPhone屏幕时你需要考虑的只是屏幕大小变化带来的UI元素间隔的变化,但是在iPad上主要针对的是横竖屏下完全不同的UI元素的布局,在这种情况下要考虑的就不仅仅是元素之间间隔这种问题了,除了要确保UI元素在这两种模式下的正确显示还要兼顾屏幕旋转的过渡动画.下图是QQZone For iPad 在横竖屏下的布局,可以看到横竖屏下菜单栏的布局方式差别很大.

上面两张图是来自同一个界面的横竖版的截屏。可以看出,横竖版显示的内容相同,但是界面布局不同。要实现上述布局,主要是运用UIView中layoutSubviews方法。当UIView设置为自动适配屏幕时,当用户旋转设备的时候,会调用layoutSubviews方法,我们只需重写这个方法,然后判断用户屏幕的方向。在调整每个空间的位置即可。

QQZone for iPad GitHub地址

下面是实现上述界面的最简单的原型:

图片 8

首先分析可以知道左面是图片,右面是一个图片加文字的视图。下面就实现一个左面视图右面是一个图加一段字的事例。

QQZone for iPad 竖屏

事例的截图如下:

图片 9

图片 10 图片 11

QQZone for iPad 横屏

其中右面的文字和绿色部分是用一个子视图封装的。

屏幕适配的N种方法

无论iPad还是iPhone适配不同屏幕(尺寸,方向)的方式都跑不出以下几种,以下会一一对不同方式做一下简单的回顾.

整个布局是我在主视图中添加了一个ContentView视图,在ContentView视图中添加了一个ArticleView视图。

Autoresizing

Autoresizing可以说是Autolayout始祖,Autoresizing的是一项比较有历史的技术了,其在iOS2的时代就推出了.当设置UIView实例对象的autoresizesSubviews属性为true(默认值为true),那么其子view会根据自已的autoresizingMask属性值自动调整与superview的位置和大小关系.autoresizingMask有六种可组合的使用的值,默认值是.None.这六种有效枚举值的意思如下:

  • FlexibleLeftMargin 按比例跟随父控件变化的左间距
  • FlexibleWidth 按比例跟随交控件变化的宽度
  • FlexibleRightMargin 按比例跟随父控件变化的右间距
  • FlexibleTopMargin 按比例跟随父控件变化的顶部间距
  • FlexibleHeight 按比例跟随控件变化的高度
  • FlexibleBottomMargin 按比例跟随父控件变化的底部间距

另外在xib,storyboard取消Autolayout时(Autoresizing与Autolayout相互冲突)可以在Size inspector可以更加直观地按需求进行组和使用.Autoresizing技术在一定应用场景下可以勉强使用但应对更为精细的布局就无能为力了,你可以在使用Autoresizing同时重写layoutSubviews方法去做更为精细的布局,尽管如此但还是不推荐这么做,因为同时得写layoutSubviews和使用Autoresizing去布局会让你的布局逻辑变得不清晰,这将给后期的维护带来麻烦.

图片 12

Autoresizing in storyboard

其中ArticleView和ContentView的xib文件都打开了

Autolayout

Autolayout是iOS6时代引入的技术,专门用来处理不同屏幕尺寸下的UI布局.从Xcode6开始Autolayout配合xib,storyboard极大的提高屏幕的适配工作效率.在一定程度上甚至可以完全摆脱设置frame布局的方式.由于storyboard,xib在多人合作开发冲突不断的尴尬境地,在实际的开发中多使用第三方框架用代码进行Autolayout布局.这样既避免了解决冲突麻烦又享受到了Autolayout带来的宏利.比较受欢迎的Autolayout每三方框架有Masonry还有GSD_iOS大神的SDAutoLayout

尽管Autolayout有很多好处但还是很多代码党不愿使用,究其原因还是约束.约束的问题大至可以分约束冲突和约束不满足两大类,当在storyboard中对一个复杂的界面进行Autolayout约束,一但出现问题将很难排查,用代码行约束往往程序运行起来才能确认约束是否满足条件,同样排查起来也不是那么方便.关于Autolayout这不再占用过多的篇幅,网上有相当多的资料可供参考.

图片 13

SizeClass

SizeClass是要配合Autolayout使用的,SizeClass实际上是对屏幕尺寸的抽象,把屏幕宽高分成Compact:紧凑、Regular:宽松、Any:任意三种类型这样就可以组合出九种不同的屏幕类型.在storyboard,xib编辑界面下最下方可以选择某一约束在只在某一类屏幕下生效.这样可以在不同屏幕下得到不同的UI布局效果.关于SizeClass的使用可以参考raywenderlich系例文章.

图片 14

SizeClass

在ContentView中重写layoutSubviews方法,然后根据stausbar的方向判断当前视图的横竖屏。具体代码:

代码计算坐标

在所有的布局方法中这种可能是最费体力的一项,因为所有的UI元素都需要一个一个明确的计算或者指定出来.尽管如此正因为每个元素的frame是手动计算因此灵活性也非常大你可以随心所欲的计算每个控件的frame,出现问题时也非常好排查.如果需要动态的改变view的frame就需要重写父控件的layoutSubviews方法,在重写的layoutSubviews明确计算出frame. 如果view是固定的则只需要在添加到父控件时指定view的frame.一般常见的代码布局形式如下:

override func layoutSubviews() {
    super.layoutSubviews()
    let x: CGFloat = 0
    let y: CGFloat = frame.height * 0.7 // 根据父控件高度按比例确定y座标
    let w: CGFloat = frame.width
    let h: CGFloat = frame.height - y  // 根据父控件高度,子控件按比例调整高度
    subView.frame = CGRectMake(x, y, w, h)
}

上面是常见的根据父控件动态调整子控件frame的形式,真实开发中可能还需要考虑横竖屏下动态的布局(下面将提到的),以上基本形式可以根据需求进行扩展.当然你也可以不用重写layoutSubviews方法而在需要改变frame的时机显式直接改变frame,但在这种方式并不符合苹果的逻辑.在view的层次结构中某一view肯定是有superview的,而子view是否变化,以及什么时候变化应是由superview来决定的,在一个多层次结构的view视图中如果显示的设定子view的frame那你不得不根据view的层次结构一级一级的设置子view的frame.superview和subView之前会出现较强的关联性.理想情况下一个superview应该只关注自身的subView的布局,无论这个superview的frame或层次结构怎么变化其subviews并不需要知道.因此我觉得比较好的做法是有所关于subviews的frame的设定都应该重写layoutSubviews,在layoutSubviews中去做.这样做的另一个好处是屏幕旋转时你并不需要显示的去写UIView动画.

-(void)layoutSubviews{       [super layoutSubviews];       UIDeviceOrientation interfaceOrientation=[[UIApplication sharedApplication] statusBarOrientation];       if (interfaceOrientation == UIDeviceOrientationPortrait || interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {           //翻转为竖屏时           [self setVerticalFrame];       }else if (interfaceOrientation==UIDeviceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationLandscapeRight) {           //翻转为横屏时           [self setHorizontalFrame];       }   }    -(void)setVerticalFrame   {       NSLog(@"竖屏");       [titleLable setFrame:CGRectMake(283, 0, 239, 83)];       [leftView setFrame:CGRectMake(38, 102, 384, 272)];       [rightView setFrame:CGRectMake(450, 102, 282, 198)];   }    -(void)setHorizontalFrame   {       NSLog(@"横屏");       [titleLable setFrame:CGRectMake(183, 0, 239, 83)];       [leftView setFrame:CGRectMake(168, 122, 384, 272)];       [rightView setFrame:CGRectMake(650, 122, 282, 198)];   } 

layoutsubviews的调用时机

用代码在layoutSubviews中布局你必需要知道系统会在哪些时机去调用layoutsubviews函数.关于这个问题的结论可以参看stackoverflow的讨论.关于这个问题我个人比较赞同第二个回答者对第一个回答的纠正.基本上layoutsubviews会在以下几种情况下调用:

  • 当view的bounds发生改变时
  • 当view的直接subView的bounds发生改变时
  • 当subView添加或移除时
  • 调用setNeedsLayout方法会在下一个显示周期主动调用layoutsubviews

在具体的横竖屏方法中,从新设置各个组件的坐标即可。

如何获取当前屏幕方向

关于获取当前屏幕方向我所知道的方法仅包括以下几种:

  • 通过控制器的interfaceOrientation只读属性获取,iOS8后过期
  • 能过状态栏的方向间接获取,UIApplication的只读属性statusBarOrientation,iOS9后过期
  • 能过UIDevice只读属性orientation获取.需主动调用beginGeneratingDeviceOrientationNotifications开启通知
  • 通过根控制器的view宽高推导获取,当高>宽为竖屏否则为横屏

接下来在ContentView中添加ArticleView视图。

Demo预览

下面是这个Demo的最终效果,我将通个下面的例子记录我认为合理的代码适配横竖屏的方式.

图片 15

Demo预览慢速

图片 16

Demo预览正常

-(id)initWithCoder:(NSCoder *)aDecoder   {       if ((self = [super initWithCoder:aDecoder])) {            NSArray *arrayContentView =[[NSBundle mainBundle] loadNibNamed:@"ArticleView" owner:self options:nil];           rightView=[arrayContentView objectAtIndex:0];           [self addSubview:rightView];       }       return self;   } 

Demo分析

上面一些基础的知识将有助于理解Demo的做法,所以尽管有一点废话连篇的感觉好在也并不是一无是处.在讲解Demo的实现思路之前你可以在GitHub下载这个Demo,以便更方便的查看我讲到的代码.

Demo中最复杂的,横竖屏布局变化最大的部分就是左侧的菜单栏可以称它为Dock栏,通过旋转屏幕可以看到原生QQZone HD的Dock栏的变化.可以根据变化的特征将整个Dock栏分为三部分.一是顶部的头像 二是中间的类TabBar,我称它为TabBar 三是 底部的快捷导航菜单.因此DockView的subview包含iconButton,tabBarView,menuBar三个,而这三个subview又可以分另包含各自的子控件.

图片 17

view层次结构

实际上实现QQZone for iPad屏幕的横竖屏的布局并不复杂.一个view要知道怎样在layoutSubviews中去布局其子view只需知道当前其superview的状态(横竖屏).在这里我声明了一个协议,这个协议只包含一个获取当前view是否是竖屏的方法.让每一个需要根据横竖屏动态变化的view都实现这个协议的方法,这样在layoutSubviews方法就可以询问当前应该怎么样布局子控件而当前状态是由父控件状态决定的.由此形成了屏幕状态的传递链,使得每个veiw只关心自身直接subview的布局.

// UIViewisPortrait协议
protocol UIViewisPortrait: NSObjectProtocol {
    func isPortrait() -> Bool
}

根控制器view的任意subview可都可以通过如下代码获取当前是否是竖屏

// subview 如何获取superview状态
func isPortrait() -> Bool {
    guard let superview = superview else { // 如果不存大superview默认返回竖屏
        return true
    }
    return ((superview as? UIViewisPortrait)?.isPortrait())!
}

而根控制器view则直接通过宽高获取屏幕状态

func isPortrait() -> Bool {
    return frame.width < frame.height
}

当前view知道是横竖屏后就可以直接在layoutsubviews布局子控件了,以menuBar为例

override func layoutSubviews() {
    super.layoutSubviews()
    guard subviews.count > 0 else {
        return
    }

    var x, y, w, h:CGFloat
    for (index, view) in subviews.enumerate() {
        if isPortrait() == true {
            w = frame.width
            h = kDockItemHeight
            x = 0
            y = CGFloat(index) * h
            view.frame = CGRect(x: x, y: y, width: w, height: h)
        } else {
            w = frame.width / CGFloat(subviews.count)
            h = kDockItemHeight
            x = CGFloat(index) * w
            y = frame.height - h
            view.frame = CGRect(x: x, y: y, width: w, height: h)
        }
    }
}

其它类型的所有子控件都可以用类似的方法进行布局,如此你只需要定义一个view并始之成为某个View的subview,在你定义的view中你可以随意的获取屏幕状态布局子控件了.

为了在根控制器View的layoutSubviewsr的方法中布局DockView,需要重写控制器的loadView方法,让控制器加载自定义的View. 如果你注意到原生QQZone for iPad的内容显示区域在横竖屏下的变化会发现在横坚屏下内容显示区域宽都是一样的,所以还需要在根控制器View中添加一个容器View以显示内容.

class HomePageView: UIView, UIViewisPortrait {
    private lazy var dockView:DockView = DockView()

    lazy var contentView: UIView = {
        let contentView = UIView()
        contentView.backgroundColor = UIColor.whiteColor()
        // 作为容器View,子控制器的view将添加到容器view上
        self.addSubview(contentView)
        return contentView
    }()

    func isPortrait() -> Bool {
        return frame.width < frame.height
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(dockView)
        dockView.backgroundColor = globalBackgroudColor
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        dockView.frame.size.height = frame.height
        dockView.frame.size.width = isPortrait() ? kDockProtraitWidth : kDockLandscapeWidth

        let x: CGFloat = dockView.frame.width
        let y: CGFloat = 20
        // 无论横竖屏,内容视图的宽都一样
        let w: CGFloat = min(frame.width, frame.height) - kDockProtraitWidth
        let h: CGFloat = frame.height - y

        contentView.frame = CGRectMake(x, y, w, h)
    }

}

最后由于内容区域不用区分横竖屏,因此内容区域的子视图可以只考虑竖屏的情况,以上可以说得不是很清楚,如果感觉有兴趣可下载原码参阅.

由于我用的是xib,所以初始化方法为initWithCoder,在这个中添加新的视图。

总结

以上重点仅仅是用代码进行iPad横竖屏适配方法的探讨,这里只是记录了我认为较为合理的方法,当然这种方法可能并不适用所有的布局,毕竟每个App都有自己独特的UI部分.如果觉得这种方法不好欢迎指出,我将虚心请教.如果这个方法对你的业务提供了一点点的灵感希望点个赞,以上完.

同样在ArticleView中设置横竖屏相应空间的坐标即可。

-(void)layoutSubviews{       [super layoutSubviews];       UIDeviceOrientation interfaceOrientation=[[UIApplication sharedApplication] statusBarOrientation];       CGRect rect=self.frame;       rect.size.width=282;       rect.size.height=198;       [self setFrame:rect];       if (interfaceOrientation == UIDeviceOrientationPortrait || interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {           //翻转为竖屏时           [self setVerticalFrame];       }else if (interfaceOrientation==UIDeviceOrientationLandscapeLeft || interfaceOrientation == UIDeviceOrientationLandscapeRight) {           //翻转为横屏时           [self setHorizontalFrame];       }   }    -(void)setVerticalFrame   {       NSLog(@"竖屏");       [contentView setFrame:CGRectMake(12, 6, 250, 125)];       [textLable setFrame:CGRectMake(50, 139, 182, 39)];   }    -(void)setHorizontalFrame   {       NSLog(@"横屏");       [contentView setFrame:CGRectMake(12, 6, 106, 158)];       [textLable setFrame:CGRectMake(135, 11, 147, 39)];   } 

源代码:

小结:详解iPad横竖屏切换解决方案的内容介绍完了,通过ipad屏幕切换的内容,是不是以及很清楚了,最后希望本文对你有所帮助。

iPad横竖屏切换 解决方案是本文要介绍的内容,不多说,先来看内容。由于 ipad 的 横竖屏 不同,所以好的应用, 横竖屏 的页面布局也...

本文由美高梅网址发布于美高梅游戏官网网站,转载请注明出处:iPad横竖屏下的代码适配,详解iPad横竖屏切换解决

上一篇:Windows控制台的使用技巧,解决命令行的乱码以及 下一篇:没有了
猜你喜欢
热门排行
精彩图文