iOS开发程序员iOS 艾欧艾斯

图文混排之控件使用

2016-08-26  本文已影响1398人  啸寒

图文混排在iOS开发中经常遇到, 故总结了多种解决方案, 以便将来使用。本文先总结简单的方法-对控件的使用。这些控件包括UIWebView, UILabel, UITextView, UITextField, 都可以进行图文混排, 各有各的使用场景。

下图是控件基础架构, iOS7以前这几个控件都是基于WebKit开发, 而iOS7之后推出了TextKit, 重写了TextView, Label, TextField这几个控件。

至于怎么使用网络图片, 其实很简单, 只需要在图片下载完之后插入到指定位置就可以了。

// 高亮
attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, 3))

// 下划线
attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(0, 10))

// 字体
attributedText.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(50), range: NSMakeRange(20, 10))

// 背景色
attributedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellowColor(), range: NSMakeRange(30, 10))

// 删除线
attributedText.addAttribute(NSStrikethroughStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(120, 20))
   
// 斜体
attributedText.addAttribute(NSObliquenessAttributeName, value: 1, range: NSMakeRange(100, 10))
   
// 阴影
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 3.0, height: 3.0)
shadow.shadowColor = UIColor.redColor()
attributedText.addAttribute(NSShadowAttributeName, value: shadow, range: NSMakeRange(0, 15))
   
// 横竖文本
attributedText.addAttribute(NSVerticalGlyphFormAttributeName, value: 0, range: NSMakeRange(70, 10))

丰富多样的效果出现了


至此, Label的图文混合(包括富文本)处理已经完成, 至于排版视具体情况而定。

但是, Label的富文本处理出现了几个问题, 现一并记录在此

// 下划线
attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(0, 10))

// 阴影
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 3.0, height: 3.0)
shadow.shadowColor = UIColor.redColor()
attributedText.addAttribute(NSShadowAttributeName, value: shadow, range: NSMakeRange(0, 15))

出现了以下效果


// 下划线
attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(0, 10))

// 阴影
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 3.0, height: 3.0)
shadow.shadowColor = UIColor.redColor()
attributedText.addAttribute(NSShadowAttributeName, value: shadow, range: NSMakeRange(100, 15))

结果阴影效果未出现


这两个问题都是同一个原因导致, 只要将下划线的范围索引改成非0就都可以正常显示, 这里有相同问题的讨论, 可供参考。

三. UITextView

这三个类之间可以形成一对多的关系, 如图所示(图来自苹果)


此处我们只创建一个一对一的关系

// 创建TextStorage, TextStorage必须被强引用
self.textStorage = NSTextStorage(string: text)

// 创建LayoutManager
let layoutManager = NSLayoutManager()
self.textStorage?.addLayoutManager(layoutManager)
 
// 创建TextContainer   
let textContainer = NSTextContainer(size: self.view.bounds.size)
layoutManager.addTextContainer(textContainer)
   
// 创建TextView, 传入textContainer   
self.textView = UITextView(frame: self.view.bounds, textContainer: textContainer)
self.textView?.delegate = self
self.view.addSubview(self.textView!)

这样就创建好了, 而例如高亮, 阴影等样式可以像之前那样直接设置, 此处不多说了。然后创建图片视图

self.imageView = UIImageView(image: UIImage(named: "catanddog"))
self.imageView?.center = CGPointMake(self.view.bounds.size.width / 2, self.imageView!.frame.size.height / 2 + 200)
self.view.addSubview(self.imageView!)

// 并给imageView加入手势, 为了拖动图片
self.imageView?.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(imagePan)))
self.imageView?.userInteractionEnabled = true

界面都创建好了, 那么就来更新图片在文本中的位置

func updateExclusionPaths() {
    // 计算图片所占范围
    var imageRect = self.textView?.convertRect(self.imageView!.frame, fromView: self.view)
    imageRect!.origin.x -= self.textView!.textContainerInset.left;
    imageRect!.origin.y -= self.textView!.textContainerInset.top;
    let path = UIBezierPath(rect: imageRect!)
    self.textView?.textContainer.exclusionPaths = [path]
}

这样就出现了图文混排我们所需要的效果了, 既然我们加入了拖动图片的手势, 那么怎么让文本图片拖动而变化了

func imagePan(pan: UIPanGestureRecognizer) {
    if pan.state == .Began {
      self.panOffset = pan.locationInView(self.imageView!)
    }
       
    let location = pan.locationInView(self.view)
    var imageCenter = self.imageView!.center
       
    // 让图片随手势变化位置   
    imageCenter.x = location.x - self.panOffset.x + self.imageView!.frame.size.width / 2
   imageCenter.y = location.y - self.panOffset.y + self.imageView!.frame.size.height / 2
        
   self.imageView?.center = imageCenter
   self.imageCenterY = imageCenter.y + self.textView!.contentOffset.y + navigationBarHeight
      
   // 更新文本排版  
   updateExclusionPaths()
}

此时, 拖动图片, 文本也可随着图片位置的变化而重新排版了, perfect!!!有没有? 但是, 如果文本太长导致textView可以上下滚动了, 完了, 图片不动了

// 实现ScrollViewDelegate协议, 让textView滚动的时候, 图片也动起来
func scrollViewDidScroll(scrollView: UIScrollView) {
    self.imageView?.center = CGPointMake(self.imageView!.center.x, self.imageCenterY - (scrollView.contentOffset.y + navigationBarHeight))
}

perfect!!! 我们来看看效果

![](http://oc3j5gzq3.bkt.clouddn.com/2016-08-26-2016-08-26 11.55.22.gif)

可是还是有一个问题, 当设置了字体样式时就出问题了

// 字体
self.textStorage?.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(50), range: NSMakeRange(220, 1))

设置字体后, 当拖动图片到这个大字体附近就会出现大段空白的情况, 谁能告诉我为什么? 有大牛能帮解释下么?

好了, 这篇到此结束了, 源码在此, 请查收!!!

参考:
https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html
https://objccn.io/issue-5-1/
https://www.raywenderlich.com/50151/text-kit-tutorial

本文由啸寒原创, 转载请注明出处!!!

上一篇下一篇

猜你喜欢

热点阅读