iOS 笔记

一. 点击uiscrollview的子视图,即使不产生滑动,总是会走- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

测试发现:

  • 如果是代码创建的scrollview,在scrollview上面添加子视图button,点击button,不产生滑动,并不会走scrollViewDidEndDecelerating
  • 如果是xib创建的scrollview,点击button,即使不产生任何滑动,总是会走scrollViewDidEndDecelerating,即使添加的是其他子视图uiview,也是一样。这种情况,只需要取消scrollview.pagesnable=yes,就可以了。是不是xib多设置了什么东西,没搞明白。

二. xib自定义的控件,用纯代码加载,报错找不到xib控件拉好的IBOutlet属性

  • 报错:
    *** Terminating app due to uncaught exception   'NSUnknownKeyException',
    reason: '[ setValue:forUndefinedKey:]: 
    this class is  not key value coding-compliant for the key searchBtn
    
  • xib控件File`s Owner关联的类取消,改由view关联对应的类
  • xib的属性全部重新拉线(由File`s Owner改为view关联,拉线的属性不改还是会报错)
  • xib控件是作为另一个xib的子视图,不是由代码加载,不用改

三. scrollview滑动卡住,也不崩溃。

  • (void)layoutSubviews不要做任何frame的更新,因为scrollview滑动会一直调用- (void)layoutSubviews,里面做frame更新,又会反过来调用- (void)layoutSubviews,就会一直卡在这个方法里

四.scrollview里面添加button或者其他控件,不能点击

  • 是因为scrollview加了撑开的contentview,button又加载contentview上,contentview高度或者宽度显示不下button,导致点击传递事件到了contentview就找不到下层的button了。
  • 层级关系: scrollview-> contentview->button,contentview的size装载不下button。传递事件断裂

