Commit 8b5f7b67 authored by CZ1004's avatar CZ1004

Merge branch 'develop_0409' into Advertisement

* develop_0409:
  优化相似度判断
  修改pod配置 解决模拟器无法运行问题
  修改代码
  修改链接
  修改追踪权限
  内购界面
  内购界面 修改
  内购 样式修改

# Conflicts:
#	PhoneManager/Class/Session/Pay/ViewController/HomePayViewController.swift
#	PhoneManager/Info.plist
parents 68493861 5712ddc8
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
// //
import UIKit import UIKit
import AppTrackingTransparency
class HomeViewController:BaseViewController { class HomeViewController:BaseViewController {
...@@ -49,7 +49,8 @@ class HomeViewController:BaseViewController { ...@@ -49,7 +49,8 @@ class HomeViewController:BaseViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// 调用下追踪权限
checkTrackingAuthorization()
let dataUpdated = Notification.Name("DataUpdatedNotification") let dataUpdated = Notification.Name("DataUpdatedNotification")
...@@ -391,3 +392,58 @@ class HomeViewController:BaseViewController { ...@@ -391,3 +392,58 @@ class HomeViewController:BaseViewController {
} }
} }
extension HomeViewController {
// 检查跟踪授权状态
func checkTrackingAuthorization() {
if #available(iOS 14, *) {
let status = ATTrackingManager.trackingAuthorizationStatus
switch status {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
case .restricted:
// 由于系统限制,无法跟踪用户
print("由于系统限制,无法跟踪用户")
case .notDetermined:
// 用户尚未对跟踪请求做出决定
print("用户尚未对跟踪请求做出决定,再次请求授权")
requestTrackingAuthorization()
@unknown default:
break
}
} else {
// iOS 14 以下系统不支持 ATT 框架
// 可以执行其他操作
}
}
func requestTrackingAuthorization() {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
switch status {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
case .restricted:
// 由于系统限制,无法跟踪用户
print("由于系统限制,无法跟踪用户")
case .notDetermined:
// 用户尚未对跟踪请求做出决定
print("用户尚未对跟踪请求做出决定")
@unknown default:
break
}
})
} else {
// iOS 14 以下系统不支持 ATT 框架
// 可以执行其他操作
}
}
}
...@@ -159,6 +159,7 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource { ...@@ -159,6 +159,7 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource {
sview.setTitle(content, for: .normal) sview.setTitle(content, for: .normal)
sview.setTitleColor(color, for: .normal) sview.setTitleColor(color, for: .normal)
sview.titleLabel?.font = font sview.titleLabel?.font = font
sview.isSelected = false;
sview.sizeToFit() sview.sizeToFit()
let attributes: [NSAttributedString.Key: Any] = [ let attributes: [NSAttributedString.Key: Any] = [
...@@ -168,7 +169,7 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource { ...@@ -168,7 +169,7 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource {
] ]
let attributedString = NSAttributedString(string: content, attributes: attributes) let attributedString = NSAttributedString(string: content, attributes: attributes)
sview.setAttributedTitle(attributedString, for: .normal) sview.setAttributedTitle(attributedString, for: .normal)
sview.addTarget(self, action: #selector(terms), for: .touchUpInside) sview.addTarget(self, action: #selector(terms(_:)), for: .touchUpInside)
sview.sizeToFit() sview.sizeToFit()
contentScroll.addSubview(sview) contentScroll.addSubview(sview)
return sview return sview
...@@ -199,6 +200,41 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource { ...@@ -199,6 +200,41 @@ class HomeNoAdsViewController: UIViewController, NoAdsStackDataSource {
return sview return sview
}() }()
private lazy var privavye_Label: UILabel = {
let priva = UILabel()
priva.font = UIFont.systemFont(ofSize: 14)
priva.textColor = .gray
priva.text = ""
priva.numberOfLines = 0
priva.clipsToBounds = true
priva.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
priva.addGestureRecognizer(tap)
contentScroll.addSubview(priva)
return priva
}()
@objc private func labelTapped(_ gesture: UITapGestureRecognizer) {
let location = gesture.location(in: privavye_Label)
let strs:NSString = "privacy policy"
let frssss = strs.boundingRect(with: CGSizeMake(.infinity, .infinity), options: .truncatesLastVisibleLine, attributes: [NSAttributedString.Key.font : privavye_Label.font], context: nil)
let frame1 = CGRectMake(0, privavye_Label.height-25, CGRectGetWidth(frssss), 25)
let frame2 = CGRectMake(CGRectGetWidth(frssss)+20, privavye_Label.height-25, 90, 25)
if frame1.contains(location) {
Print("点击了隐私")
let vc:PrivacyPolicyWebViewController = PrivacyPolicyWebViewController()
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}else if frame2.contains(location){
Print("点击了terms")
let vc:TermOfUseWebViewController = TermOfUseWebViewController()
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
}
private let data:[[String:String]] = [ private let data:[[String:String]] = [
["icon":"ic_unsel_com","t":"Intelligent cleaning of similar photos"], ["icon":"ic_unsel_com","t":"Intelligent cleaning of similar photos"],
["icon":"ic_unsel_com","t":"Unlimited usage times"], ["icon":"ic_unsel_com","t":"Unlimited usage times"],
...@@ -265,12 +301,32 @@ extension HomeNoAdsViewController : UIScrollViewDelegate { ...@@ -265,12 +301,32 @@ extension HomeNoAdsViewController : UIScrollViewDelegate {
HomePayModel.share.restore() HomePayModel.share.restore()
} }
@objc func terms() -> Void { @objc func terms(_ sender:UIButton) -> Void {
DispatchQueue.main.async {[weak self] in DispatchQueue.main.async {[weak self] in
guard let self else {return} guard let self else {return}
let vc:TermOfUseWebViewController = TermOfUseWebViewController() sender.isSelected = !sender.isSelected
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true) let strs:NSString = sender.isSelected ? "We offer 1-year and lifetime subscriptions. The price is clearly displayed within the application.\n\n·After the purchase confirmation, your iTunes account will be charged.\n\n·Unless the automatic update is turned off at least 24 hours before the end of the current cycle, the subscription will be automatically updated.\n\n·Your account will be renewed within 24 hours before the end of the current cycle cost.\n\n·You can go to the \"Account Settings\" in the iTunes store to manage your subscriptions and turn off auto renewal.\n\n·If provided, if you choose to use our free trial version, any unused portion during the free trial period will become invalid when you purchase a publication subscription, if applicable.\n\n·If you choose not to purchase the AI PhoneManager Pro version, you can continue to use it for free and enjoy PhoneManager.\n\nYour personal data is securely stored in PhoneManager, please make sure to read our \nprivacy policy and terms of use." : ""
let attribtit = NSMutableAttributedString(string:strs as String , attributes: [:])
if strs.length > 3 {
let rang = strs.range(of: "privacy policy")
attribtit.addAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14),NSAttributedString.Key.underlineStyle:NSUnderlineStyle.single.rawValue,.link:"appscheme://private"], range: rang)
let rang1 = strs.range(of: "terms of use")
attribtit.addAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14),NSAttributedString.Key.underlineStyle:NSUnderlineStyle.single.rawValue,.link:"appscheme://terms"], range: rang1)
}
privavye_Label.attributedText = attribtit
self.view.layoutIfNeeded()
DispatchQueue.main.async {
let height = CGRectGetMaxY(self.privavye_Label.frame)
self.contentScroll.contentSize = CGSize(width: 0, height: height + 30)
if self.privavye_Label.attributedText?.length ?? 0 > 10 {
UIView.animate(withDuration: 0.1) {
self.contentScroll.contentOffset = CGPoint(x: 0, y: self.contentScroll.contentSize.height - self.contentScroll.height)
}
}
}
} }
} }
...@@ -343,8 +399,8 @@ extension HomeNoAdsViewController : UIScrollViewDelegate { ...@@ -343,8 +399,8 @@ extension HomeNoAdsViewController : UIScrollViewDelegate {
private func setUI() -> Void { private func setUI() -> Void {
topBackimg.snp.makeConstraints { make in topBackimg.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.top.equalToSuperview()
make.top.equalToSuperview().offset(-20) make.height.equalTo(topBackimg.snp.width).multipliedBy(182.0/375.0)
} }
closeBtn.snp.makeConstraints { make in closeBtn.snp.makeConstraints { make in
...@@ -401,7 +457,7 @@ extension HomeNoAdsViewController : UIScrollViewDelegate { ...@@ -401,7 +457,7 @@ extension HomeNoAdsViewController : UIScrollViewDelegate {
buybut.snp.makeConstraints { make in buybut.snp.makeConstraints { make in
make.left.right.equalTo(stack) make.left.right.equalTo(stack)
make.top.equalTo(forever.snp.bottom).offset(20) make.top.equalTo(forever.snp.bottom).offset(20)
make.height.equalTo(46) make.height.equalTo(65)
} }
ppBtn.snp.makeConstraints { make in ppBtn.snp.makeConstraints { make in
make.top.equalTo(buybut.snp.bottom).offset(10) make.top.equalTo(buybut.snp.bottom).offset(10)
...@@ -411,12 +467,18 @@ extension HomeNoAdsViewController : UIScrollViewDelegate { ...@@ -411,12 +467,18 @@ extension HomeNoAdsViewController : UIScrollViewDelegate {
make.centerY.equalTo(ppBtn) make.centerY.equalTo(ppBtn)
make.right.equalTo(buybut.snp.right) make.right.equalTo(buybut.snp.right)
} }
privavye_Label.snp.makeConstraints { make in
make.left.equalTo(ppBtn.snp.left)
make.right.equalTo(restoreBtn.snp.right)
make.top.equalTo(ppBtn.snp.bottom).offset(15)
}
self.type = 0 self.type = 0
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
DispatchQueue.main.asyncAfter(deadline: .now()+0.1) { DispatchQueue.main.asyncAfter(deadline: .now()+0.1) {
self.buybut.layer.cornerRadius = CGRectGetHeight(self.buybut.frame) / 2.0 self.buybut.layer.cornerRadius = CGRectGetHeight(self.buybut.frame) / 2.0
self.scroll.contentSize = CGSize(width: CGRectGetWidth(self.scroll.frame) * 3.0, height: 0) self.scroll.contentSize = CGSize(width: CGRectGetWidth(self.scroll.frame) * 3.0, height: 0)
let height = CGRectGetMaxY(self.restoreBtn.frame) let height = CGRectGetMaxY(self.privavye_Label.frame)
self.contentScroll.contentSize = CGSize(width: 0, height: height + 30) self.contentScroll.contentSize = CGSize(width: 0, height: height + 30)
} }
} }
......
...@@ -24,9 +24,12 @@ class HomePayViewController:UIViewController { ...@@ -24,9 +24,12 @@ class HomePayViewController:UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = .green view.backgroundColor = .white
addViews() addViews()
storeKeD() storeKeD()
scroll.snp.makeConstraints { make in
make.left.right.top.bottom.equalToSuperview()
}
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
...@@ -38,17 +41,26 @@ class HomePayViewController:UIViewController { ...@@ -38,17 +41,26 @@ class HomePayViewController:UIViewController {
super.viewDidAppear(animated) super.viewDidAppear(animated)
homePayView?.playAnimationWithDelay() homePayView?.playAnimationWithDelay()
} }
override func viewDidDisappear(_ animated: Bool) { override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated) super.viewDidDisappear(animated)
doneBlock() doneBlock()
} }
lazy var scroll: UIScrollView = {
let scroll = UIScrollView()
scroll.backgroundColor = .clear
scroll.contentInsetAdjustmentBehavior = .never
view.addSubview(scroll)
return scroll
}()
private func addViews() { private func addViews() {
homePayView = HomePayView(frame: view.bounds) homePayView = HomePayView(frame: view.bounds)
view.addSubview(homePayView!) scroll.addSubview(homePayView!)
homePayView?.snp.makeConstraints { make in
make.left.top.equalToSuperview()
make.width.equalTo(scroll.snp.width)
}
homePayView?.callBack = {[weak self] status in homePayView?.callBack = {[weak self] status in
guard let self else {return} guard let self else {return}
if let operstatus = status as? OperStatus { if let operstatus = status as? OperStatus {
...@@ -71,11 +83,21 @@ class HomePayViewController:UIViewController { ...@@ -71,11 +83,21 @@ class HomePayViewController:UIViewController {
self.payTouch() self.payTouch()
break break
case .swit: case .swit:
break break
case .restore: case .restore:
self.restoreClick() self.restoreClick()
break break
case .change:
DispatchQueue.main.async {
let contentSize = CGRectGetHeight(self.homePayView?.frame ?? CGRect())
self.scroll.contentSize = CGSizeMake(0, contentSize + (cWindow?.safeAreaInsets.bottom ?? 20) + 5)
if self.homePayView?.privavye_Label.attributedText?.length ?? 0 > 10 {
UIView.animate(withDuration: 0.1) {
self.scroll.contentOffset = CGPoint(x: 0, y: self.scroll.contentSize.height - self.scroll.height)
}
}
}
break
} }
} }
} }
......
...@@ -15,7 +15,7 @@ class PrivacyPolicyWebViewController:BaseWebViewController { ...@@ -15,7 +15,7 @@ class PrivacyPolicyWebViewController:BaseWebViewController {
titleView.model.title = "Privacy Policy" titleView.model.title = "Privacy Policy"
LoadWithUrl(url: "https://sites.google.com/view/test-ios-use/home") LoadWithUrl(url: "https://app.intelliadvert.com/privacy")
view.backgroundColor = .white view.backgroundColor = .white
webView.width = ScreenW - 20.RW() webView.width = ScreenW - 20.RW()
......
...@@ -15,7 +15,7 @@ class TermOfUseWebViewController:BaseWebViewController { ...@@ -15,7 +15,7 @@ class TermOfUseWebViewController:BaseWebViewController {
titleView.model.title = "Term of Use" titleView.model.title = "Term of Use"
LoadWithUrl(url: "https://sites.google.com/view/testiosterms/home") LoadWithUrl(url: "https://app.intelliadvert.com/terms")
view.backgroundColor = .white view.backgroundColor = .white
webView.width = ScreenW - 20.RW() webView.width = ScreenW - 20.RW()
......
...@@ -55,6 +55,9 @@ class PhotoAndVideoMananger { ...@@ -55,6 +55,9 @@ class PhotoAndVideoMananger {
var ids:[String] = [] var ids:[String] = []
// 定义
private let hashDistance = 100
func setAssets() { func setAssets() {
let fetchOptions = PHFetchOptions() let fetchOptions = PHFetchOptions()
...@@ -1009,39 +1012,54 @@ class PhotoAndVideoMananger { ...@@ -1009,39 +1012,54 @@ class PhotoAndVideoMananger {
// 计算两个哈希值的汉明距离 // 计算两个哈希值的汉明距离
func hammingDistance(_ hash1: String, _ hash2: String) -> Int { func hammingDistance(_ hash1: String, _ hash2: String) -> Int {
var distance = 0 // var distance = 0
for (char1, char2) in zip(hash1, hash2) { // for (char1, char2) in zip(hash1, hash2) {
let int1 = Int(String(char1), radix: 16)! // let int1 = Int(String(char1), radix: 16)!
let int2 = Int(String(char2), radix: 16)! // let int2 = Int(String(char2), radix: 16)!
let xor = int1 ^ int2 // let xor = int1 ^ int2
distance += String(xor, radix: 2).filter { $0 == "1" }.count // distance += String(xor, radix: 2).filter { $0 == "1" }.count
} // }
return distance // return distance
guard hash1.count == hash2.count else { return Int.max }
return zip(hash1, hash2).filter { $0 != $1 }.count
} }
func groupSimilarImages(assets: [PHAsset], progressCompletion: @escaping ([[AssetModel]]) -> Void, completion: @escaping ([[AssetModel]]) -> Void) { func groupSimilarImages(assets: [PHAsset], progressCompletion: @escaping ([[AssetModel]]) -> Void, completion: @escaping ([[AssetModel]]) -> Void) {
print("开始处理相似图片")
DispatchQueue.global().async { DispatchQueue.global().async {
print("进入异步任务处理相似图片")
var assetModels: [AssetModel] = [] var assetModels: [AssetModel] = []
var hashes: [String: AssetModel] = [:] var hashes: [String: AssetModel] = [:]
var groupedImages: [[AssetModel]] = [] var groupedImages: [[AssetModel]] = []
let dispatchGroup = DispatchGroup()
for asset in assets { for asset in assets {
_ = asset.pixelWidth * asset.pixelHeight // 创建 AssetModel
let createDate = asset.creationDate ?? Date() let createDate = asset.creationDate ?? Date()
let model = AssetModel(localIdentifier: asset.localIdentifier, assetSize: self.findAssetSize(asset: asset), createDate: createDate) let model = AssetModel(localIdentifier: asset.localIdentifier, assetSize: self.findAssetSize(asset: asset), createDate: createDate)
assetModels.append(model) assetModels.append(model)
let manager = PHImageManager.default() // 请求图像
manager.requestImage(for: asset, targetSize: CGSize(width: 32, height: 32), contentMode: .aspectFit, options: nil) { (image, _) in dispatchGroup.enter()
PHImageManager.default().requestImage(for: asset, targetSize: CGSize(width: 32, height: 32), contentMode: .aspectFit, options: nil) { (image, _) in
if let image = image { if let image = image {
let hash = self.pHash(for: image) let hash = self.calculateImageHashUsingCoreImage(image)
if hash != "" { if !hash.isEmpty {
hashes[hash] = model hashes[hash] = model
} }
} }
dispatchGroup.leave()
} }
} }
// 等待所有请求完成后进行比较
dispatchGroup.notify(queue: .global()) {
print("获取到全部hash值")
var usedHashes: Set<String> = [] var usedHashes: Set<String> = []
for (hash1, model1) in hashes { for (hash1, model1) in hashes {
if usedHashes.contains(hash1) { continue } if usedHashes.contains(hash1) { continue }
...@@ -1051,7 +1069,7 @@ class PhotoAndVideoMananger { ...@@ -1051,7 +1069,7 @@ class PhotoAndVideoMananger {
for (hash2, model2) in hashes { for (hash2, model2) in hashes {
if usedHashes.contains(hash2) { continue } if usedHashes.contains(hash2) { continue }
let distance = self.hammingDistance(hash1, hash2) let distance = self.hammingDistance(hash1, hash2)
if distance < 4 { // 汉明距离小于 5 认为相似 if distance < self.hashDistance { // 可以根据需求调整阈值
similarModels.append(model2) similarModels.append(model2)
usedHashes.insert(hash2) usedHashes.insert(hash2)
} }
...@@ -1060,6 +1078,7 @@ class PhotoAndVideoMananger { ...@@ -1060,6 +1078,7 @@ class PhotoAndVideoMananger {
if similarModels.count >= 2 { if similarModels.count >= 2 {
groupedImages.append(similarModels) groupedImages.append(similarModels)
// 每次找到新的相似组,通过 progressCompletion 回调返回当前已处理好的分组数据 // 每次找到新的相似组,通过 progressCompletion 回调返回当前已处理好的分组数据
print("判断相似", similarModels)
DispatchQueue.main.async { DispatchQueue.main.async {
progressCompletion(groupedImages) progressCompletion(groupedImages)
} }
...@@ -1071,6 +1090,7 @@ class PhotoAndVideoMananger { ...@@ -1071,6 +1090,7 @@ class PhotoAndVideoMananger {
} }
} }
} }
}
} }
...@@ -1148,3 +1168,69 @@ extension Array { ...@@ -1148,3 +1168,69 @@ extension Array {
} }
} }
} }
extension PhotoAndVideoMananger{
// 计算图片的感知哈希值
func calculateImageHashUsingCoreImage(_ image: UIImage) -> String {
guard let cgImage = image.cgImage else { return "" }
// 生成CIImage
let ciImage = CIImage(cgImage: cgImage)
// 创建滤镜:灰度化图像
let filter = CIFilter(name: "CIPhotoEffectNoir")!
filter.setValue(ciImage, forKey: kCIInputImageKey)
guard let outputImage = filter.outputImage else {
return ""
}
// 将输出图像缩放到32x32
let context = CIContext()
let targetSize = CGSize(width: 32, height: 32)
let scaledImage = context.createCGImage(outputImage, from: outputImage.extent)!
let resizedImage = UIImage(cgImage: scaledImage, scale: 1.0, orientation: image.imageOrientation)
// 获取像素数据
guard let pixelData = resizedImage.cgImage?.dataProvider?.data,
let data = CFDataGetBytePtr(pixelData) else {
return ""
}
// 计算灰度像素平均值
var pixels: [UInt8] = Array(repeating: 0, count: 1024) // 32 * 32
for i in 0..<32 {
for j in 0..<32 {
let pixelIndex = (i * 32 + j) * 4 // RGBA
let r = data[pixelIndex]
let g = data[pixelIndex + 1]
let b = data[pixelIndex + 2]
// 使用灰度公式转化
let gray = UInt8(0.299 * Double(r) + 0.587 * Double(g) + 0.114 * Double(b))
pixels[i * 32 + j] = gray
}
}
// 计算平均值
let sum = pixels.reduce(0) { UInt32($0) + UInt32($1) } // 使用 UInt32 来避免溢出
let average = UInt8(sum / UInt32(pixels.count)) // 确保将 sum 转换为 UInt32
// 生成哈希值
var hash = ""
for pixel in pixels {
hash += pixel > average ? "1" : "0"
}
return hash
}
// 计算汉明距离
func calculateHammingDistance(_ hash1: String, _ hash2: String) -> Int {
guard hash1.count == hash2.count else { return Int.max }
return zip(hash1, hash2).filter { $0 != $1 }.count
}
}
...@@ -25,6 +25,7 @@ enum CommonPush { ...@@ -25,6 +25,7 @@ enum CommonPush {
case swit case swit
case pay case pay
case restore case restore
case change
} }
enum tabbarType { enum tabbarType {
......
...@@ -56,3 +56,47 @@ extension UILabel { ...@@ -56,3 +56,47 @@ extension UILabel {
self.width = width self.width = width
} }
} }
extension UILabel {
func isTapLocationInTextRange(_ gesture: UITapGestureRecognizer, range: NSRange) -> Bool {
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
let textStorage = NSTextStorage(attributedString: self.attributedText ?? NSAttributedString())
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = self.lineBreakMode
textContainer.maximumNumberOfLines = self.numberOfLines
textContainer.size = self.bounds.size
let location = gesture.location(in: self)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let alignmentOffset = self.alignmentOffsetForText(in: textBoundingBox)
let touchLocation = CGPoint(
x: (location.x - alignmentOffset),
y: (location.y - textBoundingBox.minY)
)
let index = layoutManager.characterIndex(
for: touchLocation,
in: textContainer,
fractionOfDistanceBetweenInsertionPoints: nil
)
return NSLocationInRange(index, range)
}
private func alignmentOffsetForText(in rect: CGRect) -> CGFloat {
switch self.textAlignment {
case .left, .natural, .justified:
return 0
case .center:
return (self.bounds.width - rect.width) / 2
case .right:
return self.bounds.width - rect.width
@unknown default:
return 0
}
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSUserTrackingUsageDescription</key> <key>NSUserTrackingUsageDescription</key>
<string>此应用会使用广告标识符来提供个性化广告。</string> <string>We need your permission to track your usage habits in order to provide a more personalized advertising experience</string>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
......
...@@ -14,5 +14,13 @@ target 'PhoneManager' do ...@@ -14,5 +14,13 @@ target 'PhoneManager' do
pod 'SVProgressHUD' pod 'SVProgressHUD'
pod 'Google-Mobile-Ads-SDK' pod 'Google-Mobile-Ads-SDK'
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64' # Exclude arm64 for simulator
end
end
end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment