开源项目-WaterMark

作者: 轩辕小羽 | 来源:发表于2016-08-27 22:23 被阅读478次
屏幕快照 2016-08-27 下午10.19.21.png

前言

这里引用应用描述里的一句话:

妻子做微商很辛苦所以想做个软件来缓解她的压力,于是诞生了这个软件.在她的鼓励下把这个软件上线到Appstroe上,希望能帮助到你们

本项目代码全部开源,只删除了LeanCloud相关的id和key,如果我以后不小心把key和id push上去了各位小伙伴一定要提醒我哈...

这个项目是业余时间瞎敲出来的...很多代码都是自己一边尝试一边敲出来的...所以大家可以挑重点看

我准备把WaterMark 和以后其他的开源项目写成一个系列的博客,每次更新版本都会把遇到的难点和坑点总结出来..喜欢的话大家可以收藏,关注支持一下

希望本篇博客能帮助菜鸟了解iOS项目开发中的常识

欢迎大家一起讨论正确的开发姿势

跪求大神带我飞!

(在下英文渣,请各位老爷观看时利用脑内runtime 把不对的单词替换成对的单词...哈哈哈,我会好好背单词的!)

回归正题

项目地址:https://github.com/Lafree317/WaterLabel

AppStroe:https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1148289486&mt=8

架构

项目文件结构

项目中现在集成的第三方开源库有:

  • Pod
  • LeanCloud Swift-alpha版(坑): 用于反馈数据的云存储
  • MBProgressHUD 1.0 :提示控件 每个项目必备
  • RxSwift和RxCocoa: 现在代码中还没有用到,到时候写登陆的时候准备这个写响应式
  • SnapKit: Swift版的Messary 炒鸡好用
  • Vendors
  • ShowString 我司小哥改装的一个提示框,优点在于提示的时候还可以对页面进行UI交互
  • TZImagePickerController 一个1000+star图片选择库,这个开发者很热心发issues很快就会回复你并解决问题
  • IGLDropDownMenu 一个下拉抽屉式动画,使用起来非常方便
  • ZEViewKit 我自己封装的一些小控件,准备再赞一些一起上传到Pod中
  • Scenes里有一个ZEVC是准备以后所有开源项目中公用的反馈和登陆界面

项目架构采用最基本的MVC

因为不是公司项目喜欢自己瞎搞一些东西所以一些被我改畸形了
这里详细说一下:

我发现Swfit的Extension太过强大于是将Model层和View层都用extension来代替了,如feedBackController的代码就被我改成:

  • Controller
class ZEFeedBackContoller: UIViewController,UITextFieldDelegate,UIScrollViewDelegate {
    // 各种属性
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setUI()
    }
  • Model
extension ZEFeedBackContoller {
    // model方法
    func sendFeedBack(){
    }
}
  • View
extension ZEFeedBackContoller {
    // 添加UI
    func setUI(){
    }

首先强调一点...这是我自己胡乱尝试敲出来的...没有看过类似代码..所以不推荐在正常项目中使用,只是讲一下自己的思路希望能帮助到你

这样做的好处我觉得有几点

  • 代码都为C的代码,不存在跨类调用方法和取值(高内聚?)
  • 上下两个控制器传值调用的时候也更方便一些(低耦合?)
  • C代码很少,只把主要的方法C中,如果想扩展相应功能或修改bug可以直接找到位置去进行操作

我觉得不好的地方

  • 代码较为混乱,不适合多人开发?
  • 具体功能重用性差,不利于其他项目使用

我一直自己摸索学习Swift,还没有找到它正确的开发姿势,希望大家能教我正确的开发姿势

项目中还有一小部分面向协议

如给UIView添加一个滑动手势,这里应该如果优化一下应该 extenion到具体类 而不是直接给UIView添加..

protocol ViewGestureRecognizer{
    
    
}
extension UIView:ViewGestureRecognizer{
    func addPan(){
        self.userInteractionEnabled = true
        let pan = UIPanGestureRecognizer(target: self, action: #selector(pan(_:)))
        self.addGestureRecognizer(pan)
    }
    func pan(pan:UIPanGestureRecognizer){
        let point = pan.translationInView(self)
        self.transform = CGAffineTransformTranslate(self.transform, point.x, point.y)
        pan.setTranslation(.zero, inView: self)
    }
}

一些细节

水印的绘制是由EditModel这个类实现的

原理就是利用UIGraphics 的 UIImage context 先按照图片尺寸绘制出背景图片,然后把label一个一个的绘制到图片上 最后导出一张绘制好的图片保存到相册中

