開源項(xiàng)目-WaterMark

屏幕快照 2016-08-27 下午10.19.21.png

前言

這里引用應(yīng)用描述里的一句話:

妻子做微商很辛苦所以想做個(gè)軟件來緩解她的壓力,于是誕生了這個(gè)軟件.在她的鼓勵(lì)下把這個(gè)軟件上線到Appstroe上,希望能幫助到你們

本項(xiàng)目代碼全部開源,只刪除了LeanCloud相關(guān)的id和key,如果我以后不小心把key和id push上去了各位小伙伴一定要提醒我哈...

這個(gè)項(xiàng)目是業(yè)余時(shí)間瞎敲出來的...很多代碼都是自己一邊嘗試一邊敲出來的...所以大家可以挑重點(diǎn)看

我準(zhǔn)備把WaterMark 和以后其他的開源項(xiàng)目寫成一個(gè)系列的博客,每次更新版本都會(huì)把遇到的難點(diǎn)和坑點(diǎn)總結(jié)出來..喜歡的話大家可以收藏,關(guān)注支持一下

希望本篇博客能幫助菜鳥了解iOS項(xiàng)目開發(fā)中的常識(shí)

歡迎大家一起討論正確的開發(fā)姿勢(shì)

跪求大神帶我飛!

(在下英文渣,請(qǐng)各位老爺觀看時(shí)利用腦內(nèi)runtime 把不對(duì)的單詞替換成對(duì)的單詞...哈哈哈,我會(huì)好好背單詞的!)

回歸正題

項(xiàng)目地址:https://github.com/Lafree317/WaterLabel

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

架構(gòu)

項(xiàng)目文件結(jié)構(gòu)

項(xiàng)目中現(xiàn)在集成的第三方開源庫(kù)有:

  • Pod
  • LeanCloud Swift-alpha版(坑): 用于反饋數(shù)據(jù)的云存儲(chǔ)
  • MBProgressHUD 1.0 :提示控件 每個(gè)項(xiàng)目必備
  • RxSwift和RxCocoa: 現(xiàn)在代碼中還沒有用到,到時(shí)候?qū)懙顷懙臅r(shí)候準(zhǔn)備這個(gè)寫響應(yīng)式
  • SnapKit: Swift版的Messary 炒雞好用
  • Vendors
  • ShowString 我司小哥改裝的一個(gè)提示框,優(yōu)點(diǎn)在于提示的時(shí)候還可以對(duì)頁(yè)面進(jìn)行UI交互
  • TZImagePickerController 一個(gè)1000+star圖片選擇庫(kù),這個(gè)開發(fā)者很熱心發(fā)issues很快就會(huì)回復(fù)你并解決問題
  • IGLDropDownMenu 一個(gè)下拉抽屜式動(dòng)畫,使用起來非常方便
  • ZEViewKit 我自己封裝的一些小控件,準(zhǔn)備再贊一些一起上傳到Pod中
  • Scenes里有一個(gè)ZEVC是準(zhǔn)備以后所有開源項(xiàng)目中公用的反饋和登陸界面

項(xiàng)目架構(gòu)采用最基本的MVC

因?yàn)椴皇枪卷?xiàng)目喜歡自己瞎搞一些東西所以一些被我改畸形了
這里詳細(xì)說一下:

我發(fā)現(xiàn)Swfit的Extension太過強(qiáng)大于是將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(){
    }

首先強(qiáng)調(diào)一點(diǎn)...這是我自己胡亂嘗試敲出來的...沒有看過類似代碼..所以不推薦在正常項(xiàng)目中使用,只是講一下自己的思路希望能幫助到你

這樣做的好處我覺得有幾點(diǎn)

  • 代碼都為C的代碼,不存在跨類調(diào)用方法和取值(高內(nèi)聚?)
  • 上下兩個(gè)控制器傳值調(diào)用的時(shí)候也更方便一些(低耦合?)
  • C代碼很少,只把主要的方法C中,如果想擴(kuò)展相應(yīng)功能或修改bug可以直接找到位置去進(jìn)行操作

我覺得不好的地方

  • 代碼較為混亂,不適合多人開發(fā)?
  • 具體功能重用性差,不利于其他項(xiàng)目使用

我一直自己摸索學(xué)習(xí)Swift,還沒有找到它正確的開發(fā)姿勢(shì),希望大家能教我正確的開發(fā)姿勢(shì)

項(xiàng)目中還有一小部分面向協(xié)議

如給UIView添加一個(gè)滑動(dòng)手勢(shì),這里應(yīng)該如果優(yōu)化一下應(yīng)該 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)
    }
}

一些細(xì)節(jié)

水印的繪制是由EditModel這個(gè)類實(shí)現(xiàn)的

原理就是利用UIGraphics 的 UIImage context 先按照?qǐng)D片尺寸繪制出背景圖片,然后把label一個(gè)一個(gè)的繪制到圖片上 最后導(dǎo)出一張繪制好的圖片保存到相冊(cè)中

    /**
     添加水印
     */
    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 { // 添加多個(gè)水印
                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()// 結(jié)束繪制
            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)
                    }
            })
        }
    }

