Files
midi_ios/TUIKit/TUIRoomKit/Source/View/Page/Widget/WaterMark/WaterMarkLayer.swift
2025-08-14 10:07:49 +08:00

178 lines
7.8 KiB
Swift

//
// WaterMarkLayer.swift
// TUIRoomKit
//
// Created by janejntang on 2024/4/3.
//
import Foundation
class WaterMarkLayer: CALayer {
var text: String = ""
var textColor: UIColor = UIColor(0x99A2B2).withAlphaComponent(0.3)
var lineStyle: WaterMarkLineStyle = .multiLine
private var portraitImage: UIImage?
private var landscapeImage: UIImage?
private let multiLineTextFontSize = 14.0
private let sigleLineTextFontSize = 36.0
private let singleLineWaterMarkWidth = 303.scale375()
private let multiLineWaterMarkWidth = 118.scale375()
private let offset = 39.scale375()
private var numberOfOneRow: Int {
isLandscape ? 4 : 3
}
private var numberOfOneColumn: Int {
isLandscape ? 3 : 4
}
private var textMinOffset: CGFloat {
return lineStyle == .multiLine ? 2 : 4
}
override init() {
super.init()
}
override init(layer: Any) {
super.init(layer: layer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
let rect:CGRect = .init(origin: .zero, size: CGSize(width: ctx.width, height: ctx.height))
ctx.translateBy(x: rect.origin.x, y: rect.origin.y)
ctx.translateBy(x: 0, y: rect.size.height)
ctx.scaleBy(x: 1.0, y: -1.0)
ctx.translateBy(x: -rect.origin.x, y: -rect.origin.y)
let waterMarkFullSize = CGSize(width: CGFloat(ctx.width) + offset * 2, height: CGFloat(ctx.height) + offset * 2)
guard let image = getWaterMarkImage(isLandScape: isLandscape, andFullSize: waterMarkFullSize)?.cgImage
else { return }
ctx.draw(image, in: CGRect(origin: CGPoint(x: -offset, y: -offset), size: waterMarkFullSize))
}
private func getWaterMarkImage(isLandScape: Bool, andFullSize fsize: CGSize) -> UIImage? {
var image: UIImage?
if isLandscape {
image = landscapeImage != nil ? landscapeImage : createWatermarkImage(Text: text, andFullSize: fsize)
landscapeImage = image
} else {
image = portraitImage != nil ? portraitImage : createWatermarkImage(Text: text, andFullSize: fsize)
portraitImage = image
}
return image
}
private func createWatermarkImage(Text strTxt:String, andFullSize fsize:CGSize) -> UIImage {
let attributedString = getTextAttributeString(text: text)
let _size = getTextWaterMarkSize()
if UIScreen.main.scale > 1.5 {
UIGraphicsBeginImageContextWithOptions(_size,false,0)
}
else{
UIGraphicsBeginImageContext(_size)
}
//Picture tilt
var context = UIGraphicsGetCurrentContext()
context?.concatenate(.init(translationX: _size.width * 0.8, y: _size.height * 0.4))
context?.concatenate(.init(rotationAngle: -0.25 * .pi))
context?.concatenate(.init(translationX: -_size.width * 0.8, y: -_size.height * 0.4))
let point = getTextWaterMarkPoint(attributedString: attributedString, size: _size)
attributedString.draw(in: .init(origin: point, size: attributedString.size()))
let _waterImg = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage.init()
if UIScreen.main.scale > 1.5 {
UIGraphicsBeginImageContextWithOptions(fsize,false,0)
}
else{
UIGraphicsBeginImageContext(fsize)
}
context = UIGraphicsGetCurrentContext()
if lineStyle == .singleLine {
let _rect:CGRect = .init(origin: .init(x: (fsize.width - _size.width) / 2.0,
y: (fsize.height - _size.height) / 2.0),
size: _waterImg.size)
_waterImg.draw(in: _rect)
} else {
var _tempC = fsize.width / _waterImg.size.width
var _maxColumn:Int = _tempC.isNaN || !_tempC.isFinite ? 1 : Int(_tempC)
if fsize.width.truncatingRemainder(dividingBy: _waterImg.size.width) != 0 {
_maxColumn += 1
}
_tempC = fsize.height / _waterImg.size.height
var _maxRows:Int = _tempC.isNaN || !_tempC.isFinite ? 1 : Int(_tempC)
if fsize.height.truncatingRemainder(dividingBy: _waterImg.size.height) != 0 {
_maxRows += 1
}
let spaceX = (fsize.width - multiLineWaterMarkWidth * CGFloat(numberOfOneRow)) / CGFloat(numberOfOneRow - 1)
let spaceY = (fsize.height - multiLineWaterMarkWidth * CGFloat(numberOfOneColumn)) / CGFloat(numberOfOneColumn - 1)
for r in 0..<_maxRows {
for c in 0..<_maxColumn {
let _rect:CGRect = .init(origin: .init(x: CGFloat(c) * CGFloat(_waterImg.size.width + spaceX),
y: CGFloat(r) * CGFloat(_waterImg.size.height + spaceY)),
size: _waterImg.size)
_waterImg.draw(in: _rect)
}
}
}
context?.clip()
context?.setFillColor(UIColor.clear.cgColor)
context?.fill(.init(origin: .zero, size: fsize))
let _canvasImg = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage.init()
UIGraphicsEndImageContext()
return _canvasImg
}
private func getTextAttributeString(text: String) -> NSMutableAttributedString {
let textFont: CGFloat = lineStyle == .multiLine ? multiLineTextFontSize : sigleLineTextFontSize
let paragraphStyle:NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.lineSpacing = 5
paragraphStyle.alignment = .center
var textAttributes:[NSAttributedString.Key:Any] = [
.font : UIFont.systemFont(ofSize: textFont, weight: .regular),
.foregroundColor:textColor,
.paragraphStyle: paragraphStyle,
.kern:1.0,
]
if #available(iOS 14.0, *) {
textAttributes[.tracking] = 1.0
}
let attributedString:NSMutableAttributedString = NSMutableAttributedString.init(string: text)
let stringRange = NSMakeRange(0, attributedString.string.utf16.count)
attributedString.addAttributes(textAttributes,range: stringRange)
let viewSize = getTextWaterMarkSize()
let maxLength = getViewHypotenuseLength(viewSize: viewSize) - textMinOffset * 2
if attributedString.size().width > maxLength, let range = text.range(of: "(") {
var wartMarkText = text
let location = wartMarkText.distance(from: wartMarkText.startIndex, to: range.lowerBound)
let index = wartMarkText.index(wartMarkText.startIndex, offsetBy: location)
wartMarkText.insert(contentsOf: "\n", at: index)
attributedString.replaceCharacters(in: stringRange, with: wartMarkText)
}
return attributedString
}
private func getTextWaterMarkSize() -> CGSize {
switch lineStyle {
case .singleLine:
return CGSize(width: singleLineWaterMarkWidth, height: singleLineWaterMarkWidth)
case .multiLine:
return CGSize(width: multiLineWaterMarkWidth, height: multiLineWaterMarkWidth)
}
}
private func getTextWaterMarkPoint(attributedString: NSMutableAttributedString, size: CGSize) -> CGPoint {
let viewHypotenuseLength = getViewHypotenuseLength(viewSize: size)
var value = (viewHypotenuseLength - attributedString.size().width) / 2.0
value = max(value, textMinOffset)
return CGPoint(x: value, y: value)
}
private func getViewHypotenuseLength(viewSize: CGSize) -> CGFloat {
let square = viewSize.width * viewSize.width + viewSize.height + viewSize.height
return sqrt(square)
}
}