preface
Here’s a quote from the app description:
My wife was doing wechat business very hard, so SHE wanted to make a software to relieve her pressure, so this software was born. With her encouragement, I put this software online on Appstroe, hoping to help you
The code of this project is all open source, and only the id and key related to LeanCloud are deleted. If I accidentally push the key and ID up in the future, please remind me.
This project was hammered out in my spare time… A lot of code is typed out as you try it out… So you can pick and choose
I plan to write a series of blog posts about WaterMark and other open source projects in the future, with each update providing a summary of the difficulties and pitfalls… If you like, you can collect it and support it
Hopefully this blog will help newbies understand the common sense of iOS project development
Welcome to discuss the correct development posture
Kneel and beg god to fly me!
(In the English residue, please watch the use of your brain runtime to replace the wrong words with pairs of words… Ha ha ha, I will recite the words well!
Project address :github.com/Lafree317/W…
AppStroe:itunes.apple.com/WebObjects/…
Return to the chase
Current Functions:
- Add watermark to full size/thumbnail
- Watermark cache
- Watermark editor
architecture
Project file structure
The third party open source libraries currently integrated in the project are:
- Pod
- LeanCloud Swift-Alpha (Pit): Cloud storage for feedback data
- MBProgressHUD 1.0: Prompt control is required for each project
- RxSwift and RxCocoa: it is not used in the code yet, so prepare the write response when writing the login
- SnapKit: Swift version of Messary fried chicken easy to use
- Vendors
- ShowString my little brother modified a prompt box, the advantage is that the prompt can also be UI interaction on the page
- TZImagePickerController is a 1000+star image selection library, this developer is very interested in issuing issues and will reply to you soon
- IGLDropDownMenu a drawer-down animation, very convenient to use
- ZEViewKit I package some small controls, ready to like some together uploaded to Pod
- There is a ZEVC in the Scenes that is intended to be a common feedback and login interface for all future open source projects
The project architecture uses the most basic MVC
Because they are not corporate projects and like to mess around with things, some of them have been deformed by me. Here are the details:
I found Swfit’s Extension was too powerful, so I replaced the Model layer and View layer with Extension. For example, I changed the code of feedBackController to:
-
Controller
class ZEFeedBackContoller: UIViewController.UITextFieldDelegate.UIScrollViewDelegate { // Various attributes override func viewDidLoad(a) { super.viewDidLoad() setUI() }Copy the code
-
Model
extension ZEFeedBackContoller { / / model method func sendFeedBack(a){}}Copy the code
-
View
extension ZEFeedBackContoller { / / add the UI func setUI(a){}Copy the code
First of all… I tried to knock it out myself… Never seen code like this.. So it’s not recommended to use it in normal projects, but I just want to talk about my ideas and hopefully help you
I think there are several advantages to this
- C code with no cross-class method calls and values (high cohesion?)
- It is also easier for the upper and lower controllers to pass value calls (low coupling?)
- C code is very small, only the main method in C, if you want to expand the corresponding function or modify the bug can directly find the location to operate
I feel bad about it
- Is the code messy and not suitable for multiplayer development?
- Poor reuse of specific functions is not conducive to the use of other projects
I have been learning Swift by myself and haven’t found the correct development posture. I hope you can teach me the correct development posture
There is also a small protocol-oriented part of the project
Like adding a swipe gesture to UIView, this should be extended to a concrete class if optimized, instead of directly adding a..
protocol ViewGestureRecognizer{}extension UIView:ViewGestureRecognizer{
func addPan(a){
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)}}Copy the code
Some of the details
The watermarking is done by the EditModel class
The principle is to use the UIImage context of UIGraphics to first draw the background picture according to the size of the picture, then draw the labels on the picture one by one and finally export a drawn picture and save it in the album
/** Add watermark */
func save(a){
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)// Start drawing
image.drawInRect(CGRect(origin: CGPoint.zero, size: image.size))
for label in wself.labelArr { // Add multiple watermarks
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(a)// Get the image
UIGraphicsEndImageContext(a)// Finish drawing
UIImageWriteToSavedPhotosAlbum(imageA, self.nil.nil)/ / save
dispatch_async(dispatch_get_main_queue(), {
ZEHud.sharedInstance.hideHud()
ShowString.sharedManager().showStringView("Saved successfully")
wself.imageView.image = imageA
wself.assets.removeAtIndex(wself.index)
wself.changeImage(wself.index)
ifweakSelf! .assets.count= =0{ weakSelf? .performSelector(#selector(weakSelf? .nodataPop), withObject:nil, afterDelay: 0.75)}})}}Copy the code
WaterMark is implemented in the project by the WaterMark class, which inherits from UILabel(easy to get the size), and then adds a TextField under the Label to edit the text.
// Label text is converted by the bool passed in
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() }Copy the code
I designed the step of editing the watermark into long press Label -> pop up EditView -> each operation to do the corresponding processing (cache, change the state of Label)
The style of the label is implemented by NSMutableAttributedString this class, this class can be directly used in the drawing
The difficulties in
Calculate the ratio of Label to ImageView
I declare a scale property for the ImageView class. Calling this property will calculate the scale variable
When the Label is drawn, it is drawn back to its original size in a scale of 1/scale
class EditImageView: UIImageView {
var scale:CGFloat {
get{
returnframe.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")}}Copy the code
A local cache of watermarks
Cache format:
Color cache is a pit… There will be problems if the system color is used to cache directly, because the attributes of Width Black Gary and other colors are different. These three colors only have black and transparency, while other colors are RGB and cannot be processed uniformly
So I archived an array of colors myself (it was a hassle to check the system colors).
let titleColorArr:Array"[String: [String:CGFloat[[]] > ="Black": ["red":0."green":0."blue":0."alpha":1]],
["Gray": ["red":102."green":102."blue":102."alpha":1]],
["White": ["red":255."green":255."blue":255."alpha":1]],
["Transparent": ["red":255."green":255."blue":255."alpha":0]],... ]Copy the code
Then we declare two methods, one is dictionary to color, one is color to dictionary
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)
}
}Copy the code
The time to write to the cache is when each property of the LabelModel changes
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()// Synchronize data
}
func getUD(key:String) -> AnyObject? {
return NSUserDefaults.standardUserDefaults().objectForKey(key)
}Copy the code
When the labelModel is initialized, it will fetch the cache, and if it doesn’t, it will give a default value
init() {if let text = getUD(textUDK) as? String {
self.text = text
}else{
text = "This is the first watermark."}}Copy the code
Note changes relative to the Label when the ImageView is dragged and enlarged
This is not fixed yet, the ImageView gesture has been disabled before it goes live
Every time the imageView size changes, use the Label to push back according to the scale. It will find that the scale is not correct, and can not draw according to the original scale.
pit
Welcome to joke time…
Leancloud-swift-sdk is a pit… Support only iOS9.1 and above… Believe this and 99% of projects are not going to reference it…
Then they even write English in the short boundle version of info.plist! I first encountered this Error type when packing, for which I also specially wrote down a note….
recommended
A quick plugin for AppIcon is recommended, which can be downloaded directly using Package Manager or directly go to git->github.com/kaphacius/I…
Use method: Open Assets-> right-click AppIcon-> Make An AppIcon-> Select pictures -> Success!
It is now possible for all models of mobile phones to share one set of profile pictures (maybe not just changed recently, it is always another guy who is responsible for packing @ another guy of Hard work)
The closing
Now the project has just been submitted for review, and the appstore link is pasted after the review is approved…
I’m talking about everything… It may be difficult to understand. If you don’t understand anything, please tell me in the comments and I will revise the article in time
Go to bed and wake up tomorrow morning to correct the typos…