五. tablefooterview的按钮不能点击

  • 很多帖子讲是tabletooterview``的高度不够,这其实是个很明显的问题,跟上面第四条```道理一样。
  • 但其实,我有次明显设置了footerview的高度,也看了层级关机,没有被遮挡。后来发现,原来是设置了渐变色的原因,因为渐变色是用layer去实现的,直接把渐变色layer加在button上,会遮挡button文字显示。所以写了个分类,渐变色layer加载一个bgview上,再把bgview加在button的最底层。这样就可以实现渐变色背景的按钮。但是!!!!我们点击的响应,也直接点击在bgview上,所以button接收不到响应链。解决办法,bgView.isUserInteractionEnabled = false/或者self.colorView.userInteractionEnabled = NO。部分代码如下:
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = self.bounds
    gradientLayer.colors = array
    gradientLayer.locations = [0.0,1.0]
    gradientLayer.startPoint = CGPoint.init(x: 0, y: 0.5)
    gradientLayer.endPoint = CGPoint.init(x: 1, y: 0.5)
              
    let bgView = UIView(frame: self.bounds)
    bgView.isUserInteractionEnabled = false //不设置这句,button的点击是点击在bgview上
    self.addSubview(bgView)
    self.sendSubviewToBack(bgView)
    bgView.layer.addSublayer(gradientLayer)
    

五. tableview.tableFooterView和tableview.tableHeaderView的高度或者宽度有问题。

  • tableview.tableFooterView要初始化frame
  • tableview.tableFooterView不能直接=self.footerView,解决办法,先赋值一个view,再让view addsubview,如下:
  UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 700)];
  [view addSubview:self.footerView];
  tableview.tableFooterView = view
  • tableview.tableHeaderView同理

六.@property (getter=isActive) BOOL active,设置yes/no,不生效

  • 我遇到的情景是,做聊天气泡时,xib上拉label的left和right约束,根据内容决定label的left或者right约束的active是yes还是no,从而决定label是靠左还是右。然后有时候会出现label左右位置混乱。解决:
    • 拉lable的left或者right作为属性时,是weak修饰,改为strong就可以了(个人猜测,当active=no时,约束不生效的原因,其实就是移除了该约束,然而因为是weak修饰,当再次设置该约束active=yes时,其实该约束已经不存在了,所以更改strong修饰后,再次修改active=yes,还是能生效)。

七。更改状态栏颜色问题(-(UIStatusBarStyle)preferredStatusBarStyle不调用问题)

  • 在info.plist 中 View controller-based status bar appearance 一定要改为为 YES

  • navigationcontroller中添加:

    - (UIViewController *)childViewControllerForStatusBarStyle {
     return self.topViewController;
    }
    
  • 每个Viewcontroller中添加:

    -(UIStatusBarStyle)preferredStatusBarStyle
     {
     return UIStatusBarStyleDarkContent;
     }
    
  • 需要注意,如果有导航栏navigationcontroller,状态栏颜色由导航栏统一管理,事实上由导航栏管理也最好。如果需要单独改变某个状态栏,就单独在那个控制器实现preferredStatusBarStyle

  • 状态一直是白色,怎么也改不到黑色:暗黑模式下无法设置为黑色,关闭暗黑模式 info.plist-->Appearance = Light。关闭之后再检查preferredStatusBarStyle返回的是深色字体还是白色字体。

八。使用UINavigationController,控制器self.view距离顶部的距离,一会高,一会低。(UICollectionView/UITableView顶部会留白的解决办法)

  • 场景:从控制器A,进入控制器C,C的self.view是全屏,距离顶部是0。从控制器B,进入控制器C,C的self.view距离顶部有个一导航栏的高度。这就导致控制器C写这样的布局,也会变化:
  [_headerView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.top.equalTo(self.view).offset(0);
  }];
  • 原因:设置.translucent这个属性,会影响UIViewcontroller的布局:
    • .translucent = NO,导航栏取消毛玻璃效果(导航栏设置什么颜色就是什么颜色,可以理解为会遮挡下一层的self.view),self.view的原点是从导航栏左下角开始计算。
    • .translucent = YES,导航栏显示毛玻璃效果(导航栏会有一层模糊的半透明的毛玻璃效果,可以理解为,即使第一层从导航栏滚过,也会看到一点点,不会遮挡self.view),self.view的原点是从导航栏左上角开始计算。
  • 解决:在哪个控制器设置过self.navigationController.navigationBar.translucent = NO,就在这个控制器viewWillDisappear中,设置回来(self.navigationController.navigationBar.translucent = YES)
  • 或者直接设置:(self.collectionview/self.tableview顶部会留白的解决办法)
    if (@available(iOS 11, *)) {
     self.collectionView.contentInsetAdjustmentBehavior  =     UIScrollViewContentInsetAdjustmentNever;
    }else{
     self.automaticallyAdjustsScrollViewInsets = NO;
    }
    

九。两个UICollectionView嵌套,都使用MJRefresh。滑动方向冲突

  • 场景:UICollectionView(A)和UICollectionView(B)A是横向滚动,每个cell是当前控制器的view大小。B是纵向滚动的瀑布流布局,加载A的cell上。
    这个时候在屏幕上斜方向滑动,会产生AB同时一起斜方向滑动。按照场景需求,应该是要么纵向滚动瀑布流,要么横向滚动页面。
  • 原因:后来看了MJRefresh源码,大概是,他给UICollectionView加了一个滑动手势,滑动时候,把scrollviewcontentInset还是contentOffset来着,把它的值改变,形成继承于scrollview的视图也跟着手势滑动的效果。
    • 至于为什么一个collectionview不会斜方向滑动,两个嵌套就会斜方向滑动,就没有深入追究了
  • 解决:
    • 要么AB,只留一个使用MJRefresh。上拉刷新下拉加载的功能也就只能添加在A或者B上。
    • 要么就把A或者Bmj_headermj_footer给移除掉,其实产生的效果等同于上面。

十.UITableView,用masnory布局约束,发现底部往下偏移了,也就是底部有部分显示不了

  • 场景:这个情况比较特殊,不具备普遍化,自己碰到过,记下来。因为上面第条原因,所以自己在自定义TableView基类的时候,会用一个方法禁止掉MJRefresh功能:
    self.mj_header?.scrollView?.bounces = false
    self.mj_footer?.scrollView?.bounces = false
    self.mj_header?.scrollView?.contentInset = .zero
    self.mj_footer?.scrollView?.contentInset = .zero
    self.mj_header?.endRefreshing()
    self.mj_footer?.endRefreshing()
    self.mj_footer?.removeFromSuperview()
    self.mj_header?.removeFromSuperview()
    

然而,在不需要刷新功能的界面,为了防止出现布局混乱,调用了上面这个方法,结果就出现tableview底部,被屏幕遮住了。而检查约束条件make.edges.equalToSuperview(),这完全没问题。

  • 原因:打开层级显示,定位TableView,发现这段话:
baseClass = UITableView; frame = (0 64; 375 603); clipsToBounds = YES; 
contentOffset: {0, 198}; contentSize: {375, 845}; adjustedContentInset: {0, 0, -44, 0}; 

contentOffset: {0, 198},这说明tableview无缘无故往下移动了198。这是什么原因?

  • 解决:无意中,注释掉上面说的基类tableview中的禁止MJRefresh功能方法中的,两行代码,解决了!而且层级定位发现contentOffset: {0, 0}
       就是注释了这两行代码
       //self.mj_header?.scrollView?.contentInset = .zero
       //self.mj_footer?.scrollView?.contentInset = .zero
    
  • 思考:为什么,MJRefresh中的mj_header/mj_footer,禁止scrollView?.contentInset,会导致UITableView的contentOffset: {0, 198},MJRefresh做了什么?
  • 探究:
    • 原来self.mj_header?.scrollView?等于self.mj_header?.superView等于self(tableView)
    • 然而,断点打印如下:
      (lldb) po self.mj_header?.scrollView?.contentInset
      ▿ Optional
      ▿ some : UIEdgeInsets
      - top : 0.0
      - left : 0.0
      - bottom : 44.0
      - right : 0.0       
      
    • 所以,注释掉那两行代码,就是让tableview.contenInset,由系统建立的距离底部的值bottom=44(正常显示)变成bottom=0(被遮挡一部分),可不就是遮挡了嘛
  • 最后:至于为什么不注释那两行,contentOffset: {0, 0},注释掉就变成contentOffset: {0, 198}。有待考证

十一.UICollectionView截屏,显示不全问题

  • 场景: 自定义布局的UICollectionView,cell是xib,一个section只有一个item。需要截取全部collectionView,生成图片分享。截屏代买如下:
    let frame = collectionView.frame
    UIGraphicsBeginImageContextWithOptions(collectionView.contentSize, false, 0)
    collectionView.layer.frame = CGRect(x:0, y:0, width:kUIScreenWidth, height:collectionView.contentSize.height)
    collectionView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let collectionViewImg = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    collectionView.frame = frame
    return  collectionViewImg
    
  • 问题:上面代码,只会截取已经出现在屏幕的cell,对于未出现的cell,截取的空的。
  • 原因:
    • 原来在基类控制器设置过:
      self.navigationController?.navigationBar.isTranslucent = false
      
    • isTranslucent = false 设置带来的直接结果是,控制器的顶部屏幕的顶部距离一个导航栏的高度
    • 也会导致截屏时,对于未展示过的cell,不会调用datasource去加载cell,所以未展示过的cell的layer,自然是空白的。(为什么会这样,不得而知)
    • isTranslucent 其他的影响

十二. UICollectionView反向序列排列

  • 场景:layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;下,collectionview滚动是从右往左滚动的,那么如果从坐往右滚动呢?比如滑动翻日历,往往进去就是默认最近的日历,往前翻,就需要反向排列了。
  • 解决:自定义UICollectionViewFlowLayout,全部代码如下:
    #import "RCalendarLayout.h"
    
    @implementation RCalendarLayout
    ///决定flowyout是否能主动选择其他滚动方向,默认NO
    - (BOOL)flipsHorizontallyInOppositeLayoutDirection
    {
      return YES;
    }
    ///决定flowyout的滚向,默认是LeftToRight
    - (UIUserInterfaceLayoutDirection)developmentLayoutDirection
    {
      return UIUserInterfaceLayoutDirectionRightToLeft;
    }
    @end
    
  • 这两个方法的理解可能有误,最好点进原方法,看注释。

十三. UICollectionView的UICollectionElementKindSectionHeader,设置在Cell的背后。

  • 默认自定义UICollectionElementKindSectionHeader的时候,会在cell的上层,如果要cell遮挡sectionHeader的效果,就要自定义UICollectionViewFlowLayout,具体方法如下:
     - (NSArray<__kindof UICollectionViewLayoutAttributes *>  *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        NSArray *attributes  = [[super layoutAttributesForElementsInRect:rect] copy];
        for (UICollectionViewLayoutAttributes *attr in attributes) {
        CGRect frame = attr.frame;
        if (frame.size.height < 300) {
            CGPoint origin = frame.origin;
            origin.y = origin.y - 60;
            frame.origin = origin;
            attr.frame = frame;
            attr.zIndex = attr.indexPath.row + 1;///此属性是关键,cell.zIndex 最小值是1,依次递增
        }else{
            attr.zIndex = 0;///此属性是关键,sectionheader.zIndex 永远是0
         }
       }
      return  attributes; }
    

十四. UITableView的tableHeaderView | sectionHeaderView留白解决方法

  • 方法1:tableHeaderView: 一般解决办法(带有导航栏)

    ///self=UIViewController
    ///tableView.tableViewStyle = UITableViewPlan
    
    if #available(iOS 11.0, *) {
             tableView.contentInsetAdjustmentBehavior = .never
         } else {
             self.automaticallyAdjustsScrollViewInsets = false
         }
    
  • 方法2: 我的场景是:没有导航栏tableView.tableViewStyle = UITableViewGroup设置方法1后,还是留白。

    • 需要再加上以下属性:
      其实就是要设置tableView的sectionHeaderViewtableHeaderView的高度
       tableView.tableHeaderView = UIView(frame:.zero)
       # 如果上面不生效,则用下面方法试试
      self.tableView.tableHeaderView = UIView(frame:CGRect(x:0, y:0, width:0.01, height: 0.01))
      self.tableView.tableFooterView = UIView(frame:CGRect(x:0, y:0, width:0.01, height: 0.01))
      
  • 方法3: 我的场景是:顶部空白高度=22,(没有导航栏,tableView.tableViewStyle = UITableViewPlain),iphone8,ios16.1,

     if #available(iOS 15.0, *) {
            self.tableView.sectionHeaderTopPadding = 0
     } else { }
    
  • 其他类似的问题,可能会误导致顶部有一段小留白的:

    • sectionHeaderView: 也有个类似的场景:tableview的style = .group时,sectionHeaderView的高度,会有默认的一段高度。即使设置了delegatesectionHeaderView的高度return 0不生效。return = 0.01生效。
    • sectionFooderView同上。
    • 小结:在某些iOS版本中,tablestyle = .group场景下,无论是tableHeaderView还是sectionHeaderView,对其相应的方法设置高度=0,都无效,需要设置高度=0.01

十五.关于切圆角不生效问题

  • 下面代码不生效问题:
     cell.contentView.cornerRadius = 10;
     cell.contentView.layer.masksToBounds = YES;
    
  • 检查cell的颜色,color会影响layer。某种情况下发现,xib设置的cell,如果背景颜色是默认的,上面呢代码不会生效。
  • 设置圆角加阴影,用以上方法,会产生离屏渲染,用下面方法:
    let bounds = CGRect(x:0, y: 0, width:w, height: 216.0/162*w)
    self.layer.shadowPath = UIBezierPath(roundedRect:bounds, byRoundingCorners:[.topLeft,.topRight,.bottomLeft,.bottomRight], cornerRadii: CGSize(width:4, height: 4)).cgPath
    self.layer.shadowColor = "#CFCFCF".color().cgColor
    self.layer.shadowOffset = CGSize(width: 0, height: 0)
    self.layer.shadowRadius = 4
    self.layer.shadowOpacity = 1
    self.layer.cornerRadius = 4
    self.layer.masksToBounds = false ///一定要设置这个
    

十六.UITextView带link的富文本,下划线和link字符串颜色设置问题

let text = "By clicking start, you agree to our Terms and Conditions and Privacy Statement.".attri(UIFont.systemFont(ofSize: 14), "#E2665C".color())
var attri = [.underlineStyle:NSUnderlineStyle.single.rawValue,.underlineColor:"#E2665C".color(),.link:"privacy"] as [NSAttributedString.Key : Any]
var attri2 = [.underlineStyle:NSUnderlineStyle.single.rawValue,.underlineColor:"#E2665C".color(),.link:"agreement"] as [NSAttributedString.Key : Any]

text.addAttributes(attri, range: NSRange(location:36, length: 20))
text.addAttributes(attri2, range: NSRange(location: 61, length: 18))
        
self.agreement.attributedText = text
self.agreement.linkTextAttributes = [.foregroundColor:"#E2665C".color()]

十七.XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a

  • 原因: Xcode 14.3移除了arc目录,因为Xcode 14支持的最低部署目标iOS 11, 最低部署版本的系统都已经内置了ARC相关的库, 我们只需要将第三方库部署目标的iOS版本设置成和应用最低部署目标的iOS版本即可
  • 解决方法: 原方法摘自这
post_install do |installer|
  installer.generated_projects.each do |project|
    project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' #9.0
         end
    end
  end
end

但是,我的Podfile,加上上面方法,会报很多错误!!!

  • 解决方法2: 根据上面所说,将第三方库的最低部署iOS的版本,改到ios11以上就可以了
    • Xcode 左侧文件栏最顶部,有个⚠️号的按钮,点击,找到报错的第三方库
    • 左侧文件栏,点击Pods.xcodeproj->Targets->对应三方库->General->修改Minimum Deployments = iOS11以上

十八.Command PhaseScriptExecution failed with a nonzero exit code

  • 该报错有一定概率是cocoapods 的问题,可以试试升级版本:
    • 更新gem:sudo gem update --system

    • 下载最新版本:sudo gem install -n /usr/local/bin cocoapods --pre

你可能感兴趣的:(iOS 笔记)