开源项目-WaterMark

屏幕快照 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链接贴上...

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,609评论 4 59
  • ​01 鸡汤文当道,随便一抓都是一大把。无论朋友圈还是公众号,到处充斥着努力、奋斗、坚持、成功、真爱的字眼充斥着你...
    进击的历史君阅读 521评论 0 1
  • 上个星期六,我休息,睡到中午起来,老爸打电话叫我上去吃饭。遂起床刷牙洗脸,顺便把家里的垃圾拿到三楼丢掉,我家在二楼...
    她爱她阅读 171评论 0 0
  • 姓名:刘强 公司:宁波大发化纤有限公司 六项精进第277期利他四组学员 【日精进打卡第33天】,共计33天。 【知...
    三分厂刘强阅读 160评论 0 0