項(xiàng)目中水印是由WaterMark這個(gè)類來實(shí)現(xiàn)的,因?yàn)橥祽兴灾苯永^承自UILabel(獲取尺寸的時(shí)候回方便一些),然后在Label下面添加了一個(gè)TextField,來進(jìn)行文字編輯.

    // 通過傳入的bool進(jìn)行l(wèi)abel的文字變換
    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()
    }

每次編輯水印的步驟我設(shè)計(jì)成 長(zhǎng)按Label -> 彈出EditView -> 每一次操作都做相應(yīng)的處理(緩存,改變Label的狀態(tài))

label的樣式是由NSMutableAttributedString這個(gè)類來實(shí)現(xiàn)的,這個(gè)類可以直接用在繪制里

難點(diǎn)

Label和ImageView的比例計(jì)算

我給ImageView的class聲明了一個(gè)scale屬性,調(diào)用這個(gè)屬性就會(huì)計(jì)算出比例的變量

繪制Label的時(shí)候就按照1/scale的比例逆推回原大小進(jìn)行繪制

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")
    }
}

水印的本地緩存

緩存格式:

顏色緩存是一個(gè)坑...如果用系統(tǒng)自帶的顏色直接緩存的話會(huì)有問題,因?yàn)閣idth black gary 和其他顏色的屬性是不一樣的,這三類顏色只有黑色和透明度而其他顏色都是RGB 不能進(jìn)行統(tǒng)一處理

于是我就自己歸檔了一個(gè)顏色數(shù)組(查系統(tǒng)的色值還挺麻煩的..)

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]],
    ...
]

然后聲明了兩個(gè)方法,一個(gè)是字典轉(zhuǎn)顏色,一個(gè)是顏色轉(zhuǎn)字典

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)
    }
}

寫入緩存的時(shí)機(jī)是LabelModel每一個(gè)屬性改變的時(shí)候

    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()// 同步數(shù)據(jù)
    }
    func getUD(key:String) -> AnyObject? {
        return NSUserDefaults.standardUserDefaults().objectForKey(key)
    }

labelModel初始化的時(shí)候就會(huì)取緩存,如果沒取到就會(huì)給一個(gè)默認(rèn)值

init(){
        if  let text =  getUD(textUDK) as? String {
            self.text = text
        }else{
            text = "這是第一個(gè)水印"
        }
}

當(dāng)ImageView被拖拽放大時(shí) 相對(duì) Label的筆記變化

這個(gè)還未解決,在上線前已經(jīng)禁止掉了ImageView的手勢(shì)

每次imageView大小變化的時(shí)候用Label按照scale逆推回去就會(huì)發(fā)現(xiàn)比例不對(duì),不能按照原比例繪制,求大神幫忙..

歡迎來到吐槽時(shí)間...

LeanCloud-Swift-SDK就是一個(gè)坑啊...代碼難懂不說居然只支持iOS9.1以上版本...相信這一條就讓99%的項(xiàng)目不準(zhǔn)備引用了吧...

然后他們居然還在info.plist里面的short boundle version里面寫英文!握草我頭一次遇見打包時(shí)候這個(gè)Error類型,為此我還特意記了一條筆記....

推薦

推薦一個(gè)AppIcon快速生成插件,可以用Package Manager直接下載或者直接去git->https://github.com/kaphacius/IconMaker

使用方法:打開Assets->右鍵選中AppIcon->選中Make An App Icon -> 選擇圖片 -> 成功!

現(xiàn)在審核時(shí)各個(gè)型號(hào)的手機(jī)可以共用一套簡(jiǎn)介圖片了(可能不是最近才改的,一直是另外一個(gè)小哥負(fù)責(zé)打包的 @辛勤的另外一個(gè)小哥)

結(jié)尾語

現(xiàn)在項(xiàng)目剛剛提交了審核,審核通過了再把a(bǔ)ppstore鏈接貼上...

我都是想起哪里說哪里...可能有些難懂,如果有不懂的地方可以在評(píng)論里和我說我會(huì)及時(shí)修改文章的

睡覺明早起來再改錯(cuò)別字...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,151評(píng)論 4 61
  • ?01 雞湯文當(dāng)?shù)溃S便一抓都是一大把。無論朋友圈還是公眾號(hào),到處充斥著努力、奮斗、堅(jiān)持、成功、真愛的字眼充斥著你...
    進(jìn)擊的歷史君閱讀 609評(píng)論 0 1
  • 上個(gè)星期六,我休息,睡到中午起來,老爸打電話叫我上去吃飯。遂起床刷牙洗臉,順便把家里的垃圾拿到三樓丟掉,我家在二樓...
    她愛她閱讀 199評(píng)論 0 0
  • 姓名:劉強(qiáng) 公司:寧波大發(fā)化纖有限公司 六項(xiàng)精進(jìn)第277期利他四組學(xué)員 【日精進(jìn)打卡第33天】,共計(jì)33天。 【知...
    三分廠劉強(qiáng)閱讀 170評(píng)論 0 0