    /**
     添加水印
     */
    func save(){
        guard let image = imageView.image else{
            return
        }
        weak var weakSelf = self
        guard let wself = weakSelf else{
            return
        }
        ZEHud.sharedInstance.showHud()
        dispatch_async(dispatch_queue_create("addLabel",nil)) {
            UIGraphicsBeginImageContext(image.size)// 开始绘制
            image.drawInRect(CGRect(origin: CGPoint.zero, size: image.size))
            for label in wself.labelArr { // 添加多个水印
                let rect = wself.imageView.convertRect(label.frame, fromView: nil)
                let reScale = 1/wself.imageView.scale
                let labelRect = CGRectMake((rect.origin.x)*reScale, (rect.origin.y*reScale), rect.size.width*reScale, rect.height*reScale)
                label.model.text.drawInRect(labelRect, withAttributes:label.model.getAttributes(1/wself.imageView.scale))
            }
            let imageA = UIGraphicsGetImageFromCurrentImageContext()// 获取图片
            UIGraphicsEndImageContext()// 结束绘制
            UIImageWriteToSavedPhotosAlbum(imageA, self, nil, nil)// 保存
            dispatch_async(dispatch_get_main_queue(), {
                ZEHud.sharedInstance.hideHud()
                ShowString.sharedManager().showStringView("保存成功")
                wself.imageView.image = imageA
                wself.assets.removeAtIndex(wself.index)
                wself.changeImage(wself.index)
                if weakSelf!.assets.count == 0 {
                    weakSelf?.performSelector(#selector(weakSelf?.nodataPop), withObject: nil, afterDelay: 0.75)
                    }
            })
        }
    }

项目中水印是由WaterMark这个类来实现的,因为偷懒所以直接继承自UILabel(获取尺寸的时候回方便一些),然后在Label下面添加了一个TextField,来进行文字编辑.

    // 通过传入的bool进行label的文字变换
    func changeEidtType(type:Bool){
        if type == true {
            self.textField.font = self.font
            self.textField.text = self.text
            self.textField.becomeFirstResponder()
        }else{
            textField.endEditing(true)
            let dic = model.getAttributes(1)
            let att = NSAttributedString(string: self.textField.text!, attributes: dic)
            self.attributedText = att
        }
        textField.hidden = !type
        viewChange()
    }

每次编辑水印的步骤我设计成 长按Label -> 弹出EditView -> 每一次操作都做相应的处理(缓存,改变Label的状态)

label的样式是由NSMutableAttributedString这个类来实现的,这个类可以直接用在绘制里

难点

Label和ImageView的比例计算

我给ImageView的class声明了一个scale属性,调用这个属性就会计算出比例的变量

绘制Label的时候就按照1/scale的比例逆推回原大小进行绘制

class EditImageView: UIImageView {
    var scale:CGFloat {
        get{
            return frame.width / image!.size.width
        }
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    func setNewImage(newImage:UIImage){
        image = newImage
        self.frame.size.width = screenWidth
        frame = CGRectMake(0, 0, screenWidth, newImage.size.height * scale)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

水印的本地缓存

缓存格式:

颜色缓存是一个坑...如果用系统自带的颜色直接缓存的话会有问题,因为width black gary 和其他颜色的属性是不一样的,这三类颜色只有黑色和透明度而其他颜色都是RGB 不能进行统一处理

于是我就自己归档了一个颜色数组(查系统的色值还挺麻烦的..)

let titleColorArr:Array<[String:[String:CGFloat]]> = [
    ["黑色":["red":0,"green":0,"blue":0,"alpha":1]],
    ["灰色":["red":102,"green":102,"blue":102,"alpha":1]],
    ["白色":["red":255,"green":255,"blue":255,"alpha":1]],
    ["透明":["red":255,"green":255,"blue":255,"alpha":0]],
    ...
]

然后声明了两个方法,一个是字典转颜色,一个是颜色转字典

class ColorFile {
    static func colorToDic(color:UIColor) -> [String:CGFloat] {
        let components = CGColorGetComponents(color.CGColor)
        let r = components[0]
        let g = components[1]
        let b = components[2]
        let a = components[3]
        return ["red":r,"green":g,"blue":b,"alpha":a]
    }
    static func dicToColor(dic:[String:CGFloat]) -> UIColor {
        let r = dic["red"]!
        let g = dic["green"]!
        let b = dic["blue"]!
        let a = dic["alpha"]!
        return UIColor(red: r, green: g, blue: b, alpha:a)
    }
}

写入缓存的时机是LabelModel每一个属性改变的时候

    var italic:Bool = false{
        willSet{
            self.italic = newValue
            setUD(self.italic, key:italicUDK)
        }
    }
    var underLine:Bool = false{
        willSet{
            self.underLine = newValue
            setUD(self.underLine, key:underLineUDK)
        }
    }
    
    func setUD(value:AnyObject?,key:String){
        NSUserDefaults.standardUserDefaults().setObject(value, forKey: key)
        NSUserDefaults.standardUserDefaults().synchronize()// 同步数据
    }
    func getUD(key:String) -> AnyObject? {
        return NSUserDefaults.standardUserDefaults().objectForKey(key)
    }

labelModel初始化的时候就会取缓存,如果没取到就会给一个默认值

init(){
        if  let text =  getUD(textUDK) as? String {
            self.text = text
        }else{
            text = "这是第一个水印"
        }
}

当ImageView被拖拽放大时 相对 Label的笔记变化

这个还未解决,在上线前已经禁止掉了ImageView的手势

每次imageView大小变化的时候用Label按照scale逆推回去就会发现比例不对,不能按照原比例绘制,求大神帮忙..

欢迎来到吐槽时间...

LeanCloud-Swift-SDK就是一个坑啊...代码难懂不说居然只支持iOS9.1以上版本...相信这一条就让99%的项目不准备引用了吧...

然后他们居然还在info.plist里面的short boundle version里面写英文!握草我头一次遇见打包时候这个Error类型,为此我还特意记了一条笔记....

推荐

推荐一个AppIcon快速生成插件,可以用Package Manager直接下载或者直接去git->https://github.com/kaphacius/IconMaker

使用方法:打开Assets->右键选中AppIcon->选中Make An App Icon -> 选择图片 -> 成功!

现在审核时各个型号的手机可以共用一套简介图片了(可能不是最近才改的,一直是另外一个小哥负责打包的 @辛勤的另外一个小哥)

结尾语

现在项目刚刚提交了审核,审核通过了再把appstore链接贴上...

我都是想起哪里说哪里...可能有些难懂,如果有不懂的地方可以在评论里和我说我会及时修改文章的

睡觉明早起来再改错别字...

相关文章

网友评论

本文标题:开源项目-WaterMark

本文链接:https://www.haomeiwen.com/subject/ncetettx.html