keep edit

This commit is contained in:
edwinQQQ
2025-10-17 14:52:29 +08:00
parent 22185d799e
commit 517365879a
622 changed files with 40518 additions and 7298 deletions

View File

@@ -34,7 +34,6 @@
18E7B31826F097E00064BC9B /* UserInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 18E7B31726F097E00064BC9B /* UserInfoModel.m */; };
23116B0B2BDB8FDC00F7F97A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 2368ECD72BC38FED00EDF4C9 /* PrivacyInfo.xcprivacy */; };
232EBBFF2BD7A25500E8CEAD /* MSParamsDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 232EBBFE2BD7A25500E8CEAD /* MSParamsDecode.m */; };
2331C1BD2A60F69E00E1D940 /* UILabel+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2331C1BC2A60F69E00E1D940 /* UILabel+Utils.m */; };
235A45232B04BEB6009753F5 /* PIBaseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 235A45222B04BEB6009753F5 /* PIBaseModel.m */; };
2368ECCF2BC38F9800EDF4C9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2368ECCD2BC38F9800EDF4C9 /* InfoPlist.strings */; };
2368ECD32BC38FDA00EDF4C9 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2368ECD52BC38FDA00EDF4C9 /* Launch Screen.storyboard */; };
@@ -45,7 +44,6 @@
2377010E2BCF73F400D661F1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2377010D2BCF73F400D661F1 /* CoreGraphics.framework */; };
237701102BCF740400D661F1 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 2377010F2BCF740400D661F1 /* libsqlite3.tbd */; };
237701122BCF742C00D661F1 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 237701112BCF742C00D661F1 /* libz.tbd */; };
23959FE72BB15C930085A282 /* UploadFileModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 23959FE62BB15C930085A282 /* UploadFileModel.m */; };
23CEFC4A2AFB8FC100576D89 /* BSNSStringUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 23CEFC0C2AFB8FC100576D89 /* BSNSStringUtil.m */; };
23CEFC4B2AFB8FC100576D89 /* BSUIDemoUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 23CEFC0D2AFB8FC100576D89 /* BSUIDemoUtil.m */; };
23CEFC4C2AFB8FC100576D89 /* BS_UIColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 23CEFC0F2AFB8FC100576D89 /* BS_UIColor.m */; };
@@ -138,11 +136,9 @@
4CF464322EA1277C005E96C0 /* TTAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF464222EA1277C005E96C0 /* TTAlertView.m */; };
73FFADDC93E195344047A2EC /* Pods_YuMi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACF623970097D653132D69A /* Pods_YuMi.framework */; };
9B0E1C5926E77022005D4442 /* BaseNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E1C5826E77022005D4442 /* BaseNavigationController.m */; };
9B33E3CB27D85379003B0E62 /* UploadFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B33E3C927D85379003B0E62 /* UploadFile.m */; };
9BA8A47527C60D9F000365A3 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BA8A47427C60D9F000365A3 /* AudioToolbox.framework */; };
9BA8A47727C60DF7000365A3 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9BA8A47627C60DF7000365A3 /* AVFoundation.framework */; };
E8098CA7282E00920090B9F0 /* Api+Moments.m in Sources */ = {isa = PBXBuildFile; fileRef = E8098CA6282E00920090B9F0 /* Api+Moments.m */; };
E80CBDEA27D0C53F001E1EC2 /* XPWeakTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = E80CBDE927D0C53F001E1EC2 /* XPWeakTimer.m */; };
E80E09A92A40B70100CD2BE7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E80E09AB2A40B70100CD2BE7 /* Localizable.strings */; };
E80E2377299A47F60013FD40 /* AESUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E80E2376299A47F60013FD40 /* AESUtils.m */; };
E81366E726F0A49E0076364C /* NSString+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E81366E626F0A49E0076364C /* NSString+Utils.m */; };
@@ -237,8 +233,6 @@
18E7B31726F097E00064BC9B /* UserInfoModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserInfoModel.m; sourceTree = "<group>"; };
232EBBFD2BD7A25500E8CEAD /* MSParamsDecode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSParamsDecode.h; sourceTree = "<group>"; };
232EBBFE2BD7A25500E8CEAD /* MSParamsDecode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSParamsDecode.m; sourceTree = "<group>"; };
2331C1BB2A60F69E00E1D940 /* UILabel+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UILabel+Utils.h"; sourceTree = "<group>"; };
2331C1BC2A60F69E00E1D940 /* UILabel+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UILabel+Utils.m"; sourceTree = "<group>"; };
235A45212B04BEB6009753F5 /* PIBaseModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PIBaseModel.h; sourceTree = "<group>"; };
235A45222B04BEB6009753F5 /* PIBaseModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PIBaseModel.m; sourceTree = "<group>"; };
2368ECCA2BC38F6F00EDF4C9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -254,8 +248,6 @@
2377010D2BCF73F400D661F1 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
2377010F2BCF740400D661F1 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
237701112BCF742C00D661F1 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
23959FE52BB15C930085A282 /* UploadFileModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UploadFileModel.h; sourceTree = "<group>"; };
23959FE62BB15C930085A282 /* UploadFileModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UploadFileModel.m; sourceTree = "<group>"; };
23CEFC0B2AFB8FC100576D89 /* BSXWDateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSXWDateUtil.h; sourceTree = "<group>"; };
23CEFC0C2AFB8FC100576D89 /* BSNSStringUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSNSStringUtil.m; sourceTree = "<group>"; };
23CEFC0D2AFB8FC100576D89 /* BSUIDemoUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSUIDemoUtil.m; sourceTree = "<group>"; };
@@ -417,16 +409,12 @@
7DB00EC07F1D0ADFF900B38D /* Pods-YuMi.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.debug.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.debug.xcconfig"; sourceTree = "<group>"; };
9B0E1C5726E77022005D4442 /* BaseNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseNavigationController.h; sourceTree = "<group>"; };
9B0E1C5826E77022005D4442 /* BaseNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseNavigationController.m; sourceTree = "<group>"; };
9B33E3C927D85379003B0E62 /* UploadFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UploadFile.m; sourceTree = "<group>"; };
9B33E3CA27D85379003B0E62 /* UploadFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UploadFile.h; sourceTree = "<group>"; };
9BA8A47427C60D9F000365A3 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
9BA8A47627C60DF7000365A3 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
B66633E061B1B34177CD011C /* Pods-YuMi.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.release.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.release.xcconfig"; sourceTree = "<group>"; };
CACF623970097D653132D69A /* Pods_YuMi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YuMi.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E8098CA5282E00920090B9F0 /* Api+Moments.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Api+Moments.h"; sourceTree = "<group>"; };
E8098CA6282E00920090B9F0 /* Api+Moments.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+Moments.m"; sourceTree = "<group>"; };
E80CBDE827D0C53F001E1EC2 /* XPWeakTimer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPWeakTimer.h; sourceTree = "<group>"; };
E80CBDE927D0C53F001E1EC2 /* XPWeakTimer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPWeakTimer.m; sourceTree = "<group>"; };
E80E2375299A47F60013FD40 /* AESUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AESUtils.h; sourceTree = "<group>"; };
E80E2376299A47F60013FD40 /* AESUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AESUtils.m; sourceTree = "<group>"; };
E81366E526F0A49E0076364C /* NSString+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+Utils.h"; sourceTree = "<group>"; };
@@ -683,12 +671,9 @@
23CEFC082AFB8FC100576D89 /* sdkContent */,
E83645A42A40AEF600E0DBE4 /* Bundle */,
E8F61356291E269700E12650 /* Safe */,
9B33E3C827D85379003B0E62 /* File */,
E80CBDE727D0C528001E1EC2 /* Timer */,
180116F6279E8C3100F2CBC0 /* Date */,
E88863D0278ED4A0004BCFAB /* Timestamp */,
E8EEB8EA26FC2050007C6EBA /* SDPhotoBrowser */,
2331C1BA2A60F67F00E1D940 /* UILabel */,
E81366E426F0A4820076364C /* NSString */,
E82109AA26F1C86E00FC3319 /* CountDown */,
E81C278726EAFABF0031E639 /* Security */,
@@ -748,15 +733,6 @@
path = YMTabbar;
sourceTree = "<group>";
};
2331C1BA2A60F67F00E1D940 /* UILabel */ = {
isa = PBXGroup;
children = (
2331C1BB2A60F69E00E1D940 /* UILabel+Utils.h */,
2331C1BC2A60F69E00E1D940 /* UILabel+Utils.m */,
);
path = UILabel;
sourceTree = "<group>";
};
236B2E1B2AA0786E003967A8 /* Library */ = {
isa = PBXGroup;
children = (
@@ -971,14 +947,14 @@
path = Views;
sourceTree = "<group>";
};
4C0642752E97BD6D00BAF413 /* NewMine */ = {
4C0642752E97BD6D00BAF413 /* Mine */ = {
isa = PBXGroup;
children = (
4C1E98BE2E9A3A540031AE79 /* Services */,
4C0642712E97BD6D00BAF413 /* Controllers */,
4C0642742E97BD6D00BAF413 /* Views */,
);
path = NewMine;
path = Mine;
sourceTree = "<group>";
};
4C0642782E97BD6D00BAF413 /* Controllers */ = {
@@ -1011,32 +987,32 @@
path = Views;
sourceTree = "<group>";
};
4C06427C2E97BD6D00BAF413 /* NewMoments */ = {
4C06427C2E97BD6D00BAF413 /* Moments */ = {
isa = PBXGroup;
children = (
4C0642952E98F76F00BAF413 /* Services */,
4C0642782E97BD6D00BAF413 /* Controllers */,
4C06427B2E97BD6D00BAF413 /* Views */,
);
path = NewMoments;
path = Moments;
sourceTree = "<group>";
};
4C06427E2E97BD6D00BAF413 /* NewTabBar */ = {
4C06427E2E97BD6D00BAF413 /* TabBar */ = {
isa = PBXGroup;
children = (
4C06428A2E98DC5F00BAF413 /* EPTabBarController.swift */,
);
path = NewTabBar;
path = TabBar;
sourceTree = "<group>";
};
4C0642922E98EF0A00BAF413 /* E-P */ = {
isa = PBXGroup;
children = (
4CD19C852E9CB31C0069DAA0 /* NewLogin */,
4CD19C852E9CB31C0069DAA0 /* Login */,
4C1E98C22E9A45160031AE79 /* Common */,
4C0642752E97BD6D00BAF413 /* NewMine */,
4C06427C2E97BD6D00BAF413 /* NewMoments */,
4C06427E2E97BD6D00BAF413 /* NewTabBar */,
4C0642752E97BD6D00BAF413 /* Mine */,
4C06427C2E97BD6D00BAF413 /* Moments */,
4C06427E2E97BD6D00BAF413 /* TabBar */,
);
path = "E-P";
sourceTree = "<group>";
@@ -1109,7 +1085,7 @@
path = Views;
sourceTree = "<group>";
};
4CD19C852E9CB31C0069DAA0 /* NewLogin */ = {
4CD19C852E9CB31C0069DAA0 /* Login */ = {
isa = PBXGroup;
children = (
4CD19C8D2E9CBBC80069DAA0 /* Services */,
@@ -1117,7 +1093,7 @@
4CD19C812E9CB31C0069DAA0 /* Models */,
4CD19C842E9CB31C0069DAA0 /* Views */,
);
path = NewLogin;
path = Login;
sourceTree = "<group>";
};
4CD19C8D2E9CBBC80069DAA0 /* Services */ = {
@@ -1328,17 +1304,6 @@
path = TTPopup;
sourceTree = "<group>";
};
9B33E3C827D85379003B0E62 /* File */ = {
isa = PBXGroup;
children = (
9B33E3CA27D85379003B0E62 /* UploadFile.h */,
9B33E3C927D85379003B0E62 /* UploadFile.m */,
23959FE52BB15C930085A282 /* UploadFileModel.h */,
23959FE62BB15C930085A282 /* UploadFileModel.m */,
);
path = File;
sourceTree = "<group>";
};
9B9DFDA227DB4F68000F95B3 /* Recovered References */ = {
isa = PBXGroup;
children = (
@@ -1373,15 +1338,6 @@
path = Pods;
sourceTree = "<group>";
};
E80CBDE727D0C528001E1EC2 /* Timer */ = {
isa = PBXGroup;
children = (
E80CBDE827D0C53F001E1EC2 /* XPWeakTimer.h */,
E80CBDE927D0C53F001E1EC2 /* XPWeakTimer.m */,
);
path = Timer;
sourceTree = "<group>";
};
E81366E426F0A4820076364C /* NSString */ = {
isa = PBXGroup;
children = (
@@ -1743,7 +1699,6 @@
18E7B31826F097E00064BC9B /* UserInfoModel.m in Sources */,
4C0642912E98DC8700BAF413 /* EPMomentViewController.m in Sources */,
232EBBFF2BD7A25500E8CEAD /* MSParamsDecode.m in Sources */,
23959FE72BB15C930085A282 /* UploadFileModel.m in Sources */,
189DD73F26E21C3F00AB55B1 /* YYUtility+Carrier.m in Sources */,
23CEFC5D2AFB8FC100576D89 /* BSRecordModel.m in Sources */,
9B0E1C5926E77022005D4442 /* BaseNavigationController.m in Sources */,
@@ -1758,7 +1713,6 @@
23CEFC622AFB8FC100576D89 /* BSLogNetDetailViewController.m in Sources */,
4C7B909B2E9F822900A5E236 /* EPEmotionColorWheelView.m in Sources */,
4C7B8F632E9F6E1300A5E236 /* EPSignatureColorGuideView.m in Sources */,
9B33E3CB27D85379003B0E62 /* UploadFile.m in Sources */,
4CD19EAE2E9CDFC30069DAA0 /* EPLoginButton.swift in Sources */,
4CD19EAF2E9CDFC30069DAA0 /* EPLoginInputView.swift in Sources */,
4C0642852E97BD9500BAF413 /* APIConfig.swift in Sources */,
@@ -1788,7 +1742,6 @@
187EEEDC26E89B32002833B2 /* BaseModel.m in Sources */,
E80E2377299A47F60013FD40 /* AESUtils.m in Sources */,
23CEFC642AFB8FC100576D89 /* BSkObject.m in Sources */,
2331C1BD2A60F69E00E1D940 /* UILabel+Utils.m in Sources */,
E83645A82A40AF5400E0DBE4 /* NSBundle+Localizable.m in Sources */,
4C06429C2E99120600BAF413 /* EPMomentPublishViewController.m in Sources */,
23CEFC632AFB8FC100576D89 /* BSRealTextView.m in Sources */,
@@ -1816,7 +1769,6 @@
4CD19C8E2E9CBBC80069DAA0 /* EPLoginService.swift in Sources */,
4CD19C8F2E9CBBC80069DAA0 /* EPLoginManager.swift in Sources */,
4C1E98BF2E9A3A540031AE79 /* EPMineAPIHelper.m in Sources */,
E80CBDEA27D0C53F001E1EC2 /* XPWeakTimer.m in Sources */,
189DD55026DE37F900AB55B1 /* MvpViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -1,9 +1,7 @@
//
// AppDelegate.h
// YUMI
//
// Created by admin on 2023/3/9.
//
#import <UIKit/UIKit.h>

View File

@@ -0,0 +1,15 @@
//
// AppDelegate.h
// YUMI
//
// Created by admin on 2023/3/9.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -1,9 +1,6 @@
//
// AppDelegate.m
// YUMI
//
// Created by admin on 2023/3/9.
//
#import "AppDelegate.h"
@@ -40,9 +37,9 @@ UIKIT_EXTERN NSString * const kOpenRoomNotification;
// MARK: - Helper Methods
/// keyWindowiOS 13+
- (UIWindow *)getKeyWindow {
// iOS 13+ 使 connectedScenes window
if (@available(iOS 13.0, *)) {
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
@@ -51,13 +48,13 @@ UIKIT_EXTERN NSString * const kOpenRoomNotification;
return window;
}
}
// keyWindow window
return scene.windows.firstObject;
}
}
}
// iOS 13 使
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [UIApplication sharedApplication].keyWindow;
@@ -74,44 +71,44 @@ UIKIT_EXTERN NSString * const kOpenRoomNotification;
}else{
[self toHomeTabbarPage];
// window
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self checkAndShowSignatureColorGuide];
});
}
}
///
- (void)checkAndShowSignatureColorGuide {
UIWindow *keyWindow = [self getKeyWindow];
if (!keyWindow) return;
BOOL hasSignatureColor = [EPEmotionColorStorage hasUserSignatureColor];
#if DEBUG
// Debug
NSLog(@"[AppDelegate] Debug 模式:显示专属颜色引导页(已有颜色: %@", hasSignatureColor ? @"YES" : @"NO");
//#if DEBUG
//
// NSLog(@"[AppDelegate] Debug 模式:显示专属颜色引导页(已有颜色: %@", hasSignatureColor ? @"YES" : @"NO");
//
// EPSignatureColorGuideView *guideView = [[EPSignatureColorGuideView alloc] init];
//
//
// guideView.onColorConfirmed = ^(NSString *hexColor) {
// [EPEmotionColorStorage saveUserSignatureColor:hexColor];
// NSLog(@"[AppDelegate] 用户选择专属颜色: %@", hexColor);
// };
//
//
// if (hasSignatureColor) {
// guideView.onSkipTapped = ^{
// NSLog(@"[AppDelegate] 用户跳过专属颜色选择");
// };
// }
//
//
// [guideView showInWindow:keyWindow showSkipButton:hasSignatureColor];
//
//#else
EPSignatureColorGuideView *guideView = [[EPSignatureColorGuideView alloc] init];
//
guideView.onColorConfirmed = ^(NSString *hexColor) {
[EPEmotionColorStorage saveUserSignatureColor:hexColor];
NSLog(@"[AppDelegate] 用户选择专属颜色: %@", hexColor);
};
// Skip
if (hasSignatureColor) {
guideView.onSkipTapped = ^{
NSLog(@"[AppDelegate] 用户跳过专属颜色选择");
};
}
// Skip
[guideView showInWindow:keyWindow showSkipButton:hasSignatureColor];
#else
// Release
if (!hasSignatureColor) {
EPSignatureColorGuideView *guideView = [[EPSignatureColorGuideView alloc] init];
guideView.onColorConfirmed = ^(NSString *hexColor) {
@@ -120,11 +117,11 @@ UIKIT_EXTERN NSString * const kOpenRoomNotification;
};
[guideView showInWindow:keyWindow];
}
#endif
//#endif
}
- (void)toLoginPage {
// 使 Swift
EPLoginViewController *lvc = [[EPLoginViewController alloc] init];
BaseNavigationController *navigationController =
[[BaseNavigationController alloc] initWithRootViewController:lvc];
@@ -159,10 +156,10 @@ UIKIT_EXTERN NSString * const kOpenRoomNotification;
case ATTrackingManagerAuthorizationStatusAuthorized:
break;
case ATTrackingManagerAuthorizationStatusNotDetermined: {
// NSLog(@"用户未做选择或未弹窗IDFA");
//1app
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// NSLog(@"app追踪IDFA权限%lu",(unsigned long)status);
}];
}
break;

View File

@@ -0,0 +1,176 @@
//
// AppDelegate.m
// YUMI
//
// Created by admin on 2023/3/9.
//
#import "AppDelegate.h"
#import "BaseNavigationController.h"
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import "YuMi-swift.h"
UIKIT_EXTERN NSString * const kOpenRoomNotification;
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil];
UIViewController *launchScreenVC = [launchStoryboard instantiateInitialViewController];
self.window.rootViewController = launchScreenVC;
[self.window makeKeyAndVisible];
[self loadMainPage];
if (@available(iOS 15, *)) {
[[UITableView appearance] setSectionHeaderTopPadding:0];
}
return YES;
}
// MARK: - Helper Methods
/// 获取 keyWindowiOS 13+ 兼容)
- (UIWindow *)getKeyWindow {
// iOS 13+ 使用 connectedScenes 获取 window
if (@available(iOS 13.0, *)) {
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
for (UIWindow *window in scene.windows) {
if (window.isKeyWindow) {
return window;
}
}
// 如果没有 keyWindow返回第一个 window
return scene.windows.firstObject;
}
}
}
// iOS 13 以下,使用旧方法(已废弃但仍然可用)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [UIApplication sharedApplication].keyWindow;
#pragma clang diagnostic pop
}
- (void)loadMainPage {
AccountModel *accountModel = [[AccountInfoStorage instance] getCurrentAccountInfo];
if (accountModel == nil ||
accountModel.uid == nil ||
accountModel.access_token == nil) {
[self toLoginPage];
}else{
[self toHomeTabbarPage];
// 延迟检查专属颜色(等待 window 初始化完成)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self checkAndShowSignatureColorGuide];
});
}
}
/// 检查并显示专属颜色引导页
- (void)checkAndShowSignatureColorGuide {
UIWindow *keyWindow = [self getKeyWindow];
if (!keyWindow) return;
BOOL hasSignatureColor = [EPEmotionColorStorage hasUserSignatureColor];
#if DEBUG
// Debug 环境:总是显示引导页
NSLog(@"[AppDelegate] Debug 模式:显示专属颜色引导页(已有颜色: %@", hasSignatureColor ? @"YES" : @"NO");
EPSignatureColorGuideView *guideView = [[EPSignatureColorGuideView alloc] init];
// 设置颜色确认回调
guideView.onColorConfirmed = ^(NSString *hexColor) {
[EPEmotionColorStorage saveUserSignatureColor:hexColor];
NSLog(@"[AppDelegate] 用户选择专属颜色: %@", hexColor);
};
// 如果已有颜色,设置 Skip 回调
if (hasSignatureColor) {
guideView.onSkipTapped = ^{
NSLog(@"[AppDelegate] 用户跳过专属颜色选择");
};
}
// 显示引导页,已有颜色时显示 Skip 按钮
[guideView showInWindow:keyWindow showSkipButton:hasSignatureColor];
#else
// Release 环境:仅在未设置专属颜色时显示
if (!hasSignatureColor) {
EPSignatureColorGuideView *guideView = [[EPSignatureColorGuideView alloc] init];
guideView.onColorConfirmed = ^(NSString *hexColor) {
[EPEmotionColorStorage saveUserSignatureColor:hexColor];
NSLog(@"[AppDelegate] 用户选择专属颜色: %@", hexColor);
};
[guideView showInWindow:keyWindow];
}
#endif
}
- (void)toLoginPage {
// 使用新的 Swift 登录页面
EPLoginViewController *lvc = [[EPLoginViewController alloc] init];
BaseNavigationController *navigationController =
[[BaseNavigationController alloc] initWithRootViewController:lvc];
navigationController.modalPresentationStyle = UIModalPresentationFullScreen;
self.window.rootViewController = navigationController;
}
- (void)toHomeTabbarPage {
EPTabBarController *epTabBar = [EPTabBarController create];
[epTabBar refreshTabBarWithIsLogin:YES];
UIWindow *window = [self getKeyWindow];
if (window) {
window.rootViewController = epTabBar;
[window makeKeyAndVisible];
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self getAdvertisingTrackingAuthority];
[[NSNotificationCenter defaultCenter]postNotificationName:@"kAppDidBecomeActive" object:nil];
}
- (void)getAdvertisingTrackingAuthority {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (@available(iOS 14, *)) {
ATTrackingManagerAuthorizationStatus status = ATTrackingManager.trackingAuthorizationStatus;
switch (status) {
case ATTrackingManagerAuthorizationStatusDenied:
break;
case ATTrackingManagerAuthorizationStatusAuthorized:
break;
case ATTrackingManagerAuthorizationStatusNotDetermined: {
// NSLog(@"用户未做选择或未弹窗IDFA");
//请求弹出用户授权框只会在程序运行是弹框1次除非卸载app重装通地图、相机等权限弹框一样
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// NSLog(@"app追踪IDFA权限%lu",(unsigned long)status);
}];
}
break;
default:
break;
}
}
});
}
@end

View File

@@ -1,44 +1,39 @@
//
// APIConfig.swift
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
import Foundation
/// API
/// 使 XOR + Base64
@objc class APIConfig: NSObject {
// MARK: - Private Properties
/// XOR
private static let xorKey: UInt8 = 77
/// RELEASE
/// https://api.epartylive.com
private static let releaseEncodedParts: [String] = [
"JTk5PT53YmI=", // https:// (XOR Base64)
"LD0kYw==", // api. (XOR Base64)
"KD0sPzk0ISQ7KGMuIiA=", // epartylive.com (XOR Base64)
"JTk5PT53YmI=",
"LD0kYw==",
"KD0sPzk0ISQ7KGMuIiA=",
]
// MARK: - Public Methods
/// API
/// - Returns:
@objc static func baseURL() -> String {
#if DEBUG
// DEV 使 Bridging HttpRequestHelper
// TODO: return HttpRequestHelper.getHostUrl()
return getDevBaseURL()
#else
// RELEASE 使
let url = decodeURL(from: releaseEncodedParts)
//
if url.isEmpty || !url.hasPrefix("http") {
NSLog("[APIConfig] 警告:域名解密失败,使用备用域名")
return backupURL()
@@ -48,33 +43,29 @@ import Foundation
#endif
}
/// DEV
/// - Returns: DEV
private static func getDevBaseURL() -> String {
// UserDefaults HttpRequestHelper
#if DEBUG
let isProduction = UserDefaults.standard.string(forKey: "kIsProductionEnvironment")
if isProduction == "YES" {
return "https://api.epartylive.com" //
return "https://api.epartylive.com"
} else {
return "https://test-api.yourdomain.com" //
return "https://test-api.yourdomain.com"
}
#else
return "https://api.epartylive.com"
#endif
}
///
/// - Returns: 使
@objc static func backupURL() -> String {
return getDevBaseURL()
}
// MARK: - Private Methods
///
/// - Parameter parts:
/// - Returns:
private static func decodeURL(from parts: [String]) -> String {
let decoded = parts.compactMap { part -> String? in
guard let data = Data(base64Encoded: part) else {
@@ -99,7 +90,7 @@ import Foundation
#if DEBUG
extension APIConfig {
/// /
@objc static func testEncryption() {
print("=== APIConfig 加密测试 ===")
print("Release 域名: \(decodeURL(from: releaseEncodedParts))")

View File

@@ -0,0 +1,110 @@
//
// APIConfig.swift
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
import Foundation
/// API 域名配置类
/// 使用 XOR + Base64 双重混淆防止反编译
@objc class APIConfig: NSObject {
// MARK: - Private Properties
/// XOR 加密密钥
private static let xorKey: UInt8 = 77
/// RELEASE 环境域名(加密后)
/// 原始域名https://api.epartylive.com
private static let releaseEncodedParts: [String] = [
"JTk5PT53YmI=", // https:// (XOR 后 Base64)
"LD0kYw==", // api. (XOR 后 Base64)
"KD0sPzk0ISQ7KGMuIiA=", // epartylive.com (XOR 后 Base64)
]
// MARK: - Public Methods
/// 获取 API 基础域名
/// - Returns: 根据编译环境返回对应的域名
@objc static func baseURL() -> String {
#if DEBUG
// DEV 环境:临时使用硬编码(等 Bridging 修复后再改回 HttpRequestHelper
// TODO: 修复后改为 return HttpRequestHelper.getHostUrl()
return getDevBaseURL()
#else
// RELEASE 环境:使用动态生成的新域名
let url = decodeURL(from: releaseEncodedParts)
// 验证解密结果
if url.isEmpty || !url.hasPrefix("http") {
NSLog("[APIConfig] 警告:域名解密失败,使用备用域名")
return backupURL()
}
return url
#endif
}
/// 获取 DEV 环境域名(临时方案)
/// - Returns: DEV 域名
private static func getDevBaseURL() -> String {
// 从 UserDefaults 读取(原 HttpRequestHelper 的逻辑)
#if DEBUG
let isProduction = UserDefaults.standard.string(forKey: "kIsProductionEnvironment")
if isProduction == "YES" {
return "https://api.epartylive.com" // 正式环境
} else {
return "https://test-api.yourdomain.com" // 测试环境(请替换为实际测试域名)
}
#else
return "https://api.epartylive.com"
#endif
}
/// 备用域名(降级方案)
/// - Returns: 原域名(仅在解密失败时使用)
@objc static func backupURL() -> String {
return getDevBaseURL()
}
// MARK: - Private Methods
/// 解密域名
/// - Parameter parts: 加密后的域名片段数组
/// - Returns: 解密后的完整域名
private static func decodeURL(from parts: [String]) -> String {
let decoded = parts.compactMap { part -> String? in
guard let data = Data(base64Encoded: part) else {
NSLog("[APIConfig] Base64 解码失败: \(part)")
return nil
}
let xored = data.map { $0 ^ xorKey }
return String(bytes: xored, encoding: .utf8)
}
let result = decoded.joined()
#if DEBUG
NSLog("[APIConfig] 解密后的域名: \(result)")
#endif
return result
}
}
// MARK: - Debug Helper
#if DEBUG
extension APIConfig {
/// 测试方法:验证域名加密/解密是否正常
@objc static func testEncryption() {
print("=== APIConfig 加密测试 ===")
print("Release 域名: \(decodeURL(from: releaseEncodedParts))")
print("当前环境域名: \(baseURL())")
print("备用域名: \(backupURL())")
}
}
#endif

View File

@@ -1,9 +1,7 @@
//
// DJDKMIMOMColor.h
// YUMI
//
// Created by YUMI on 2021/9/9.
//
#import <Foundation/Foundation.h>
@@ -13,62 +11,58 @@
NS_ASSUME_NONNULL_BEGIN
@interface DJDKMIMOMColor : NSObject
/// 主题色0x9682FF
+ (UIColor *)appMainColor;
///强调色 #248CFE
+ (UIColor *)appEmphasizeColor;
///强调色1 0xBF36FF
+ (UIColor *)appEmphasizeColor1;
///强调色2 0xFB486A
+ (UIColor *)appEmphasizeColor2;
/* ------页面相关颜色 START------ */
/// view的背景色 0xF3F5FA
+ (UIColor *)appBackgroundColor;
/// cell的背景色 0xFFFFFF
+ (UIColor *)appCellBackgroundColor;
///正文颜色 0x333333
+ (UIColor *)mainTextColor;
/// 二级文字颜色 0x666666
+ (UIColor *)secondTextColor;
///三级文字的颜色 0x999999
+ (UIColor *)textThirdColor;
///分割线的颜色 0xE8E8E8
+ (UIColor *)dividerColor;
/* ------页面相关颜色 END------ */
/* ------Button 相关颜色 START------ */
/// button 可用 渐变色的开始 0xFFA936
+ (UIColor *)confirmButtonGradientStartColor;
/// button 可用 渐变色的中间 #9CB3FF
+ (UIColor *)confirmButtonGradientMiddleColor;
/// button 可用 渐变色的开始 0xFFCB47
+ (UIColor *)confirmButtonGradientEndColor;
/// 确定的按钮文字颜色 #FFFFFF
+ (UIColor *)confirmButtonTextColor;
/// 取消按钮 渐变色的开始 0xF7DDBF
+ (UIColor *)cancelButtonGradientStartColor;
/// 取消按钮 渐变色的结束 0xF7E8C4
+ (UIColor *)cancelButtonGradientEndColor;
/// 取消的按钮文字颜色 0xFFA936
+ (UIColor *)cancelButtonTextColor;
/// 取消按钮单一普通背景色 0xFFCE4E
+ (UIColor *)cancelButtonNormalBgColor;
/// 按钮不可点击背景色 0xD2D5D7
+ (UIColor *)disableButtonColor;
/// 按钮不可点击文字颜色 0xF9F9F9
+ (UIColor *)disableButtonTextColor;
/* ------Button 相关颜色 END------ */
/* ------弹窗相关颜色 START------ */
+ (UIColor *)confirmButtonGradientMiddleColor;
+ (UIColor *)confirmButtonGradientEndColor;
+ (UIColor *)confirmButtonTextColor;
+ (UIColor *)cancelButtonGradientStartColor;
+ (UIColor *)cancelButtonGradientEndColor;
+ (UIColor *)cancelButtonTextColor;
+ (UIColor *)cancelButtonNormalBgColor;
+ (UIColor *)disableButtonColor;
+ (UIColor *)disableButtonTextColor;
+ (UIColor *)alertBackgroundColor;
+ (UIColor *)alertTitleColor;
+ (UIColor *)alertMessageColor;
+ (UIColor *)actionSeparatorColor;
/* ------弹窗相关颜色 END------ */
///tabbar 没有点击的时候颜色 0x333333, 0.4
+ (UIColor *)tabbarNormalColor;
/// tabbar的View的color 0xFFFFFF
+ (UIColor *)tabbarViewColor;
+ (UIColor *)colorWithHexString:(NSString *)hexString;

View File

@@ -0,0 +1,79 @@
//
// DJDKMIMOMColor.h
// YUMI
//
// Created by YUMI on 2021/9/9.
//
#import <Foundation/Foundation.h>
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define UIColorRGBAlpha(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:(a)]
NS_ASSUME_NONNULL_BEGIN
@interface DJDKMIMOMColor : NSObject
/// 主题色0x9682FF
+ (UIColor *)appMainColor;
///强调色 #248CFE
+ (UIColor *)appEmphasizeColor;
///强调色1 0xBF36FF
+ (UIColor *)appEmphasizeColor1;
///强调色2 0xFB486A
+ (UIColor *)appEmphasizeColor2;
/* ------页面相关颜色 START------ */
/// view的背景色 0xF3F5FA
+ (UIColor *)appBackgroundColor;
/// cell的背景色 0xFFFFFF
+ (UIColor *)appCellBackgroundColor;
///正文颜色 0x333333
+ (UIColor *)mainTextColor;
/// 二级文字颜色 0x666666
+ (UIColor *)secondTextColor;
///三级文字的颜色 0x999999
+ (UIColor *)textThirdColor;
///分割线的颜色 0xE8E8E8
+ (UIColor *)dividerColor;
/* ------页面相关颜色 END------ */
/* ------Button 相关颜色 START------ */
/// button 可用 渐变色的开始 0xFFA936
+ (UIColor *)confirmButtonGradientStartColor;
/// button 可用 渐变色的中间 #9CB3FF
+ (UIColor *)confirmButtonGradientMiddleColor;
/// button 可用 渐变色的开始 0xFFCB47
+ (UIColor *)confirmButtonGradientEndColor;
/// 确定的按钮文字颜色 #FFFFFF
+ (UIColor *)confirmButtonTextColor;
/// 取消按钮 渐变色的开始 0xF7DDBF
+ (UIColor *)cancelButtonGradientStartColor;
/// 取消按钮 渐变色的结束 0xF7E8C4
+ (UIColor *)cancelButtonGradientEndColor;
/// 取消的按钮文字颜色 0xFFA936
+ (UIColor *)cancelButtonTextColor;
/// 取消按钮单一普通背景色 0xFFCE4E
+ (UIColor *)cancelButtonNormalBgColor;
/// 按钮不可点击背景色 0xD2D5D7
+ (UIColor *)disableButtonColor;
/// 按钮不可点击文字颜色 0xF9F9F9
+ (UIColor *)disableButtonTextColor;
/* ------Button 相关颜色 END------ */
/* ------弹窗相关颜色 START------ */
+ (UIColor *)alertBackgroundColor;
+ (UIColor *)alertTitleColor;
+ (UIColor *)alertMessageColor;
+ (UIColor *)actionSeparatorColor;
/* ------弹窗相关颜色 END------ */
///tabbar 没有点击的时候颜色 0x333333, 0.4
+ (UIColor *)tabbarNormalColor;
/// tabbar的View的color 0xFFFFFF
+ (UIColor *)tabbarViewColor;
+ (UIColor *)colorWithHexString:(NSString *)hexString;
+ (UIColor *)inputTextColor;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,105 +1,99 @@
//
// DJDKMIMOMColor.m
// YUMI
//
// Created by YUMI on 2021/9/9.
//
#import "DJDKMIMOMColor.h"
@implementation DJDKMIMOMColor
/// 0x9682FF
+ (UIColor *)appMainColor {
return UIColorFromRGB(0x9682FF);
}
/// #248CFE
+ (UIColor *)appEmphasizeColor {
return UIColorFromRGB(0x248CFE);
}
///1 0xBF36FF
+ (UIColor *)appEmphasizeColor1 {
return UIColorFromRGB(0xBF36FF);
}
///2 0xFB486A
+ (UIColor *)appEmphasizeColor2 {
return UIColorFromRGB(0xFB486A);
}
/* ------ START------ */
/// view 0xF3F5FA
+ (UIColor *)appBackgroundColor {
return UIColorFromRGB(0xF3F5FA);
}
/// cell 0xFFFFFF
+ (UIColor *)appCellBackgroundColor {
return UIColorFromRGB(0xFFFFFF);
}
/// 0x333333
+ (UIColor *)mainTextColor {
return UIColorFromRGB(0x161958);
}
/// 0x666666
+ (UIColor *)secondTextColor {
return UIColorFromRGB(0x8A8CAB);
}
/// 0x999999
+ (UIColor *)textThirdColor {
return UIColorFromRGB(0xBABBCD);
}
///线 0xE8E8E8
+ (UIColor *)dividerColor {
return UIColorFromRGB(0xE8E8E8);
}
/* ------ END------ */
/* ------Button START------ */
/// button 0x3CAAFF
+ (UIColor *)confirmButtonGradientStartColor {
return UIColorFromRGB(0x13E2F5);
}
/// button 0xB176FF
+ (UIColor *)confirmButtonGradientEndColor {
return UIColorFromRGB(0xCC66FF);
}
/// #FFFFFF
+ (UIColor *)confirmButtonTextColor {
return UIColorFromRGB(0xFFFFFF);
}
/// 0xF7DDBF
+ (UIColor *)cancelButtonGradientStartColor {
return UIColorFromRGB(0xCEEFFD);
}
/// button #9CB3FF
+ (UIColor *)confirmButtonGradientMiddleColor {
return UIColorFromRGB(0xf9CB3FF);
}
/// 0xF7E8C4
+ (UIColor *)cancelButtonGradientEndColor {
return UIColorFromRGB(0xD2F4F4);
}
/// 0xFFA936
+ (UIColor *)cancelButtonTextColor {
return UIColorFromRGB(0x5FCCE4);
}
/// 0xFFCE4E
+ (UIColor *)cancelButtonNormalBgColor {
return UIColorFromRGB(0xCEEFFD);
}
/// 0xD2D5D7
+ (UIColor *)disableButtonColor {
return UIColorFromRGB(0xCEEFFD);
}
/// 0xF9F9F9
+ (UIColor *)disableButtonTextColor {
return UIColorFromRGB(0xB3B3C3);
}
/* ------Button END------ */
/* ------ START------ */
+ (UIColor *)alertBackgroundColor {
return UIColorFromRGB(0xFFFFFF);
}
@@ -112,13 +106,12 @@
+ (UIColor *)actionSeparatorColor {
return UIColorFromRGB(0xF0F0F0);
}
/* ------ END------ */
///tabbar 0x333333, 0.4
+ (UIColor *)tabbarNormalColor {
return UIColorRGBAlpha(0x333333, 0.4);
}
/// tabbarViewcolor 0xFFFFFF
+ (UIColor *)tabbarViewColor {
return UIColorFromRGB(0xFFFFFF);
}
@@ -130,25 +123,25 @@
NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString];
CGFloat alpha, red, blue, green;
switch ([colorString length]) {
case 3: // #RGB
case 3:
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 1];
green = [self colorComponentFrom: colorString start: 1 length: 1];
blue = [self colorComponentFrom: colorString start: 2 length: 1];
break;
case 4: // #ARGB
case 4:
alpha = [self colorComponentFrom: colorString start: 0 length: 1];
red = [self colorComponentFrom: colorString start: 1 length: 1];
green = [self colorComponentFrom: colorString start: 2 length: 1];
blue = [self colorComponentFrom: colorString start: 3 length: 1];
break;
case 6: // #RRGGBB
case 6:
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 2];
green = [self colorComponentFrom: colorString start: 2 length: 2];
blue = [self colorComponentFrom: colorString start: 4 length: 2];
break;
case 8: // #AARRGGBB
case 8:
alpha = [self colorComponentFrom: colorString start: 0 length: 2];
red = [self colorComponentFrom: colorString start: 2 length: 2];
green = [self colorComponentFrom: colorString start: 4 length: 2];
@@ -169,7 +162,7 @@
return hexComponent / 255.0;
}
/// #1F1A4E
+ (UIColor *)inputTextColor {
return [self colorWithHexString:@"#1F1A4E"];
}

View File

@@ -0,0 +1,177 @@
//
// DJDKMIMOMColor.m
// YUMI
//
// Created by YUMI on 2021/9/9.
//
#import "DJDKMIMOMColor.h"
@implementation DJDKMIMOMColor
/// 主题色0x9682FF
+ (UIColor *)appMainColor {
return UIColorFromRGB(0x9682FF);
}
///强调色 #248CFE
+ (UIColor *)appEmphasizeColor {
return UIColorFromRGB(0x248CFE);
}
///强调色1 0xBF36FF
+ (UIColor *)appEmphasizeColor1 {
return UIColorFromRGB(0xBF36FF);
}
///强调色2 0xFB486A
+ (UIColor *)appEmphasizeColor2 {
return UIColorFromRGB(0xFB486A);
}
/* ------页面相关颜色 START------ */
/// view的背景色 0xF3F5FA
+ (UIColor *)appBackgroundColor {
return UIColorFromRGB(0xF3F5FA);
}
/// cell的背景色 0xFFFFFF
+ (UIColor *)appCellBackgroundColor {
return UIColorFromRGB(0xFFFFFF);
}
///正文颜色 0x333333
+ (UIColor *)mainTextColor {
return UIColorFromRGB(0x161958);
}
/// 二级文字颜色 0x666666
+ (UIColor *)secondTextColor {
return UIColorFromRGB(0x8A8CAB);
}
///三级文字的颜色 0x999999
+ (UIColor *)textThirdColor {
return UIColorFromRGB(0xBABBCD);
}
///分割线的颜色 0xE8E8E8
+ (UIColor *)dividerColor {
return UIColorFromRGB(0xE8E8E8);
}
/* ------页面相关颜色 END------ */
/* ------Button 相关颜色 START------ */
/// button 可用 渐变色的开始 0x3CAAFF
+ (UIColor *)confirmButtonGradientStartColor {
return UIColorFromRGB(0x13E2F5);
}
/// button 可用 渐变色的开始 0xB176FF
+ (UIColor *)confirmButtonGradientEndColor {
return UIColorFromRGB(0xCC66FF);
}
/// 确定的按钮文字颜色 #FFFFFF
+ (UIColor *)confirmButtonTextColor {
return UIColorFromRGB(0xFFFFFF);
}
/// 取消按钮 渐变色的开始 0xF7DDBF
+ (UIColor *)cancelButtonGradientStartColor {
return UIColorFromRGB(0xCEEFFD);
}
/// button 可用 渐变色的中间 #9CB3FF
+ (UIColor *)confirmButtonGradientMiddleColor {
return UIColorFromRGB(0xf9CB3FF);
}
/// 取消按钮 渐变色的结束 0xF7E8C4
+ (UIColor *)cancelButtonGradientEndColor {
return UIColorFromRGB(0xD2F4F4);
}
/// 取消的按钮文字颜色 0xFFA936
+ (UIColor *)cancelButtonTextColor {
return UIColorFromRGB(0x5FCCE4);
}
/// 取消按钮单一普通背景色 0xFFCE4E
+ (UIColor *)cancelButtonNormalBgColor {
return UIColorFromRGB(0xCEEFFD);
}
/// 按钮不可点击背景色 0xD2D5D7
+ (UIColor *)disableButtonColor {
return UIColorFromRGB(0xCEEFFD);
}
/// 按钮不可点击文字颜色 0xF9F9F9
+ (UIColor *)disableButtonTextColor {
return UIColorFromRGB(0xB3B3C3);
}
/* ------Button 相关颜色 END------ */
/* ------弹窗相关颜色 START------ */
+ (UIColor *)alertBackgroundColor {
return UIColorFromRGB(0xFFFFFF);
}
+ (UIColor *)alertTitleColor {
return UIColorFromRGB(0x333333);
}
+ (UIColor *)alertMessageColor {
return UIColorFromRGB(0x333333);
}
+ (UIColor *)actionSeparatorColor {
return UIColorFromRGB(0xF0F0F0);
}
/* ------弹窗相关颜色 END------ */
///tabbar 没有点击的时候颜色 0x333333, 0.4
+ (UIColor *)tabbarNormalColor {
return UIColorRGBAlpha(0x333333, 0.4);
}
/// tabbar的View的color 0xFFFFFF
+ (UIColor *)tabbarViewColor {
return UIColorFromRGB(0xFFFFFF);
}
+ (UIColor *)colorWithHexString: (NSString *) hexString {
if (hexString.length == 0) {
return [UIColor blackColor];
}
NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString];
CGFloat alpha, red, blue, green;
switch ([colorString length]) {
case 3: // #RGB
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 1];
green = [self colorComponentFrom: colorString start: 1 length: 1];
blue = [self colorComponentFrom: colorString start: 2 length: 1];
break;
case 4: // #ARGB
alpha = [self colorComponentFrom: colorString start: 0 length: 1];
red = [self colorComponentFrom: colorString start: 1 length: 1];
green = [self colorComponentFrom: colorString start: 2 length: 1];
blue = [self colorComponentFrom: colorString start: 3 length: 1];
break;
case 6: // #RRGGBB
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 2];
green = [self colorComponentFrom: colorString start: 2 length: 2];
blue = [self colorComponentFrom: colorString start: 4 length: 2];
break;
case 8: // #AARRGGBB
alpha = [self colorComponentFrom: colorString start: 0 length: 2];
red = [self colorComponentFrom: colorString start: 2 length: 2];
green = [self colorComponentFrom: colorString start: 4 length: 2];
blue = [self colorComponentFrom: colorString start: 6 length: 2];
break;
default:
[NSException raise:@"Invalid color value" format: @"Color value %@ is invalid. It should be a hex value of the form #RBG, #ARGB, #RRGGBB, or #AARRGGBB", hexString];
break;
}
return [UIColor colorWithRed: red green: green blue: blue alpha: alpha];
}
+ (CGFloat) colorComponentFrom: (NSString *) string start: (NSUInteger) start length: (NSUInteger) length {
NSString *substring = [string substringWithRange: NSMakeRange(start, length)];
NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring];
unsigned hexComponent;
[[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent];
return hexComponent / 255.0;
}
///输入框的文本颜色 #1F1A4E
+ (UIColor *)inputTextColor {
return [self colorWithHexString:@"#1F1A4E"];
}
@end

View File

@@ -1,60 +1,46 @@
//
// TTActionSheetConfig.h
// AFNetworking
//
// Created by lee on 2019/5/23.
// action sheet item 配置
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TTItemSelectNormal,
TTItemSelectHighLight,
} TTItemSelectType;
NS_ASSUME_NONNULL_BEGIN
typedef void(^TTActionSheetClickAction)(void);
@interface TTActionSheetConfig : NSObject
/** 标题 */
@property (nonatomic, copy) NSString *title;
/**
标题颜色
*/
@property (nonatomic, strong) UIColor *titleColor;
/** 是否选中 */
@property (nonatomic, assign) TTItemSelectType type;
/** 点击事件 */
@property (nonatomic, copy) TTActionSheetClickAction clickAction;
@property(nonatomic, assign) BOOL displayMoliCoin;
/**
构建 actionSheet item 实例
@param title 标题
@param clickAction 点击事件
@return item 实例
*/
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
clickAction:(TTActionSheetClickAction)clickAction;
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
selectColorType:(TTItemSelectType)type clickAction:(TTActionSheetClickAction)clickAction;
/// 构建实例
/// @param title 标题
/// @param textColor 颜色
/// @param handler 事件处理
+ (TTActionSheetConfig *)actionWithTitle:(NSString *)title
color:(UIColor *)textColor
handler:(TTActionSheetClickAction)handler;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/23.
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TTItemSelectNormal,
TTItemSelectHighLight,
} TTItemSelectType;
NS_ASSUME_NONNULL_BEGIN
typedef void(^TTActionSheetClickAction)(void);
@interface TTActionSheetConfig : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIColor *titleColor;
@property (nonatomic, assign) TTItemSelectType type;
@property (nonatomic, copy) TTActionSheetClickAction clickAction;
@property(nonatomic, assign) BOOL displayMoliCoin;
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
clickAction:(TTActionSheetClickAction)clickAction;
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
selectColorType:(TTItemSelectType)type clickAction:(TTActionSheetClickAction)clickAction;
+ (TTActionSheetConfig *)actionWithTitle:(NSString *)title
color:(UIColor *)textColor
handler:(TTActionSheetClickAction)handler;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,60 @@
//
// TTActionSheetConfig.h
// AFNetworking
//
// Created by lee on 2019/5/23.
// action sheet item 配置
#import <UIKit/UIKit.h>
typedef enum : NSUInteger {
TTItemSelectNormal,
TTItemSelectHighLight,
} TTItemSelectType;
NS_ASSUME_NONNULL_BEGIN
typedef void(^TTActionSheetClickAction)(void);
@interface TTActionSheetConfig : NSObject
/** 标题 */
@property (nonatomic, copy) NSString *title;
/**
标题颜色
*/
@property (nonatomic, strong) UIColor *titleColor;
/** 是否选中 */
@property (nonatomic, assign) TTItemSelectType type;
/** 点击事件 */
@property (nonatomic, copy) TTActionSheetClickAction clickAction;
@property(nonatomic, assign) BOOL displayMoliCoin;
/**
构建 actionSheet item 实例
@param title 标题
@param clickAction 点击事件
@return item 实例
*/
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
clickAction:(TTActionSheetClickAction)clickAction;
+ (TTActionSheetConfig *)normalTitle:(NSString *)title
selectColorType:(TTItemSelectType)type clickAction:(TTActionSheetClickAction)clickAction;
/// 构建实例
/// @param title 标题
/// @param textColor 颜色
/// @param handler 事件处理
+ (TTActionSheetConfig *)actionWithTitle:(NSString *)title
color:(UIColor *)textColor
handler:(TTActionSheetClickAction)handler;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,22 +1,14 @@
//
// TTActionSheetConfig.m
// AFNetworking
//
// Created by lee on 2019/5/23.
//
#import "TTActionSheetConfig.h"
#import "DJDKMIMOMColor.h"
@implementation TTActionSheetConfig
/**
actionSheet item
@param title
@param clickAction
@return item
*/
+ (TTActionSheetConfig *)normalTitle:(NSString *)title clickAction:(TTActionSheetClickAction)clickAction {
return [self normalTitle:title selectColorType:TTItemSelectNormal clickAction:clickAction];

View File

@@ -0,0 +1,49 @@
//
// TTActionSheetConfig.m
// AFNetworking
//
// Created by lee on 2019/5/23.
//
#import "TTActionSheetConfig.h"
#import "DJDKMIMOMColor.h"
@implementation TTActionSheetConfig
/**
构建 actionSheet item 实例
@param title 标题
@param clickAction 点击事件
@return item 实例
*/
+ (TTActionSheetConfig *)normalTitle:(NSString *)title clickAction:(TTActionSheetClickAction)clickAction {
return [self normalTitle:title selectColorType:TTItemSelectNormal clickAction:clickAction];
}
+ (TTActionSheetConfig *)normalTitle:(NSString *)title selectColorType:(TTItemSelectType)type clickAction:(TTActionSheetClickAction)clickAction {
UIColor *color = type == TTItemSelectHighLight ? [DJDKMIMOMColor alertTitleColor] : [DJDKMIMOMColor alertTitleColor];
TTActionSheetConfig *config = [self actionWithTitle:title color:color handler:clickAction];
config.type = type;
return config;
}
+ (TTActionSheetConfig *)actionWithTitle:(NSString *)title
color:(UIColor *)textColor
handler:(TTActionSheetClickAction)handler {
TTActionSheetConfig *config = [[TTActionSheetConfig alloc] init];
config.type = TTItemSelectNormal;
config.title = title;
config.titleColor = textColor;
config.clickAction = handler;
return config;
}
@end

View File

@@ -1,30 +1,28 @@
//
// TTAlertButtonConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
// alert 按钮配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertButtonConfig : NSObject
/** 按钮标题 */
@property (nonatomic, copy) NSString *title;
/** 按钮字体 */
@property (nonatomic, strong) UIFont *font;
/** 按钮字体颜色 */
@property (nonatomic, strong) UIColor *titleColor;
/** 背景色 */
@property (nonatomic, strong) UIColor *backgroundColor;
/** 背景图 */
@property (nonatomic, strong) UIImage *backgroundImage;
/** 圆角 */
@property (nonatomic, assign) CGFloat cornerRadius;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertButtonConfig : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, strong) UIColor *titleColor;
@property (nonatomic, strong) UIColor *backgroundColor;
@property (nonatomic, strong) UIImage *backgroundImage;
@property (nonatomic, assign) CGFloat cornerRadius;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,30 @@
//
// TTAlertButtonConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
// alert 按钮配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertButtonConfig : NSObject
/** 按钮标题 */
@property (nonatomic, copy) NSString *title;
/** 按钮字体 */
@property (nonatomic, strong) UIFont *font;
/** 按钮字体颜色 */
@property (nonatomic, strong) UIColor *titleColor;
/** 背景色 */
@property (nonatomic, strong) UIColor *backgroundColor;
/** 背景图 */
@property (nonatomic, strong) UIImage *backgroundImage;
/** 圆角 */
@property (nonatomic, assign) CGFloat cornerRadius;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTAlertButtonConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertButtonConfig.h"

View File

@@ -0,0 +1,13 @@
//
// TTAlertButtonConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertButtonConfig.h"
@implementation TTAlertButtonConfig
@end

View File

@@ -1,86 +1,70 @@
//
// TTAlertConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
// alert 配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TTAlertButtonConfig.h"
#import "TTAlertMessageAttributedConfig.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TTAlertActionStyle) {
TTAlertActionConfirmStyle = 0, // 只有确定按钮
TTAlertActionCancelStyle = 1, // 只有取消按钮
TTAlertActionBothStyle = 2, // 全部按钮
};
@interface TTAlertConfig : NSObject
// 按钮显示风格
@property (nonatomic, assign) TTAlertActionStyle actionStyle;
// 容器背景设置
@property (nonatomic, strong) UIColor *backgroundColor;
// 标题设置
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIFont *titleFont;
@property (nonatomic, strong) UIColor *titleColor;
// 内容设置
@property (nonatomic, copy) NSString *message;
@property (nonatomic, strong) UIFont *messageFont;
@property (nonatomic, strong) UIColor *messageColor;
/** 内容的行间距,默认不设置,当值为 0 或负数时无效 */
@property (nonatomic, assign) CGFloat messageLineSpacing;
@property(nonatomic, assign) NSTextAlignment messageTextAlignment;
/** 内容富文本配置 */
@property (nonatomic, strong) NSArray<TTAlertMessageAttributedConfig *> *messageAttributedConfig;
///配置内容的富文本
@property (nonatomic,strong) NSMutableAttributedString *messageAttributed;
// 取消按钮配置
@property (nonatomic, strong) TTAlertButtonConfig *cancelButtonConfig;
// 确定按钮配置
@property (nonatomic, strong) TTAlertButtonConfig *confirmButtonConfig;
/**
背景蒙层的透明度
@Description 默认是 000000 黑色 0.3 alpha
*/
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
// 圆角
@property (nonatomic, assign) CGFloat cornerRadius;
// 点击蒙层是否消失默认YES
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
/**
点击确定取消按钮时禁止弹窗自动消失默认NO
@discussion 若值为 YES需要主动调用 [TTPopup dismiss] 消除弹窗
*/
@property (nonatomic, assign) BOOL disableAutoDismissWhenClickButton;
/**
重复弹窗过滤默认NO
@discussion 设置过滤时,队列中将不会出现相同过滤标识的弹窗
过滤标识通过 #<filterIdentifier> 设置
*/
@property (nonatomic, assign) BOOL shouldFilterPopup;
/**
过滤标识默认nil
*/
@property (nonatomic, copy) NSString *filterIdentifier;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TTAlertButtonConfig.h"
#import "TTAlertMessageAttributedConfig.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TTAlertActionStyle) {
TTAlertActionConfirmStyle = 0,
TTAlertActionCancelStyle = 1,
TTAlertActionBothStyle = 2,
};
@interface TTAlertConfig : NSObject
@property (nonatomic, assign) TTAlertActionStyle actionStyle;
@property (nonatomic, strong) UIColor *backgroundColor;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIFont *titleFont;
@property (nonatomic, strong) UIColor *titleColor;
@property (nonatomic, copy) NSString *message;
@property (nonatomic, strong) UIFont *messageFont;
@property (nonatomic, strong) UIColor *messageColor;
@property (nonatomic, assign) CGFloat messageLineSpacing;
@property(nonatomic, assign) NSTextAlignment messageTextAlignment;
@property (nonatomic, strong) NSArray<TTAlertMessageAttributedConfig *> *messageAttributedConfig;
@property (nonatomic,strong) NSMutableAttributedString *messageAttributed;
@property (nonatomic, strong) TTAlertButtonConfig *cancelButtonConfig;
@property (nonatomic, strong) TTAlertButtonConfig *confirmButtonConfig;
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
@property (nonatomic, assign) CGFloat cornerRadius;
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
@property (nonatomic, assign) BOOL disableAutoDismissWhenClickButton;
@property (nonatomic, assign) BOOL shouldFilterPopup;
@property (nonatomic, copy) NSString *filterIdentifier;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,86 @@
//
// TTAlertConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
// alert 配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TTAlertButtonConfig.h"
#import "TTAlertMessageAttributedConfig.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TTAlertActionStyle) {
TTAlertActionConfirmStyle = 0, // 只有确定按钮
TTAlertActionCancelStyle = 1, // 只有取消按钮
TTAlertActionBothStyle = 2, // 全部按钮
};
@interface TTAlertConfig : NSObject
// 按钮显示风格
@property (nonatomic, assign) TTAlertActionStyle actionStyle;
// 容器背景设置
@property (nonatomic, strong) UIColor *backgroundColor;
// 标题设置
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIFont *titleFont;
@property (nonatomic, strong) UIColor *titleColor;
// 内容设置
@property (nonatomic, copy) NSString *message;
@property (nonatomic, strong) UIFont *messageFont;
@property (nonatomic, strong) UIColor *messageColor;
/** 内容的行间距,默认不设置,当值为 0 或负数时无效 */
@property (nonatomic, assign) CGFloat messageLineSpacing;
@property(nonatomic, assign) NSTextAlignment messageTextAlignment;
/** 内容富文本配置 */
@property (nonatomic, strong) NSArray<TTAlertMessageAttributedConfig *> *messageAttributedConfig;
///配置内容的富文本
@property (nonatomic,strong) NSMutableAttributedString *messageAttributed;
// 取消按钮配置
@property (nonatomic, strong) TTAlertButtonConfig *cancelButtonConfig;
// 确定按钮配置
@property (nonatomic, strong) TTAlertButtonConfig *confirmButtonConfig;
/**
背景蒙层的透明度
@Description 默认是 000000 黑色 0.3 alpha
*/
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
// 圆角
@property (nonatomic, assign) CGFloat cornerRadius;
// 点击蒙层是否消失默认YES
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
/**
点击确定取消按钮时禁止弹窗自动消失默认NO
@discussion 若值为 YES需要主动调用 [TTPopup dismiss] 消除弹窗
*/
@property (nonatomic, assign) BOOL disableAutoDismissWhenClickButton;
/**
重复弹窗过滤默认NO
@discussion 设置过滤时,队列中将不会出现相同过滤标识的弹窗
过滤标识通过 #<filterIdentifier> 设置
*/
@property (nonatomic, assign) BOOL shouldFilterPopup;
/**
过滤标识默认nil
*/
@property (nonatomic, copy) NSString *filterIdentifier;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTAlertConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertConfig.h"
#import "UIImage+Utils.h"
@@ -26,36 +24,35 @@ static CGFloat kAlertButtonCornerRadius = 8.f;
_backgroundColor = [DJDKMIMOMColor alertBackgroundColor];
//
kAlertTitleFont = 16.f;
kAlertCornerRadius = 14.f;
kAlertButtonCornerRadius = 19.f;
_actionStyle = TTAlertActionBothStyle;
// title
_title = @"";//
_titleFont = [UIFont fontWithName:@"PingFangSC-Medium" size:kAlertTitleFont];//
_titleColor = [DJDKMIMOMColor alertTitleColor];//
// message
_title = @"";
_titleFont = [UIFont fontWithName:@"PingFangSC-Medium" size:kAlertTitleFont];
_titleColor = [DJDKMIMOMColor alertTitleColor];
_message = @"";
_messageFont = [UIFont systemFontOfSize:kAlertMessageFont];//
_messageColor = [DJDKMIMOMColor alertMessageColor];//
_messageLineSpacing = kAlertMessageFontLineSpace;//
_messageAttributedConfig = @[];//
_messageFont = [UIFont systemFontOfSize:kAlertMessageFont];
_messageColor = [DJDKMIMOMColor alertMessageColor];
_messageLineSpacing = kAlertMessageFontLineSpace;
_messageAttributedConfig = @[];
_messageTextAlignment = NSTextAlignmentCenter;
// cancel button
_cancelButtonConfig = [[TTAlertButtonConfig alloc] init];
_cancelButtonConfig.title = YMLocalizedString(@"XPRoomSettingInputView4");//
_cancelButtonConfig.font = [UIFont systemFontOfSize:kAlertButtonFont];//
_cancelButtonConfig.titleColor = [DJDKMIMOMColor cancelButtonTextColor];//
_cancelButtonConfig.title = YMLocalizedString(@"XPRoomSettingInputView4");
_cancelButtonConfig.font = [UIFont systemFontOfSize:kAlertButtonFont];
_cancelButtonConfig.titleColor = [DJDKMIMOMColor cancelButtonTextColor];
_cancelButtonConfig.backgroundImage = [UIImage gradientColorImageFromColors:@[[DJDKMIMOMColor cancelButtonGradientStartColor], [DJDKMIMOMColor cancelButtonGradientEndColor]] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(10, 10)];
_cancelButtonConfig.cornerRadius = kAlertButtonCornerRadius;//
_cancelButtonConfig.cornerRadius = kAlertButtonCornerRadius;
// confirm button
_confirmButtonConfig = [[TTAlertButtonConfig alloc] init];
_confirmButtonConfig.title = YMLocalizedString(@"TTAlertConfig0");
_confirmButtonConfig.font = [UIFont systemFontOfSize:kAlertButtonFont];
@@ -65,22 +62,12 @@ static CGFloat kAlertButtonCornerRadius = 8.f;
imgSize:CGSizeMake(10, 10)];
_confirmButtonConfig.cornerRadius = kAlertButtonCornerRadius;
// //
// CAGradientLayer *gradientLayer = [CAGradientLayer layer];
// gradientLayer.colors = @[(__bridge id)UIColorFromRGB(0xE29030).CGColor,
// (__bridge id)UIColorFromRGB(0xFCC074).CGColor];
// gradientLayer.startPoint = CGPointMake(0.0, 0.0); //
// gradientLayer.endPoint = CGPointMake(0.0, 1.0); //
// gradientLayer.frame = CGRectMake(0, 0, 120, 38); //
//
// //
// [_previewActionButton.layer insertSublayer:gradientLayer atIndex:0];
_cornerRadius = kAlertCornerRadius;//
_shouldDismissOnBackgroundTouch = YES;//
_cornerRadius = kAlertCornerRadius;
_shouldDismissOnBackgroundTouch = YES;
// mask default 0.3 black
_maskBackgroundAlpha = kAlertBackgroundColorAlpha; // alert
_maskBackgroundAlpha = kAlertBackgroundColorAlpha;
_disableAutoDismissWhenClickButton = NO;
}
return self;

View File

@@ -0,0 +1,90 @@
//
// TTAlertConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertConfig.h"
#import "UIImage+Utils.h"
#import "DJDKMIMOMColor.h"
static CGFloat kAlertTitleFont = 18.f;
static CGFloat kAlertButtonFont = 15.f;
static CGFloat kAlertMessageFont = 15.f;
static CGFloat kAlertCornerRadius = 12.f;
static CGFloat kAlertBackgroundColorAlpha = 0.3;
static CGFloat kAlertMessageFontLineSpace = -1;
static CGFloat kAlertButtonCornerRadius = 8.f;
@implementation TTAlertConfig
- (instancetype)init {
self = [super init];
if (self) {
_backgroundColor = [DJDKMIMOMColor alertBackgroundColor];
//背景颜色
kAlertTitleFont = 16.f;
kAlertCornerRadius = 14.f;
kAlertButtonCornerRadius = 19.f;
_actionStyle = TTAlertActionBothStyle;
// title
_title = @"";// 标题
_titleFont = [UIFont fontWithName:@"PingFangSC-Medium" size:kAlertTitleFont];// 字体
_titleColor = [DJDKMIMOMColor alertTitleColor];// 颜色
// message
_message = @"";
_messageFont = [UIFont systemFontOfSize:kAlertMessageFont];// 内容
_messageColor = [DJDKMIMOMColor alertMessageColor];// 颜色
_messageLineSpacing = kAlertMessageFontLineSpace;// 字体行间距
_messageAttributedConfig = @[];// 自定义富文本样式
_messageTextAlignment = NSTextAlignmentCenter;
// cancel button
_cancelButtonConfig = [[TTAlertButtonConfig alloc] init];
_cancelButtonConfig.title = YMLocalizedString(@"XPRoomSettingInputView4");// 取消按钮
_cancelButtonConfig.font = [UIFont systemFontOfSize:kAlertButtonFont];// 按钮字体
_cancelButtonConfig.titleColor = [DJDKMIMOMColor cancelButtonTextColor];// 字体颜色
_cancelButtonConfig.backgroundImage = [UIImage gradientColorImageFromColors:@[[DJDKMIMOMColor cancelButtonGradientStartColor], [DJDKMIMOMColor cancelButtonGradientEndColor]] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(10, 10)];
_cancelButtonConfig.cornerRadius = kAlertButtonCornerRadius;// 按钮背景图
// confirm button
_confirmButtonConfig = [[TTAlertButtonConfig alloc] init];
_confirmButtonConfig.title = YMLocalizedString(@"TTAlertConfig0");
_confirmButtonConfig.font = [UIFont systemFontOfSize:kAlertButtonFont];
_confirmButtonConfig.titleColor = [DJDKMIMOMColor confirmButtonTextColor];
_confirmButtonConfig.backgroundImage = [UIImage gradientColorImageFromColors:@[UIColorFromRGB(0xE29030), UIColorFromRGB(0xFCC074)]
gradientType:GradientTypeLeftToRight
imgSize:CGSizeMake(10, 10)];
_confirmButtonConfig.cornerRadius = kAlertButtonCornerRadius;
// // 创建渐变图层
// CAGradientLayer *gradientLayer = [CAGradientLayer layer];
// gradientLayer.colors = @[(__bridge id)UIColorFromRGB(0xE29030).CGColor,
// (__bridge id)UIColorFromRGB(0xFCC074).CGColor];
// gradientLayer.startPoint = CGPointMake(0.0, 0.0); // 顶部中央
// gradientLayer.endPoint = CGPointMake(0.0, 1.0); // 底部中央
// gradientLayer.frame = CGRectMake(0, 0, 120, 38); // 设置渐变图层大小
//
// // 将渐变图层添加到按钮图层
// [_previewActionButton.layer insertSublayer:gradientLayer atIndex:0];
_cornerRadius = kAlertCornerRadius;// 默认圆角
_shouldDismissOnBackgroundTouch = YES;// 点击蒙层是否消失
// mask default 0.3 black
_maskBackgroundAlpha = kAlertBackgroundColorAlpha; // alert 背景色
_disableAutoDismissWhenClickButton = NO;
}
return self;
}
@end

View File

@@ -1,34 +1,28 @@
//
// TTAlertContentAttributedConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
// alert 提示内容富文本配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertMessageAttributedConfig : NSObject
/** 富文本字段需要特殊显示的文本 */
@property (nonatomic, copy) NSString *text;
/** 颜色 */
@property (nonatomic, strong) UIColor *color;
/** 字体 */
@property (nonatomic, strong) UIFont *font;
/**
目标文本指定位置,一旦设置则无视 text 的值
@discussion 内容文本中出现相同的目标文本时,可通过设置 range 精确指定位置
*/
@property (nonatomic, assign) NSRange range;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertMessageAttributedConfig : NSObject
@property (nonatomic, copy) NSString *text;
@property (nonatomic, strong) UIColor *color;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSRange range;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,34 @@
//
// TTAlertContentAttributedConfig.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
// alert 提示内容富文本配置
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertMessageAttributedConfig : NSObject
/** 富文本字段需要特殊显示的文本 */
@property (nonatomic, copy) NSString *text;
/** 颜色 */
@property (nonatomic, strong) UIColor *color;
/** 字体 */
@property (nonatomic, strong) UIFont *font;
/**
目标文本指定位置,一旦设置则无视 text 的值
@discussion 内容文本中出现相同的目标文本时,可通过设置 range 精确指定位置
*/
@property (nonatomic, assign) NSRange range;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTAlertContentAttributedConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertMessageAttributedConfig.h"

View File

@@ -0,0 +1,13 @@
//
// TTAlertContentAttributedConfig.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertMessageAttributedConfig.h"
@implementation TTAlertMessageAttributedConfig
@end

View File

@@ -1,38 +1,23 @@
//
// TTPopupConstants.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
Popup 组件通用回调
*/
typedef void(^TTPopupCompletionHandler)(void);
/**
弹窗类型
- TTPopupStyleAlert: Alert
- TTPopupStyleActionSheet: ActionSheet
*/
typedef NS_ENUM(NSUInteger, TTPopupStyle) {
TTPopupStyleAlert = 0,
TTPopupStyleActionSheet
};
/**
弹窗优先级
- TTPopupPriorityNormal: 普通
- TTPopupPriorityHigh: 高
- TTPopupPriorityRequired: 必须
*/
typedef NS_ENUM(NSUInteger, TTPopupPriority) {
TTPopupPriorityNormal = 0,
TTPopupPriorityHigh,
TTPopupPriorityRequired
};
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import <UIKit/UIKit.h>
typedef void(^TTPopupCompletionHandler)(void);
typedef NS_ENUM(NSUInteger, TTPopupStyle) {
TTPopupStyleAlert = 0,
TTPopupStyleActionSheet
};
typedef NS_ENUM(NSUInteger, TTPopupPriority) {
TTPopupPriorityNormal = 0,
TTPopupPriorityHigh,
TTPopupPriorityRequired
};

View File

@@ -0,0 +1,38 @@
//
// TTPopupConstants.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
/**
Popup 组件通用回调
*/
typedef void(^TTPopupCompletionHandler)(void);
/**
弹窗类型
- TTPopupStyleAlert: Alert
- TTPopupStyleActionSheet: ActionSheet
*/
typedef NS_ENUM(NSUInteger, TTPopupStyle) {
TTPopupStyleAlert = 0,
TTPopupStyleActionSheet
};
/**
弹窗优先级
- TTPopupPriorityNormal: 普通
- TTPopupPriorityHigh: 高
- TTPopupPriorityRequired: 必须
*/
typedef NS_ENUM(NSUInteger, TTPopupPriority) {
TTPopupPriorityNormal = 0,
TTPopupPriorityHigh,
TTPopupPriorityRequired
};

View File

@@ -1,24 +1,21 @@
//
// TTPopupManagerService.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TTPopupManagerServiceProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupManagerService : NSObject<TTPopupManagerServiceProtocol>
/**
当前显示的弹窗服务
*/
@property (nonatomic, strong) id<TTPopupServiceProtocol> currentPopupService;
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import "TTPopupManagerServiceProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupManagerService : NSObject<TTPopupManagerServiceProtocol>
@property (nonatomic, strong) id<TTPopupServiceProtocol> currentPopupService;
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,24 @@
//
// TTPopupManagerService.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TTPopupManagerServiceProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupManagerService : NSObject<TTPopupManagerServiceProtocol>
/**
当前显示的弹窗服务
*/
@property (nonatomic, strong) id<TTPopupServiceProtocol> currentPopupService;
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTPopupManagerService.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupManagerService.h"
@@ -13,9 +11,8 @@
@interface TTPopupManagerService ()
@property (nonatomic, strong) NSMutableArray< id<TTPopupServiceProtocol> > *queue;
/**
*/
@property (nonatomic, assign, getter=isShowingPopup) BOOL showingPopup;
@end
@@ -53,7 +50,7 @@
return;
}
[_queue insertObject:service atIndex:insertPosition];
//
if (_currentPopupService == nil && _queue.count == 1) {
[self showPopup];
}
@@ -61,8 +58,7 @@
- (void)removePopupService {
// dismiss
//使 _currentPopupService
if (!self.isShowingPopup) {
return;
}
@@ -79,11 +75,7 @@
_currentPopupService = nil;
}
/**
@discussion dismissPopupForView
*/
- (void)removeSourceWhenTouchMaskView {
if (_currentPopupService == nil) {
return;
@@ -95,9 +87,8 @@
}
#pragma mark - Private Methods
/**
*/
- (void)showPopup {
if (self.isShowingPopup) {
return;
@@ -131,27 +122,26 @@
[popup showWithLayout:FFPopupLayoutMake(horizontalLayout, verticalLayout) duration:0.0];
__weak typeof(self) weakSelf = self;
// dismissPopupForView:animated:
//
// _currentPopupService
popup.didFinishDismissingBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
BOOL isDismissOnBackgroundTouch = strongSelf.currentPopupService != nil;
if (isDismissOnBackgroundTouch) {
//
[self removeSourceWhenTouchMaskView];
}
if (popupService.didFinishDismissHandler) {
popupService.didFinishDismissHandler(isDismissOnBackgroundTouch);
}
//
strongSelf.showingPopup = NO;
//
[strongSelf showPopup];
};
popup.didFinishShowingBlock = ^{
//
self.showingPopup = YES;
if (popupService.didFinishShowingHandler) {
popupService.didFinishShowingHandler();
@@ -159,11 +149,7 @@
};
}
/**
@param service
@return
*/
- (NSInteger)insertPositionForPopupService:(id<TTPopupServiceProtocol>)service {
__block NSInteger result = NSNotFound;
if (service == nil) {
@@ -172,7 +158,7 @@
if (_queue.count == 0) {
return 0;
}
//
if (service.shouldFilterPopup && service.filterIdentifier.length > 0) {
BOOL filterFlag = NO;
for (id<TTPopupServiceProtocol> serv in _queue) {
@@ -186,13 +172,13 @@
}
}
[_queue enumerateObjectsUsingBlock:^(id<TTPopupServiceProtocol> _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
//
if (service.priority > model.priority) {
result = idx;
*stop = YES;
}
}];
//
result = MIN(result, _queue.count);
return result;
}

View File

@@ -0,0 +1,200 @@
//
// TTPopupManagerService.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupManagerService.h"
#import <FFPopup/FFPopup.h>
@interface TTPopupManagerService ()
@property (nonatomic, strong) NSMutableArray< id<TTPopupServiceProtocol> > *queue;
/**
当前是否正在显示状态
*/
@property (nonatomic, assign, getter=isShowingPopup) BOOL showingPopup;
@end
@implementation TTPopupManagerService
#pragma mark - Life Cycle
+ (instancetype)sharedInstance {
static TTPopupManagerService *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
if (self = [super init]) {
_queue = [NSMutableArray array];
}
return self;
}
#pragma mark - TTPopupManagerServiceProtocol
- (void)addPopupService:(id<TTPopupServiceProtocol>)service {
if (![service conformsToProtocol:@protocol(TTPopupServiceProtocol)]) {
return;
}
if ([_queue containsObject:service]) {
return;
}
NSInteger insertPosition = [self insertPositionForPopupService:service];
if (insertPosition == NSNotFound) {
return;
}
[_queue insertObject:service atIndex:insertPosition];
//当前没有弹窗显示且队列只有一个元素时,显示弹窗
if (_currentPopupService == nil && _queue.count == 1) {
[self showPopup];
}
}
- (void)removePopupService {
//防止当弹窗还未显示完成,在显示过程中频繁调用 dismiss
//使得 _currentPopupService 被清空,导致弹窗无法消失,从而假死现象
if (!self.isShowingPopup) {
return;
}
if (_currentPopupService == nil) {
return;
}
if (_queue.count > 0) {
[_queue removeObjectAtIndex:0];
}
[FFPopup dismissPopupForView:_currentPopupService.contentView animated:YES];
_currentPopupService = nil;
}
/**
点击蒙层时移除队列中的数据源
@discussion 注意无需调用 dismissPopupForView
*/
- (void)removeSourceWhenTouchMaskView {
if (_currentPopupService == nil) {
return;
}
if (_queue.count > 0) {
[_queue removeObjectAtIndex:0];
}
_currentPopupService = nil;
}
#pragma mark - Private Methods
/**
显示弹窗
*/
- (void)showPopup {
if (self.isShowingPopup) {
return;
}
if (_currentPopupService) {
return;
}
if (_queue.count == 0) {
return;
}
id<TTPopupServiceProtocol> popupService = _queue.firstObject;
if (![popupService conformsToProtocol:@protocol(TTPopupServiceProtocol)]) {
return;
}
_currentPopupService = popupService;
FFPopupHorizontalLayout horizontalLayout = FFPopupHorizontalLayout_Center;
FFPopupVerticalLayout verticalLayout = FFPopupVerticalLayout_Center;
FFPopupShowType showType = (FFPopupShowType)popupService.showType;
FFPopupDismissType dismissType = FFPopupDismissType_GrowOut;
if (popupService.style == TTPopupStyleActionSheet) {
verticalLayout = FFPopupVerticalLayout_Bottom;
showType = FFPopupShowType_SlideInFromBottom;
dismissType = FFPopupDismissType_SlideOutToBottom;
}
FFPopup *popup = [FFPopup popupWithContentView:popupService.contentView];
popup.showType = showType;
popup.dismissType = dismissType;
popup.maskType = FFPopupMaskType_Dimmed;
popup.dimmedMaskAlpha = popupService.maskBackgroundAlpha;
popup.shouldDismissOnBackgroundTouch = popupService.shouldDismissOnBackgroundTouch;
[popup showWithLayout:FFPopupLayoutMake(horizontalLayout, verticalLayout) duration:0.0];
__weak typeof(self) weakSelf = self;
// 不管是调用dismissPopupForView:animated: 还是‘点击蒙层消除’,最终都会走到这里
// 适合在此展示队列中下一个弹窗
// 通过 _currentPopupService 是否为空可以判断是哪种消除方式
popup.didFinishDismissingBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
BOOL isDismissOnBackgroundTouch = strongSelf.currentPopupService != nil;
if (isDismissOnBackgroundTouch) {
// ‘点击蒙层消除’时,在展现下一个弹窗前移除数据源
[self removeSourceWhenTouchMaskView];
}
if (popupService.didFinishDismissHandler) {
popupService.didFinishDismissHandler(isDismissOnBackgroundTouch);
}
// 弹窗消除结束,更新状态
strongSelf.showingPopup = NO;
// 显示下一个弹窗
[strongSelf showPopup];
};
popup.didFinishShowingBlock = ^{
// 开始弹窗,更新状态
self.showingPopup = YES;
if (popupService.didFinishShowingHandler) {
popupService.didFinishShowingHandler();
}
};
}
/**
弹窗将要插入队列的位置
@param service 弹窗服务实例
@return 队列的位置
*/
- (NSInteger)insertPositionForPopupService:(id<TTPopupServiceProtocol>)service {
__block NSInteger result = NSNotFound;
if (service == nil) {
return result;
}
if (_queue.count == 0) {
return 0;
}
// 设置重复弹窗过滤
if (service.shouldFilterPopup && service.filterIdentifier.length > 0) {
BOOL filterFlag = NO;
for (id<TTPopupServiceProtocol> serv in _queue) {
if ([serv.filterIdentifier isEqualToString:service.filterIdentifier]) {
filterFlag = YES;
break;
}
}
if (filterFlag) {
return result;
}
}
[_queue enumerateObjectsUsingBlock:^(id<TTPopupServiceProtocol> _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
// 找到队中比当前弹窗优先级低的位置
if (service.priority > model.priority) {
result = idx;
*stop = YES;
}
}];
// 如果没有合适的位置,则将新的弹窗放在队尾
result = MIN(result, _queue.count);
return result;
}
@end

View File

@@ -1,25 +1,17 @@
//
// TTPopupManagerServiceProtocol.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupServiceProtocol.h"
@protocol TTPopupManagerServiceProtocol <NSObject>
/**
添加一个弹窗
@param service 服从弹窗服务的实例
*/
- (void)addPopupService:(id<TTPopupServiceProtocol>)service;
/**
移除一个弹窗
*/
- (void)removePopupService;
@end
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import "TTPopupServiceProtocol.h"
@protocol TTPopupManagerServiceProtocol <NSObject>
- (void)addPopupService:(id<TTPopupServiceProtocol>)service;
- (void)removePopupService;
@end

View File

@@ -0,0 +1,25 @@
//
// TTPopupManagerServiceProtocol.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupServiceProtocol.h"
@protocol TTPopupManagerServiceProtocol <NSObject>
/**
添加一个弹窗
@param service 服从弹窗服务的实例
*/
- (void)addPopupService:(id<TTPopupServiceProtocol>)service;
/**
移除一个弹窗
*/
- (void)removePopupService;
@end

View File

@@ -1,22 +1,19 @@
//
// TTPopupService.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TTPopupServiceProtocol.h"
// TTPopupConfig 是 TTPopupService 的别称
// 具体信息见 TTPopupService
#define TTPopupConfig TTPopupService
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupService : NSObject<TTPopupServiceProtocol>
@end
NS_ASSUME_NONNULL_END
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import "TTPopupServiceProtocol.h"
#define TTPopupConfig TTPopupService
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupService : NSObject<TTPopupServiceProtocol>
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,22 @@
//
// TTPopupService.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TTPopupServiceProtocol.h"
// TTPopupConfig 是 TTPopupService 的别称
// 具体信息见 TTPopupService
#define TTPopupConfig TTPopupService
NS_ASSUME_NONNULL_BEGIN
@interface TTPopupService : NSObject<TTPopupServiceProtocol>
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTPopupService.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupService.h"

View File

@@ -0,0 +1,36 @@
//
// TTPopupService.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupService.h"
@implementation TTPopupService
@synthesize style = _style;
@synthesize priority = _priority;
@synthesize contentView = _contentView;
@synthesize maskBackgroundAlpha = _maskBackgroundAlpha;
@synthesize shouldDismissOnBackgroundTouch = _shouldDismissOnBackgroundTouch;
@synthesize didFinishDismissHandler = _didFinishDismissHandler;
@synthesize didFinishShowingHandler = _didFinishShowingHandler;
@synthesize shouldFilterPopup = _shouldFilterPopup;
@synthesize filterIdentifier = _filterIdentifier;
@synthesize showType = _showType;
- (instancetype)init {
self = [super init];
if (self) {
_style = TTPopupStyleAlert;
_priority = TTPopupPriorityNormal;
_maskBackgroundAlpha = 0.5;
_shouldDismissOnBackgroundTouch = YES;
_shouldFilterPopup = NO;
_showType = TTPopupShowTypeDefault;
}
return self;
}
@end

View File

@@ -1,78 +1,47 @@
//
// TTPopupServiceProtocol.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupConstants.h"
typedef NS_ENUM(NSUInteger, TTPopupShowType) {
TTPopupShowType_FadeIn = 1,
TTPopupShowTypeDefault = 8,
};
@class UIView;
@protocol TTPopupServiceProtocol <NSObject>
/**
弹窗样式默认TTPopupStyleAlert
*/
@property (nonatomic, assign) TTPopupStyle style;
/**
弹窗优先级权重默认TTPopupPriorityNormal
@discussion 权重越高在弹窗队列的优先级越高,即优先弹出;相同权重按先来后到原则
*/
@property (nonatomic, assign) TTPopupPriority priority;
/**
自定义视图内容默认nil
@discussion 如果未配置,或 contentView 未继承自 UIView 及其子类,将忽略该弹窗
*/
@property (nonatomic, strong) UIView *contentView;
/**
背景蒙层透明度默认0x000000 0.3 alpha
@discussion 由于第三方原因,暂不支持蒙层颜色修改
*/
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
/**
点击蒙层是否消除弹窗默认YES
*/
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
/**
弹窗消失回调isDismissOnBackgroundTouch 区分是否点击蒙层触发
*/
@property (nonatomic, copy) void (^didFinishDismissHandler)(BOOL isDismissOnBackgroundTouch);
/**
弹窗显示成功回调
*/
@property (nonatomic, copy) void (^didFinishShowingHandler)(void);
/**
重复弹窗过滤默认NO
@discussion 设置过滤时,队列中将不会出现相同过滤标识的弹窗
过滤标识通过 #<filterIdentifier> 设置
*/
@property (nonatomic, assign) BOOL shouldFilterPopup;
/**
过滤标识默认nil
*/
@property (nonatomic, copy) NSString *filterIdentifier;
/**
显示动画类型, 默认是 default
*/
@property (nonatomic, assign) TTPopupShowType showType;
@end
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
#import "TTPopupConstants.h"
typedef NS_ENUM(NSUInteger, TTPopupShowType) {
TTPopupShowType_FadeIn = 1,
TTPopupShowTypeDefault = 8,
};
@class UIView;
@protocol TTPopupServiceProtocol <NSObject>
@property (nonatomic, assign) TTPopupStyle style;
@property (nonatomic, assign) TTPopupPriority priority;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
@property (nonatomic, copy) void (^didFinishDismissHandler)(BOOL isDismissOnBackgroundTouch);
@property (nonatomic, copy) void (^didFinishShowingHandler)(void);
@property (nonatomic, assign) BOOL shouldFilterPopup;
@property (nonatomic, copy) NSString *filterIdentifier;
@property (nonatomic, assign) TTPopupShowType showType;
@end

View File

@@ -0,0 +1,78 @@
//
// TTPopupServiceProtocol.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/21.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopupConstants.h"
typedef NS_ENUM(NSUInteger, TTPopupShowType) {
TTPopupShowType_FadeIn = 1,
TTPopupShowTypeDefault = 8,
};
@class UIView;
@protocol TTPopupServiceProtocol <NSObject>
/**
弹窗样式默认TTPopupStyleAlert
*/
@property (nonatomic, assign) TTPopupStyle style;
/**
弹窗优先级权重默认TTPopupPriorityNormal
@discussion 权重越高在弹窗队列的优先级越高,即优先弹出;相同权重按先来后到原则
*/
@property (nonatomic, assign) TTPopupPriority priority;
/**
自定义视图内容默认nil
@discussion 如果未配置,或 contentView 未继承自 UIView 及其子类,将忽略该弹窗
*/
@property (nonatomic, strong) UIView *contentView;
/**
背景蒙层透明度默认0x000000 0.3 alpha
@discussion 由于第三方原因,暂不支持蒙层颜色修改
*/
@property (nonatomic, assign) CGFloat maskBackgroundAlpha;
/**
点击蒙层是否消除弹窗默认YES
*/
@property (nonatomic, assign) BOOL shouldDismissOnBackgroundTouch;
/**
弹窗消失回调isDismissOnBackgroundTouch 区分是否点击蒙层触发
*/
@property (nonatomic, copy) void (^didFinishDismissHandler)(BOOL isDismissOnBackgroundTouch);
/**
弹窗显示成功回调
*/
@property (nonatomic, copy) void (^didFinishShowingHandler)(void);
/**
重复弹窗过滤默认NO
@discussion 设置过滤时,队列中将不会出现相同过滤标识的弹窗
过滤标识通过 #<filterIdentifier> 设置
*/
@property (nonatomic, assign) BOOL shouldFilterPopup;
/**
过滤标识默认nil
*/
@property (nonatomic, copy) NSString *filterIdentifier;
/**
显示动画类型, 默认是 default
*/
@property (nonatomic, assign) TTPopupShowType showType;
@end

View File

@@ -1,126 +1,78 @@
//
// TTPopup.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
// 弹窗工具类
#import <Foundation/Foundation.h>
#import "TTPopupConstants.h"
#import "TTAlertConfig.h"
#import "TTActionSheetConfig.h"
#import "TTPopupService.h"
NS_ASSUME_NONNULL_BEGIN
@class UIView;
@interface TTPopup : NSObject
#pragma mark Alert
/**
显示 alert 弹窗
@discussion 显示四个内容:默认标题‘提示’,提示内容,取消按钮,确认按钮
@param message 提示内容,不能为空(⊙o⊙)哦
@param confirmHandler 确认操作
@param cancelHandler 取消操作
*/
+ (void)alertWithMessage:(NSString *)message
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
/**
显示 alert 弹窗
@discussion 显示四个内容:默认标题‘提示’,提示内容,取消按钮,确认按钮
@param config 完善的视图配置,为您变态的需求保驾护航
@param isShowBorder 是否显示边框
@param confirmHandler 确认操作
@param cancelHandler 取消操作
*/
+ (void)alertWithConfig:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
+ (void)alertWithMessage:(NSString *)message
config:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
cancelHandler:(TTPopupCompletionHandler)cancelHandler
confirmHandler:(TTPopupCompletionHandler)confirmHandler;
/**
显示 alert 弹窗
@discussion 显示四个内容:标题,提示内容,取消按钮,确认按钮
@param config 完善的视图配置,为您变态的需求保驾护航
@param cancelHandler 取消操作
@param confirmHandler 确认操作
*/
+ (void)alertWithConfig:(TTAlertConfig *)config
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
#pragma mark Action Sheet
/**
显示 action sheet 弹窗,自带贴心的取消按钮😊
@param items 配置列表
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items;
/**
显示 action sheet 弹窗
@param items 配置列表
@param showCancelItem 是否显示取消按钮
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
showCancelItem:(BOOL)showCancelItem;
/**
显示 action sheet 弹窗
@param items 配置列表
@param cancelHandler 取消按钮回调
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
cancelHandler:(TTActionSheetClickAction)cancelHandler;
#pragma mark Popup
/**
显示自定义弹窗
@param customView 自定义 view
@param style 弹窗样式
*/
+ (void)popupView:(UIView *)customView
style:(TTPopupStyle)style;
/**
显示自定义弹窗
@param config 自定义弹窗配置
*/
+ (void)popupWithConfig:(TTPopupService *)config;
#pragma mark Dismiss
/**
消除当前弹窗
*/
+ (void)dismiss;
#pragma mark Query
/**
当前是否有显示弹窗
*/
+ (BOOL)hasShowPopup;
@end
NS_ASSUME_NONNULL_END
// Created by lvjunhang on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
#import <Foundation/Foundation.h>
#import "TTPopupConstants.h"
#import "TTAlertConfig.h"
#import "TTActionSheetConfig.h"
#import "TTPopupService.h"
NS_ASSUME_NONNULL_BEGIN
@class UIView;
@interface TTPopup : NSObject
#pragma mark Alert
+ (void)alertWithMessage:(NSString *)message
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
+ (void)alertWithConfig:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
+ (void)alertWithMessage:(NSString *)message
config:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
cancelHandler:(TTPopupCompletionHandler)cancelHandler
confirmHandler:(TTPopupCompletionHandler)confirmHandler;
+ (void)alertWithConfig:(TTAlertConfig *)config
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
#pragma mark Action Sheet
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items;
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
showCancelItem:(BOOL)showCancelItem;
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
cancelHandler:(TTActionSheetClickAction)cancelHandler;
#pragma mark Popup
+ (void)popupView:(UIView *)customView
style:(TTPopupStyle)style;
+ (void)popupWithConfig:(TTPopupService *)config;
#pragma mark Dismiss
+ (void)dismiss;
#pragma mark Query
+ (BOOL)hasShowPopup;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,126 @@
//
// TTPopup.h
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
// 弹窗工具类
#import <Foundation/Foundation.h>
#import "TTPopupConstants.h"
#import "TTAlertConfig.h"
#import "TTActionSheetConfig.h"
#import "TTPopupService.h"
NS_ASSUME_NONNULL_BEGIN
@class UIView;
@interface TTPopup : NSObject
#pragma mark Alert
/**
显示 alert 弹窗
@discussion 显示四个内容:默认标题‘提示’,提示内容,取消按钮,确认按钮
@param message 提示内容,不能为空(⊙o⊙)哦
@param confirmHandler 确认操作
@param cancelHandler 取消操作
*/
+ (void)alertWithMessage:(NSString *)message
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
/**
显示 alert 弹窗
@discussion 显示四个内容:默认标题‘提示’,提示内容,取消按钮,确认按钮
@param config 完善的视图配置,为您变态的需求保驾护航
@param isShowBorder 是否显示边框
@param confirmHandler 确认操作
@param cancelHandler 取消操作
*/
+ (void)alertWithConfig:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
+ (void)alertWithMessage:(NSString *)message
config:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
cancelHandler:(TTPopupCompletionHandler)cancelHandler
confirmHandler:(TTPopupCompletionHandler)confirmHandler;
/**
显示 alert 弹窗
@discussion 显示四个内容:标题,提示内容,取消按钮,确认按钮
@param config 完善的视图配置,为您变态的需求保驾护航
@param cancelHandler 取消操作
@param confirmHandler 确认操作
*/
+ (void)alertWithConfig:(TTAlertConfig *)config
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler;
#pragma mark Action Sheet
/**
显示 action sheet 弹窗,自带贴心的取消按钮😊
@param items 配置列表
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items;
/**
显示 action sheet 弹窗
@param items 配置列表
@param showCancelItem 是否显示取消按钮
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
showCancelItem:(BOOL)showCancelItem;
/**
显示 action sheet 弹窗
@param items 配置列表
@param cancelHandler 取消按钮回调
*/
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
cancelHandler:(TTActionSheetClickAction)cancelHandler;
#pragma mark Popup
/**
显示自定义弹窗
@param customView 自定义 view
@param style 弹窗样式
*/
+ (void)popupView:(UIView *)customView
style:(TTPopupStyle)style;
/**
显示自定义弹窗
@param config 自定义弹窗配置
*/
+ (void)popupWithConfig:(TTPopupService *)config;
#pragma mark Dismiss
/**
消除当前弹窗
*/
+ (void)dismiss;
#pragma mark Query
/**
当前是否有显示弹窗
*/
+ (BOOL)hasShowPopup;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTPopup.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopup.h"
#import "TTAlertView.h"
@@ -59,13 +57,13 @@ static CGFloat const kMaxHeight = 450.f;
NSAssert(NO, @" message can not be nil, 弹窗文案不可以为空");
return;
#else
// crash
config.message = @" ";
#endif
}
CGFloat width = [UIScreen mainScreen].bounds.size.width - 40 * 2;
CGFloat height = ([self messageSize:config.message width:width].height + 160);
// 200 450
if (height < kMixHeight) {
height = kMixHeight;
} else if (height > KScreenHeight - kSafeAreaTopHeight - kSafeAreaBottomHeight) {
@@ -77,7 +75,7 @@ static CGFloat const kMaxHeight = 450.f;
contentView.cancelAction = cancelHandler;
contentView.confirmAction = confirmHandler;
if (!contentView.config.disableAutoDismissWhenClickButton) {
//
contentView.dismissAction = ^{
[TTPopup dismiss];
};
@@ -115,7 +113,7 @@ static CGFloat const kMaxHeight = 450.f;
CGFloat width = [UIScreen mainScreen].bounds.size.width - 40 * 2;
CGFloat height = ([self messageSize:config.message width:width].height + 160);
// 200 450
if (height < kMixHeight) {
height = kMixHeight;
} else if (height > kMaxHeight) {
@@ -129,7 +127,7 @@ static CGFloat const kMaxHeight = 450.f;
contentView.isConfigBoard = isShowBorder;
if (!contentView.config.disableAutoDismissWhenClickButton) {
//
contentView.dismissAction = ^{
[TTPopup dismiss];
};
@@ -162,14 +160,14 @@ static CGFloat const kMaxHeight = 450.f;
CGFloat width = [UIScreen mainScreen].bounds.size.width - kActionSheetViewPadding * 2;
CGFloat height = kActionSheetViewCellHeight * items.count + kActionSheetViewBottomPadding;
//
if (showCancelItem) {
//
height += kActionSheetViewCancelViewHeight;
}
if (@available(iOS 11.0, *)) {
// iPhone X () 34
height += [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;
}
@@ -180,7 +178,7 @@ static CGFloat const kMaxHeight = 450.f;
items:items];
sheetView.cancelAction = cancelHandler;
//
sheetView.dismissAction = ^{
[TTPopup dismiss];
};
@@ -227,9 +225,8 @@ static CGFloat const kMaxHeight = 450.f;
}
#pragma mark Query
/**
*/
+ (BOOL)hasShowPopup {
return [TTPopupManagerService sharedInstance].currentPopupService != nil;
}

View File

@@ -0,0 +1,243 @@
//
// TTPopup.m
// YM_TTChatViewKit
//
// Created by lvjunhang on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTPopup.h"
#import "TTAlertView.h"
#import "TTActionSheetView.h"
#import "TTPopupService.h"
#import "TTPopupManagerService.h"
static CGFloat const kActionSheetViewPadding = 15.f;
static CGFloat const kActionSheetViewCellHeight = 52.f;
static CGFloat const kActionSheetViewCancelViewHeight = 67.f;
static CGFloat const kActionSheetViewBottomPadding = 15.f;
static CGFloat const kMixHeight = 200.f;
static CGFloat const kMaxHeight = 450.f;
@implementation TTPopup
#pragma mark - Public Methods
#pragma mark Alert
+ (void)alertWithMessage:(NSString *)message
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler {
[self alertWithMessage:message
config:nil
cancelHandler:cancelHandler
confirmHandler:confirmHandler];
}
+ (void)alertWithConfig:(TTAlertConfig *)config
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler {
[self alertWithMessage:nil
config:config
cancelHandler:cancelHandler
confirmHandler:confirmHandler];
}
+ (void)alertWithMessage:(NSString *)message
config:(TTAlertConfig *)config
cancelHandler:(TTPopupCompletionHandler)cancelHandler
confirmHandler:(TTPopupCompletionHandler)confirmHandler {
if (!config) {
config = [[TTAlertConfig alloc] init];
config.title = YMLocalizedString(@"UserDetail_CP_Toast_0");
config.message = message;
}
if (config.message.length <= 0 && config.messageAttributed.length <=0 && config.messageAttributedConfig.count <=0) {
#if DEBUG
NSAssert(NO, @" message can not be nil, 弹窗文案不可以为空");
return;
#else
// 防止正式环境 crash
config.message = @" ";
#endif
}
CGFloat width = [UIScreen mainScreen].bounds.size.width - 40 * 2;
CGFloat height = ([self messageSize:config.message width:width].height + 160);
// 最小 200 最大 450
if (height < kMixHeight) {
height = kMixHeight;
} else if (height > KScreenHeight - kSafeAreaTopHeight - kSafeAreaBottomHeight) {
height = KScreenHeight - kSafeAreaTopHeight - kSafeAreaBottomHeight;
}
TTAlertView *contentView = [[TTAlertView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
contentView.config = config;
contentView.isConfigBoard = NO;
contentView.cancelAction = cancelHandler;
contentView.confirmAction = confirmHandler;
if (!contentView.config.disableAutoDismissWhenClickButton) {
// 设置弹窗按钮自动消除
contentView.dismissAction = ^{
[TTPopup dismiss];
};
}
[self popupView:contentView style:TTPopupStyleAlert config:config];
}
+ (void)alertWithConfig:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
confirmHandler:(TTPopupCompletionHandler)confirmHandler
cancelHandler:(TTPopupCompletionHandler)cancelHandler {
[self alertWithMessage:@""
config:config
showBorder:isShowBorder
cancelHandler:cancelHandler
confirmHandler:confirmHandler];
}
+ (void)alertWithMessage:(NSString *)message
config:(TTAlertConfig *)config
showBorder:(BOOL)isShowBorder
cancelHandler:(TTPopupCompletionHandler)cancelHandler
confirmHandler:(TTPopupCompletionHandler)confirmHandler {
if (!config) {
config = [[TTAlertConfig alloc] init];
config.message = message;
}
if (config.message.length <= 0) {
NSAssert(NO, @" message can not be nil, 弹窗文案不可以为空");
return;
}
CGFloat width = [UIScreen mainScreen].bounds.size.width - 40 * 2;
CGFloat height = ([self messageSize:config.message width:width].height + 160);
// 最小 200 最大 450
if (height < kMixHeight) {
height = kMixHeight;
} else if (height > kMaxHeight) {
height = kMaxHeight;
}
TTAlertView *contentView = [[TTAlertView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
contentView.config = config;
contentView.cancelAction = cancelHandler;
contentView.confirmAction = confirmHandler;
contentView.isConfigBoard = isShowBorder;
if (!contentView.config.disableAutoDismissWhenClickButton) {
// 设置弹窗按钮自动消除
contentView.dismissAction = ^{
[TTPopup dismiss];
};
}
[self popupView:contentView style:TTPopupStyleAlert config:config];
}
#pragma mark Action Sheet
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items {
[TTPopup actionSheetWithItems:items showCancelItem:YES cancelHandler:nil];
}
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
showCancelItem:(BOOL)showCancelItem {
[TTPopup actionSheetWithItems:items showCancelItem:showCancelItem cancelHandler:nil];
}
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items cancelHandler:(TTActionSheetClickAction)cancelHandler {
[TTPopup actionSheetWithItems:items showCancelItem:YES cancelHandler:cancelHandler];
}
+ (void)actionSheetWithItems:(NSArray<TTActionSheetConfig *> *)items
showCancelItem:(BOOL)showCancelItem
cancelHandler:(TTActionSheetClickAction)cancelHandler {
CGFloat width = [UIScreen mainScreen].bounds.size.width - kActionSheetViewPadding * 2;
CGFloat height = kActionSheetViewCellHeight * items.count + kActionSheetViewBottomPadding;
// 如果需要显示取消按钮则增加响应的高度
if (showCancelItem) {
// 按钮的高度和间距
height += kActionSheetViewCancelViewHeight;
}
if (@available(iOS 11.0, *)) {
// 如果是 iPhone X 系列(刘海屏幕系列) 底部则需要添加 34 的高度
height += [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;
}
CGRect rect = CGRectMake(0, 0, width, height);
TTActionSheetView *sheetView = [[TTActionSheetView alloc] initWithFrame:rect
needCancel:showCancelItem
items:items];
sheetView.cancelAction = cancelHandler;
// 设置弹窗按钮自动消除
sheetView.dismissAction = ^{
[TTPopup dismiss];
};
[self popupView:sheetView style:TTPopupStyleActionSheet];
}
#pragma mark Popup
+ (void)popupView:(UIView *)customView
style:(TTPopupStyle)style {
TTPopupService *service = [[TTPopupService alloc] init];
service.style = style;
service.contentView = customView;
[self popupWithConfig:service];
}
+ (void)popupView:(UIView *)customView
style:(TTPopupStyle)style
config:(TTAlertConfig *)config {
TTPopupService *service = [[TTPopupService alloc] init];
service.style = style;
service.contentView = customView;
service.shouldDismissOnBackgroundTouch = config.shouldDismissOnBackgroundTouch;
service.maskBackgroundAlpha = config.maskBackgroundAlpha;
[self popupWithConfig:service];
}
+ (void)popupWithConfig:(TTPopupService *)config {
if (![config.contentView isKindOfClass:UIView.class]) {
NSAssert(NO, @"TTPopup customView should inherit from UIView.");
return;
}
[[TTPopupManagerService sharedInstance] addPopupService:config];
}
#pragma mark Dismiss
+ (void)dismiss {
[[TTPopupManagerService sharedInstance] removePopupService];
}
#pragma mark Query
/**
当前是否有显示弹窗
*/
+ (BOOL)hasShowPopup {
return [TTPopupManagerService sharedInstance].currentPopupService != nil;
}
#pragma mark - Privite
+ (CGSize)messageSize:(NSString *)text width:(CGFloat)width {
CGRect stringRect = [text boundingRectWithSize:CGSizeMake(width - 2 * 20, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15.f]} context:nil];
return stringRect.size;
}
@end

View File

@@ -1,26 +1,24 @@
//
// TTActionSheetView.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
NS_ASSUME_NONNULL_BEGIN
@class TTActionSheetConfig;
@interface TTActionSheetView : UIView
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
- (instancetype)initWithFrame:(CGRect)frame
needCancel:(BOOL)needCancel
items:(NSArray<TTActionSheetConfig *> *)items;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
NS_ASSUME_NONNULL_BEGIN
@class TTActionSheetConfig;
@interface TTActionSheetView : UIView
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
- (instancetype)initWithFrame:(CGRect)frame
needCancel:(BOOL)needCancel
items:(NSArray<TTActionSheetConfig *> *)items;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,26 @@
//
// TTActionSheetView.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
NS_ASSUME_NONNULL_BEGIN
@class TTActionSheetConfig;
@interface TTActionSheetView : UIView
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
- (instancetype)initWithFrame:(CGRect)frame
needCancel:(BOOL)needCancel
items:(NSArray<TTActionSheetConfig *> *)items;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTActionSheetView.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTActionSheetView.h"
#import "TTActionSheetConfig.h"
@@ -17,13 +15,13 @@ static NSString *const kSheetViewCellConst = @"kSheetViewCellConst";
@interface TTActionSheetView ()<UITableViewDelegate, UITableViewDataSource>
/** sheetView */
@property (nonatomic, strong) UITableView *tableView;
/** */
@property (nonatomic, strong) NSArray<TTActionSheetConfig *> *items;
/** */
@property (nonatomic, assign) BOOL needCancel;
/** */
@property (nonatomic, strong) UIButton *cancelButton;
@end
@@ -56,7 +54,7 @@ static NSString *const kSheetViewCellConst = @"kSheetViewCellConst";
}];
if (_needCancel) {
// cancel view
self.cancelButton.hidden = NO;
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.mas_equalTo(self);
@@ -111,7 +109,7 @@ static NSString *const kSheetViewCellConst = @"kSheetViewCellConst";
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//
TTActionSheetConfig *config = _items[indexPath.row];
!config.clickAction ?: config.clickAction();

View File

@@ -0,0 +1,159 @@
//
// TTActionSheetView.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/22.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTActionSheetView.h"
#import "TTActionSheetConfig.h"
#import "DJDKMIMOMColor.h"
#import <Masonry/Masonry.h>
static CGFloat const kSheetViewCellHeight = 51.f;
static CGFloat const kSheetViewCornerRadius = 14.f;
static NSString *const kSheetViewCellConst = @"kSheetViewCellConst";
@interface TTActionSheetView ()<UITableViewDelegate, UITableViewDataSource>
/** sheetView 载体 */
@property (nonatomic, strong) UITableView *tableView;
/** 数据源 */
@property (nonatomic, strong) NSArray<TTActionSheetConfig *> *items;
/** 是否需要显示取消按钮 */
@property (nonatomic, assign) BOOL needCancel;
/** 取消按钮 */
@property (nonatomic, strong) UIButton *cancelButton;
@end
@implementation TTActionSheetView
#pragma mark -
#pragma mark lifeCycle
- (instancetype)initWithFrame:(CGRect)frame needCancel:(BOOL)needCancel items:(NSArray<TTActionSheetConfig *> *)items {
self = [super initWithFrame:frame];
if (self) {
_items = items;
_needCancel = needCancel;
[self initViews];
[self initConstraints];
}
return self;
}
- (void)initViews {
[self addSubview:self.tableView];
[self addSubview:self.cancelButton];
}
- (void)initConstraints {
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.top.mas_equalTo(self);
make.height.mas_equalTo(self.items.count * kSheetViewCellHeight);
}];
if (_needCancel) {
// 显示 cancel view
self.cancelButton.hidden = NO;
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.mas_equalTo(self);
make.height.mas_equalTo(kSheetViewCellHeight);
make.top.mas_equalTo(self.tableView.mas_bottom).offset(15);
}];
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kSheetViewCellConst];
cell.backgroundColor = UIColor.clearColor;
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.textLabel.text = _items[indexPath.row].title;
cell.textLabel.textColor = _items[indexPath.row].titleColor;
if ([_items[indexPath.row] displayMoliCoin]) {
NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init];
coinAttachment.image = kImage(@"moli_money_icon");
coinAttachment.bounds = CGRectMake(0, -3.5, 20, 20);
NSAttributedString *coinAttributedString = [NSAttributedString attributedStringWithAttachment:coinAttachment];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:coinAttributedString];
NSAttributedString *titleAttributedString = [[NSAttributedString alloc] initWithString:_items[indexPath.row].title
attributes:@{
NSForegroundColorAttributeName: _items[indexPath.row].titleColor,
NSFontAttributeName: cell.textLabel.font
}];
[string insertAttributedString:titleAttributedString atIndex:0];
cell.textLabel.attributedText = string.copy;
UIImageView *questionMark = [[UIImageView alloc] initWithImage:kImage(@"question_mark")];
[cell.contentView addSubview:questionMark];
[questionMark mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(cell.textLabel);
make.trailing.mas_equalTo(-20);
make.size.mas_equalTo(CGSizeMake(22, 22));
}];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// 配置中的事件处理
TTActionSheetConfig *config = _items[indexPath.row];
!config.clickAction ?: config.clickAction();
!_dismissAction ?: _dismissAction();
}
- (void)onClickCancelButtonAction:(UIButton *)cancelButton {
!_cancelAction ?: _cancelAction();
!_dismissAction ?: _dismissAction();
}
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.separatorColor = [DJDKMIMOMColor actionSeparatorColor];
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
_tableView.rowHeight = kSheetViewCellHeight;
_tableView.tableFooterView = [[UIView alloc] init];
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.layer.cornerRadius = kSheetViewCornerRadius;
_tableView.layer.masksToBounds = YES;
_tableView.bounces = NO;
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kSheetViewCellConst];
}
return _tableView;
}
- (UIButton *)cancelButton {
if (!_cancelButton) {
_cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_cancelButton setTitle:YMLocalizedString(@"TTActionSheetView0") forState:UIControlStateNormal];
[_cancelButton setBackgroundColor:UIColor.whiteColor];
[_cancelButton setTitleColor:[DJDKMIMOMColor alertMessageColor] forState:UIControlStateNormal];
[_cancelButton.titleLabel setFont:[UIFont systemFontOfSize:16]];
_cancelButton.layer.cornerRadius = kSheetViewCornerRadius;
_cancelButton.layer.masksToBounds = YES;
[_cancelButton addTarget:self action:@selector(onClickCancelButtonAction:) forControlEvents:UIControlEventTouchUpInside];
_cancelButton.hidden = YES;
}
return _cancelButton;
}
@end

View File

@@ -1,25 +1,23 @@
//
// TTAlertView.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
@class TTAlertConfig;
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertView : UIView
@property (nonatomic, strong) TTAlertConfig *config;// 配置
@property (nonatomic, assign) BOOL isConfigBoard;// 是否配置边框
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler confirmAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
@end
NS_ASSUME_NONNULL_END
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
@class TTAlertConfig;
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertView : UIView
@property (nonatomic, strong) TTAlertConfig *config;
@property (nonatomic, assign) BOOL isConfigBoard;
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler confirmAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,25 @@
//
// TTAlertView.h
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TTPopupConstants.h"
@class TTAlertConfig;
NS_ASSUME_NONNULL_BEGIN
@interface TTAlertView : UIView
@property (nonatomic, strong) TTAlertConfig *config;// 配置
@property (nonatomic, assign) BOOL isConfigBoard;// 是否配置边框
@property (nonatomic, copy) TTPopupCompletionHandler cancelAction;
@property (nonatomic, copy) TTPopupCompletionHandler confirmAction;
@property (nonatomic, copy) TTPopupCompletionHandler dismissAction;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// TTAlertView.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertView.h"
#import "TTAlertConfig.h"
@@ -17,10 +15,10 @@ static CGFloat const kBtnHeight = 38.f;
@interface TTAlertView ()
@property (nonatomic, strong) UILabel *titleLabel; //
@property (nonatomic, strong) UILabel *messageLabel; //
@property (nonatomic, strong) UIButton *cancelButton; //
@property (nonatomic, strong) UIButton *confirmButton; //
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *messageLabel;
@property (nonatomic, strong) UIButton *cancelButton;
@property (nonatomic, strong) UIButton *confirmButton;
@property (nonatomic, strong) UIStackView *stackView;
@end
@@ -86,27 +84,24 @@ static CGFloat const kBtnHeight = 38.f;
}
#pragma mark - private method
/**
messageLabel
@param config
@return
*/
- (NSMutableAttributedString *)messageAttributeString:(TTAlertConfig *)config {
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:config.message];
if (config.messageLineSpacing > 0) { //
if (config.messageLineSpacing > 0) {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = config.messageLineSpacing;
paragraphStyle.alignment = config.messageTextAlignment;
[attString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, config.message.length)];
}
//
[config.messageAttributedConfig enumerateObjectsUsingBlock:^(TTAlertMessageAttributedConfig * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//
if ([obj isKindOfClass:[TTAlertMessageAttributedConfig class]]) {
if (obj.text && obj.text.length > 0) {
NSRange range = [config.message rangeOfString:obj.text];
// range range
if (obj.range.length != 0) {
if (obj.range.location + obj.range.length > config.message.length) {
NSAssert(NO, @"obj.range out of bounds");
@@ -114,10 +109,10 @@ static CGFloat const kBtnHeight = 38.f;
}
range = obj.range;
}
if (obj.font) { //
if (obj.font) {
[attString addAttribute:NSFontAttributeName value:obj.font range:range];
}
if (obj.color) { //
if (obj.color) {
[attString addAttribute:NSForegroundColorAttributeName value:obj.color range:range];
}
}
@@ -130,14 +125,14 @@ static CGFloat const kBtnHeight = 38.f;
- (void)setConfig:(TTAlertConfig *)config {
_config = config;
// cornerRadius
if (config.cornerRadius > 0) {
self.layer.cornerRadius = config.cornerRadius;
self.layer.masksToBounds = YES;
}
//
self.backgroundColor = config.backgroundColor;
// title
_titleLabel.text = config.title;
_titleLabel.textColor = config.titleColor;
_titleLabel.font = config.titleFont;
@@ -145,7 +140,7 @@ static CGFloat const kBtnHeight = 38.f;
_cancelButton.hidden = config.actionStyle == TTAlertActionConfirmStyle;
_confirmButton.hidden = config.actionStyle == TTAlertActionCancelStyle;
// cancel button
[_cancelButton setTitle:config.cancelButtonConfig.title forState:UIControlStateNormal];
[_cancelButton setTitleColor:config.cancelButtonConfig.titleColor forState:UIControlStateNormal];
[_cancelButton.titleLabel setFont:config.cancelButtonConfig.font];
@@ -156,7 +151,7 @@ static CGFloat const kBtnHeight = 38.f;
_cancelButton.layer.masksToBounds = YES;
}
// confirm button
[_confirmButton setTitle:config.confirmButtonConfig.title forState:UIControlStateNormal];
[_confirmButton setTitleColor:config.confirmButtonConfig.titleColor forState:UIControlStateNormal];
[_confirmButton.titleLabel setFont:config.confirmButtonConfig.font];
@@ -167,7 +162,7 @@ static CGFloat const kBtnHeight = 38.f;
_confirmButton.layer.masksToBounds = YES;
}
// message
_messageLabel.font = config.messageFont;
_messageLabel.textColor = config.messageColor;
@@ -191,13 +186,13 @@ static CGFloat const kBtnHeight = 38.f;
- (void)setIsConfigBoard:(BOOL)isConfigBoard {
_isConfigBoard = isConfigBoard;
if (isConfigBoard) {
//
_cancelButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_cancelButton.layer.borderWidth = 2.f;
_confirmButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_confirmButton.layer.borderWidth = 2.f;
}else {
//
_cancelButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_cancelButton.layer.borderWidth = 0;
_confirmButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;

View File

@@ -0,0 +1,255 @@
//
// TTAlertView.m
// YM_TTChatViewKit
//
// Created by lee on 2019/5/20.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "TTAlertView.h"
#import "TTAlertConfig.h"
#import "DJDKMIMOMColor.h"
#import <Masonry/Masonry.h>
static CGFloat const kMargin = 25.f;
static CGFloat const kPadding = 20.f;
static CGFloat const kBtnHeight = 38.f;
@interface TTAlertView ()
@property (nonatomic, strong) UILabel *titleLabel; // 标题
@property (nonatomic, strong) UILabel *messageLabel; // 内容
@property (nonatomic, strong) UIButton *cancelButton; // 取消按钮
@property (nonatomic, strong) UIButton *confirmButton; // 确认按钮
@property (nonatomic, strong) UIStackView *stackView;
@end
@implementation TTAlertView
#pragma mark - lifeCyle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initViews];
[self initConstraints];
}
return self;
}
- (void)initViews {
[self addSubview:self.titleLabel];
[self addSubview:self.messageLabel];
[self addSubview:self.stackView];
[self.stackView addSubview:self.cancelButton];
[self.stackView addSubview:self.confirmButton];
}
- (void)initConstraints {
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(kPadding);
make.leading.trailing.mas_equalTo(self).inset(kPadding);
}];
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.titleLabel.mas_bottom).offset(kMargin);
make.leading.trailing.mas_equalTo(self).inset(kPadding);
make.bottom.mas_equalTo(self).offset(-kBtnHeight * 2);
}];
[self.stackView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(-kPadding);
make.centerX.mas_equalTo(self);
}];
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(kBtnHeight);
make.width.mas_equalTo(self.mas_width).multipliedBy(0.4);
}];
[self.confirmButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(kBtnHeight);
make.width.mas_equalTo(self.mas_width).multipliedBy(0.4);
}];
}
#pragma mark - Button Events
- (void)onClickConfirmButtonAction:(UIButton *)confirmButton {
!_confirmAction ?: _confirmAction();
!_dismissAction ?: _dismissAction();
}
- (void)onClickCancelButtonAction:(UIButton *)cancelButton {
!_cancelAction ?: _cancelAction();
!_dismissAction ?: _dismissAction();
}
#pragma mark - private method
/**
设置 messageLabel 需要显示的富文本效果
@param config 弹窗配置
@return 富文本内容
*/
- (NSMutableAttributedString *)messageAttributeString:(TTAlertConfig *)config {
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:config.message];
if (config.messageLineSpacing > 0) { // 行间距
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = config.messageLineSpacing;
paragraphStyle.alignment = config.messageTextAlignment;
[attString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, config.message.length)];
}
// 富文本显示效果
[config.messageAttributedConfig enumerateObjectsUsingBlock:^(TTAlertMessageAttributedConfig * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 遍历数组,添加展示富文本效果
if ([obj isKindOfClass:[TTAlertMessageAttributedConfig class]]) {
if (obj.text && obj.text.length > 0) {
NSRange range = [config.message rangeOfString:obj.text];
// 如果外部指定了 range 。就不用查找出的 range
if (obj.range.length != 0) {
if (obj.range.location + obj.range.length > config.message.length) {
NSAssert(NO, @"obj.range out of bounds");
return;
}
range = obj.range;
}
if (obj.font) { // 字体
[attString addAttribute:NSFontAttributeName value:obj.font range:range];
}
if (obj.color) { // 颜色
[attString addAttribute:NSForegroundColorAttributeName value:obj.color range:range];
}
}
}
}];
return attString;
}
#pragma mark - getter && setter
- (void)setConfig:(TTAlertConfig *)config {
_config = config;
// cornerRadius
if (config.cornerRadius > 0) {
self.layer.cornerRadius = config.cornerRadius;
self.layer.masksToBounds = YES;
}
//背景
self.backgroundColor = config.backgroundColor;
// title
_titleLabel.text = config.title;
_titleLabel.textColor = config.titleColor;
_titleLabel.font = config.titleFont;
_cancelButton.hidden = config.actionStyle == TTAlertActionConfirmStyle;
_confirmButton.hidden = config.actionStyle == TTAlertActionCancelStyle;
// cancel button
[_cancelButton setTitle:config.cancelButtonConfig.title forState:UIControlStateNormal];
[_cancelButton setTitleColor:config.cancelButtonConfig.titleColor forState:UIControlStateNormal];
[_cancelButton.titleLabel setFont:config.cancelButtonConfig.font];
[_cancelButton setBackgroundColor:config.cancelButtonConfig.backgroundColor];
[_cancelButton setBackgroundImage:config.cancelButtonConfig.backgroundImage forState:UIControlStateNormal];
if (config.cancelButtonConfig.cornerRadius > 0) {
_cancelButton.layer.cornerRadius = config.cancelButtonConfig.cornerRadius;
_cancelButton.layer.masksToBounds = YES;
}
// confirm button
[_confirmButton setTitle:config.confirmButtonConfig.title forState:UIControlStateNormal];
[_confirmButton setTitleColor:config.confirmButtonConfig.titleColor forState:UIControlStateNormal];
[_confirmButton.titleLabel setFont:config.confirmButtonConfig.font];
[_confirmButton setBackgroundColor:config.confirmButtonConfig.backgroundColor];
[_confirmButton setBackgroundImage:config.confirmButtonConfig.backgroundImage forState:UIControlStateNormal];
if (config.confirmButtonConfig.cornerRadius > 0) {
_confirmButton.layer.cornerRadius = config.confirmButtonConfig.cornerRadius;
_confirmButton.layer.masksToBounds = YES;
}
// message
_messageLabel.font = config.messageFont;
_messageLabel.textColor = config.messageColor;
if (config.messageAttributedConfig.count > 0) {
_messageLabel.attributedText = [self messageAttributeString:config];
_messageLabel.textAlignment = NSTextAlignmentCenter;
} else if(config.messageAttributed.length > 0) {
_messageLabel.attributedText = config.messageAttributed;
_messageLabel.textAlignment = NSTextAlignmentCenter;
} else {
_messageLabel.text = config.message;
_messageLabel.textAlignment = config.messageTextAlignment;
}
_cancelButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_cancelButton.layer.borderWidth = 2.f;
_confirmButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_confirmButton.layer.borderWidth = 2.f;
}
- (void)setIsConfigBoard:(BOOL)isConfigBoard {
_isConfigBoard = isConfigBoard;
if (isConfigBoard) {
//需要边框
_cancelButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_cancelButton.layer.borderWidth = 2.f;
_confirmButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_confirmButton.layer.borderWidth = 2.f;
}else {
//不需要边框
_cancelButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_cancelButton.layer.borderWidth = 0;
_confirmButton.layer.borderColor = [DJDKMIMOMColor dividerColor].CGColor;
_confirmButton.layer.borderWidth = 0;
}
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.numberOfLines = 0;
}
return _titleLabel ;
}
- (UILabel *)messageLabel {
if (!_messageLabel) {
_messageLabel = [[UILabel alloc] init];
_messageLabel.numberOfLines = 0;
_messageLabel.textAlignment = NSTextAlignmentCenter;
_messageLabel.minimumScaleFactor = 0.7;
_messageLabel.adjustsFontSizeToFitWidth = YES;
}
return _messageLabel;
}
- (UIButton *)cancelButton {
if (!_cancelButton) {
_cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_cancelButton addTarget:self action:@selector(onClickCancelButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _cancelButton;
}
- (UIButton *)confirmButton {
if (!_confirmButton) {
_confirmButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_confirmButton addTarget:self action:@selector(onClickConfirmButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _confirmButton;
}
- (UIStackView *)stackView {
if (!_stackView) {
_stackView = [[UIStackView alloc] initWithArrangedSubviews:@[self.cancelButton, self.confirmButton]];
_stackView.distribution = UIStackViewDistributionFillEqually;
_stackView.alignment = UIStackViewAlignmentCenter;
_stackView.spacing = 16;
}
return _stackView;
}
@end

View File

@@ -1,17 +1,13 @@
//
// TTNewAlertView.h
// xplan-ios
//
// Created by duoban on 2023/1/9.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTNewAlertView : UIView
@property (nonatomic,copy) NSString *message;
@property (nonatomic, copy) TTPopupCompletionHandler confirmHandle;

View File

@@ -0,0 +1,20 @@
//
// TTNewAlertView.h
// xplan-ios
//
// Created by duoban on 2023/1/9.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TTNewAlertView : UIView
@property (nonatomic,copy) NSString *message;
@property (nonatomic, copy) TTPopupCompletionHandler confirmHandle;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,9 +1,7 @@
//
// TTNewAlertView.m
// xplan-ios
//
// Created by duoban on 2023/1/9.
//
#import "TTNewAlertView.h"
@interface TTNewAlertView()
@@ -58,7 +56,6 @@
}];
}
-(void)setMessage:(NSString *)message{
_message = message;
@@ -86,7 +83,7 @@
}
- (UILabel *)messageView{
if (!_messageView){
_messageView = [UILabel labelInitWithText:@"" font:kFontRegular(15) textColor:[DJDKMIMOMColor inputTextColor]];
_messageView = [[UILabel alloc] init];
_messageView.textAlignment = NSTextAlignmentCenter;
_messageView.numberOfLines = 0;
}

View File

@@ -0,0 +1,118 @@
//
// TTNewAlertView.m
// xplan-ios
//
// Created by duoban on 2023/1/9.
//
#import "TTNewAlertView.h"
@interface TTNewAlertView()
@property (nonatomic,strong) UIView *bgView;
@property (nonatomic,strong) UILabel *messageView;
@property (nonatomic,strong) UIButton *confirmBtn;
@property (nonatomic,strong) UIButton *cancelBtn;
@end
@implementation TTNewAlertView
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
[self initSubViews];
[self initSubViewConstraints];
}
return self;
}
#pragma mark - Private Method
- (void)initSubViews {
self.backgroundColor = [UIColor clearColor];
[self addSubview:self.bgView];
[self.bgView addSubview:self.messageView];
[self.bgView addSubview:self.confirmBtn];
[self.bgView addSubview:self.cancelBtn];
}
- (void)initSubViewConstraints {
[self.bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(kGetScaleWidth(310));
make.height.mas_equalTo(kGetScaleWidth(149));
make.center.equalTo(self);
}];
[self.confirmBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(kGetScaleWidth(110));
make.height.mas_equalTo(kGetScaleWidth(37));
make.leading.mas_equalTo(kGetScaleWidth(31));
make.bottom.mas_equalTo(-kGetScaleWidth(31));
}];
[self.cancelBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.centerY.equalTo(self.confirmBtn);
make.trailing.mas_equalTo(-kGetScaleWidth(31));
}];
[self.messageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(kGetScaleWidth(25));
make.centerX.equalTo(self);
make.leading.trailing.equalTo(self).inset(kGetScaleWidth(10));
}];
}
-(void)setMessage:(NSString *)message{
_message = message;
_messageView.text = message;
}
-(void)confirmAction{
[TTPopup dismiss];
if(self.confirmHandle){
self.confirmHandle();
}
}
-(void)cancelAction{
[TTPopup dismiss];
}
#pragma mark -懒加载
- (UIView *)bgView{
if (!_bgView){
_bgView = [UIView new];
_bgView.backgroundColor = [UIColor whiteColor];
[_bgView setCornerWithLeftTopCorner:kGetScaleWidth(12) rightTopCorner:kGetScaleWidth(12) bottomLeftCorner:kGetScaleWidth(12) bottomRightCorner:kGetScaleWidth(12) size:CGSizeMake(kGetScaleWidth(310), kGetScaleWidth(149))];
}
return _bgView;
}
- (UILabel *)messageView{
if (!_messageView){
_messageView = [UILabel labelInitWithText:@"" font:kFontRegular(15) textColor:[DJDKMIMOMColor inputTextColor]];
_messageView.textAlignment = NSTextAlignmentCenter;
_messageView.numberOfLines = 0;
}
return _messageView;
}
-(UIButton *)confirmBtn{
if (!_confirmBtn){
_confirmBtn = [UIButton new];
[_confirmBtn setTitle:YMLocalizedString(@"TTAlertConfig0") forState:UIControlStateNormal];
_confirmBtn.backgroundColor = UIColorFromRGB(0xE6E6F0);
_confirmBtn.layer.cornerRadius = kGetScaleWidth(37)/2;
_confirmBtn.layer.masksToBounds = YES;
[_confirmBtn addTarget:self action:@selector(confirmAction) forControlEvents:UIControlEventTouchUpInside];
}
return _confirmBtn;
}
-(UIButton *)cancelBtn{
if (!_cancelBtn){
_cancelBtn = [UIButton new];
[_cancelBtn setTitle:YMLocalizedString(@"XPShareView7") forState:UIControlStateNormal];
UIImage *image = [UIImage gradientColorImageFromColors:@[[DJDKMIMOMColor confirmButtonGradientStartColor],[DJDKMIMOMColor confirmButtonGradientMiddleColor],[DJDKMIMOMColor confirmButtonGradientEndColor]] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(kGetScaleWidth(110), kGetScaleWidth(37))];
_cancelBtn.backgroundColor = [UIColor colorWithPatternImage:image];
_cancelBtn.layer.cornerRadius = kGetScaleWidth(37)/2;
_cancelBtn.layer.masksToBounds = YES;
[_cancelBtn addTarget:self action:@selector(cancelAction) forControlEvents:UIControlEventTouchUpInside];
}
return _cancelBtn;
}
@end

View File

@@ -1,65 +1,21 @@
//
// UIImage+Utils.h
// YYMobileFramework
//
// Created by wuwei on 14/6/20.
// Copyright (c) 2014年 YY Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, GradientType) {
GradientTypeTopToBottom = 0,//从上到小
GradientTypeLeftToRight = 1,//从左到右
GradientTypeUpleftToLowright = 2,//左上到右下
GradientTypeUprightToLowleft = 3,//右上到左下
};
@interface UIImage (Utils)
- (UIImage *)grayscaleImage;
- (UIImage *)imageBlendInGray;
- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode;
+ (UIImage *)imageWithColor:(UIColor *)color;
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
+ (UIImage *)fixOrientation:(UIImage *)aImage;
- (UIImage *)imageWithColor:(UIColor *)color;
- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size;
//异步生成纯色圆角图片
- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion;
/**
返回指定大小,颜色,渐变模式的渐变色图片
*/
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *>*)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize;
+ (UIImage *)waterImageWithImage:(UIImage *)image waterImage:(UIImage *)waterImage waterImageRect:(CGRect)rect;
+ (CGSize)sizeWithImageOriginSize:(CGSize)originSize
minSize:(CGSize)imageMinSize
maxSize:(CGSize)imageMaxSize;
///裁剪图片
- (UIImage *)cutImage:(CGSize)newSize;
- (UIImage *)cropRightAndBottomPixels:(NSUInteger)pixels;
-(UIImage *)compressWithMaxLength:(NSUInteger)maxLength;
- (UIImage *)roundedImageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)size;
+(UIImage *)getImageFromView:(UIView *)view;
+ (NSString *)getImageTypeWithImageData: (NSData *)data;
+(UIImage *)getLanguageImage:(NSString *)image;
+(NSString *)getLanguageText:(NSString *)image;
- (UIImage *)resizeTo:(CGSize)size;
- (UIImage *)imageByApplyingAlpha:(CGFloat)alpha;
@end
// Created by wuwei on 14/6/20.
// Copyright (c) 2014年 YY Inc. All rights reserved.
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, GradientType) {
GradientTypeTopToBottom = 0,
GradientTypeLeftToRight = 1,
GradientTypeUpleftToLowright = 2,
GradientTypeUprightToLowleft = 3,
};
@interface UIImage (Utils)
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *>*)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize;
+ (NSString *)getImageTypeWithImageData: (NSData *)data;
@end

View File

@@ -0,0 +1,65 @@
//
// UIImage+Utils.h
// YYMobileFramework
//
// Created by wuwei on 14/6/20.
// Copyright (c) 2014年 YY Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, GradientType) {
GradientTypeTopToBottom = 0,//从上到小
GradientTypeLeftToRight = 1,//从左到右
GradientTypeUpleftToLowright = 2,//左上到右下
GradientTypeUprightToLowleft = 3,//右上到左下
};
@interface UIImage (Utils)
- (UIImage *)grayscaleImage;
- (UIImage *)imageBlendInGray;
- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode;
+ (UIImage *)imageWithColor:(UIColor *)color;
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
+ (UIImage *)fixOrientation:(UIImage *)aImage;
- (UIImage *)imageWithColor:(UIColor *)color;
- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size;
//异步生成纯色圆角图片
- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion;
/**
返回指定大小,颜色,渐变模式的渐变色图片
*/
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *>*)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize;
+ (UIImage *)waterImageWithImage:(UIImage *)image waterImage:(UIImage *)waterImage waterImageRect:(CGRect)rect;
+ (CGSize)sizeWithImageOriginSize:(CGSize)originSize
minSize:(CGSize)imageMinSize
maxSize:(CGSize)imageMaxSize;
///裁剪图片
- (UIImage *)cutImage:(CGSize)newSize;
- (UIImage *)cropRightAndBottomPixels:(NSUInteger)pixels;
-(UIImage *)compressWithMaxLength:(NSUInteger)maxLength;
- (UIImage *)roundedImageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)size;
+(UIImage *)getImageFromView:(UIView *)view;
+ (NSString *)getImageTypeWithImageData: (NSData *)data;
+(UIImage *)getLanguageImage:(NSString *)image;
+(NSString *)getLanguageText:(NSString *)image;
- (UIImage *)resizeTo:(CGSize)size;
- (UIImage *)imageByApplyingAlpha:(CGFloat)alpha;
@end

View File

@@ -1,487 +1,13 @@
//
// UIImage+Utils.m
// YYMobileFramework
//
// Created by wuwei on 14/6/20.
// Copyright (c) 2014 YY Inc. All rights reserved.
//
#import "UIImage+Utils.h"
#import <ImageIO/ImageIO.h>
@implementation UIImage (Utils)
- (UIImage *)grayscaleImage
{
CGFloat width = self.size.width;
CGFloat height = self.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil,
width,
height,
8,
0,
colorSpace,
kCGImageAlphaNone);
// kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
if (context == NULL) {
return nil;
}
CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *grayscaleImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return grayscaleImage;
}
- (UIImage *)imageBlendInGray {
UIGraphicsBeginImageContext(self.size);
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextFillRect(context, bounds);
[self drawInRect:bounds blendMode:kCGBlendModeLuminosity alpha:1.0f];
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn) {
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
}
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
+ (UIImage *)imageWithColor:(UIColor *)color
{
return [self imageWithColor:color size:CGSizeMake(1, 1)];
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
if (!color || size.width <= 0 || size.height <= 0) return nil;
CGRect rect = CGRectMake(0.0f, 0.0f, size.width + 1, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (UIImage *)fixOrientation:(UIImage *)aImage {
// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
- (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
//
- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion {
//
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//
UIGraphicsBeginImageContextWithOptions(size, true, 0);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//
[backColor setFill];
UIRectFill(rect);
// //
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
// [path addClip];
// [self drawInRect:rect];
//
UIImage *resultImage = [UIGraphicsGetImageFromCurrentImageContext() circularImage];
//
UIGraphicsEndImageContext();
//
dispatch_async(dispatch_get_main_queue(), ^{
completion(resultImage);
});
});
}
- (UIImage *)circularImage {
// 1.
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0);
// 2.
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.size.width, self.size.height) byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(self.size.width, self.size.height)];
// UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// 3.
[path addClip];
// 4.
[self drawAtPoint:CGPointZero];
// 5.
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 6.
UIGraphicsEndImageContext();
// 7.
return image;
}
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *> *)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize{
NSMutableArray *ar = [NSMutableArray array];
for(UIColor *c in colors) {
[ar addObject:(id)c.CGColor];
}
UIGraphicsBeginImageContextWithOptions(imgSize, YES, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
CGPoint start;
CGPoint end;
switch (gradientType) {
case GradientTypeTopToBottom:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
case GradientTypeLeftToRight:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, 0.0);
break;
case GradientTypeUpleftToLowright:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, imgSize.height);
break;
case GradientTypeUprightToLowleft:
start = CGPointMake(imgSize.width, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
default:
break;
}
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size {
//
UIGraphicsBeginImageContext(size);
//
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
//Path
CGContextAddPath(UIGraphicsGetCurrentContext(), path.CGPath);
//
CGContextClip(UIGraphicsGetCurrentContext());
//
[self drawInRect:rect];
//
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathStroke);
//
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
//
UIGraphicsEndImageContext();
//
return output;
}
//
+ (UIImage *)waterImageWithImage:(UIImage *)image waterImage:(UIImage *)waterImage waterImageRect:(CGRect)rect
{
//1.
//2.
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//3.
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
//
[waterImage drawInRect:rect];
//4.
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.
UIGraphicsEndImageContext();
//
return newImage;
}
+ (CGSize)sizeWithImageOriginSize:(CGSize)originSize
minSize:(CGSize)imageMinSize
maxSize:(CGSize)imageMaxSiz {
CGSize size;
NSInteger imageWidth = originSize.width ,imageHeight = originSize.height;
NSInteger imageMinWidth = imageMinSize.width, imageMinHeight = imageMinSize.height;
NSInteger imageMaxWidth = imageMaxSiz.width, imageMaxHeight = imageMaxSiz.height;
if (imageWidth > imageHeight) //
{
size.height = imageMinHeight; //
size.width = imageWidth * imageMinHeight / imageHeight;
if (size.width > imageMaxWidth)
{
size.width = imageMaxWidth;
}
}
else if(imageWidth < imageHeight)//
{
size.width = imageMinWidth;
size.height = imageHeight *imageMinWidth / imageWidth;
if (size.height > imageMaxHeight){
size.height = imageMaxHeight;
}
}
else//
{
if (imageWidth > imageMaxWidth){
size.width = imageMaxWidth;
size.height = imageMaxHeight;
}else if(imageWidth > imageMinWidth){
size.width = imageWidth;
size.height = imageHeight;
}else{
size.width = imageMinWidth;
size.height = imageMinHeight;
}
}
return size;
}
- (UIImage *)cutImage:(CGSize)newSize{
CGFloat scale = newSize.height / self.size.height;
UIImage *scaleImage = [self originImage:self scaleToSize:CGSizeMake(self.size.width*scale, self.size.height*scale)];
//
return scaleImage;
}
- (UIImage *)resizeTo:(CGSize)size {
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
}];
}
- (UIImage *)cropRightAndBottomPixels:(NSUInteger)pixels {
//
CGSize originalSize = self.size;
//
CGSize newSize = CGSizeMake(originalSize.width - pixels, originalSize.height - pixels);
//
UIGraphicsBeginImageContextWithOptions(newSize, NO, self.scale);
//
[self drawAtPoint:CGPointZero];
//
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
//
UIGraphicsEndImageContext();
return croppedImage;
}
- (UIImage*) originImage:(UIImage *)image scaleToSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
-(UIImage *)compressWithMaxLength:(NSUInteger)maxLength{
// Compress by quality
CGFloat compression = 1;
NSData *data = UIImageJPEGRepresentation(self, compression);
if (data.length < maxLength) return self;
CGFloat max = 1;
CGFloat min = 0;
for (int i = 0; i < 6; ++i) {
compression = (max + min) / 2;
data = UIImageJPEGRepresentation(self, compression);
//NSLog(@"Compression = %.1f", compression);
//NSLog(@"In compressing quality loop, image size = %ld KB", data.length / 1024);
if (data.length < maxLength * 0.9) {
min = compression;
} else if (data.length > maxLength) {
max = compression;
} else {
break;
}
}
//NSLog(@"After compressing quality, image size = %ld KB", data.length / 1024);
if (data.length < maxLength) return self;
UIImage *resultImage = [UIImage imageWithData:data];
// Compress by size
NSUInteger lastDataLength = 0;
while (data.length > maxLength && data.length != lastDataLength) {
lastDataLength = data.length;
CGFloat ratio = (CGFloat)maxLength / data.length;
//NSLog(@"Ratio = %.1f", ratio);
CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
(NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank
UIGraphicsBeginImageContext(size);
[resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
data = UIImageJPEGRepresentation(resultImage, compression);
//NSLog(@"In compressing size loop, image size = %ld KB", data.length / 1024);
}
if (data) {
return [UIImage imageWithData:data];;
} else {
return self;
}
}
- (UIImage *)roundedImageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)size{
UIGraphicsBeginImageContextWithOptions(size, NO, 1);
UIBezierPath *clippingPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:cornerRadius];
[clippingPath addClip];
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return roundedImage;
}
+(UIImage *)getImageFromView:(UIView *)view {
// 1. viewbounds
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
// 2.
CGContextRef context = UIGraphicsGetCurrentContext();
// 3. view
[view.layer renderInContext:context];
// 4.
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 5.
UIGraphicsEndImageContext();
// 6.
return image;
}
+ (NSString *)getImageTypeWithImageData:(NSData *)data {
uint8_t c;
@@ -530,63 +56,49 @@
return nil;
}
+(UIImage *)getLanguageImage:(NSString *)image{
NSString *curImage = image;
NSString *language = [NSBundle getLanguageText];
if (isMSZH()) {
//
} else if (isMSTR()) {
image = [NSString stringWithFormat:@"%@_tr", image];
} else if (isMSRTL()) {
image = [NSString stringWithFormat:@"%@_ar", image];
} else {
image = [NSString stringWithFormat:@"%@_en", image];
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *> *)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize{
NSMutableArray *ar = [NSMutableArray array];
for(UIColor *c in colors) {
[ar addObject:(id)c.CGColor];
}
//
UIImage *getImage = kImage(image);
// 使
if (getImage == nil) {
NSString *defaultImageName = [NSString stringWithFormat:@"%@_en", curImage];
getImage = kImage(defaultImageName) ?: kImage(curImage);
UIGraphicsBeginImageContextWithOptions(imgSize, YES, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
CGPoint start;
CGPoint end;
switch (gradientType) {
case GradientTypeTopToBottom:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
case GradientTypeLeftToRight:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, 0.0);
break;
case GradientTypeUpleftToLowright:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, imgSize.height);
break;
case GradientTypeUprightToLowleft:
start = CGPointMake(imgSize.width, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
default:
break;
}
return getImage;
}
+(NSString *)getLanguageText:(NSString *)image{
NSString *curImage = image;
NSString *language = [NSBundle getLanguageText];
if ([language isEqualToString:@"en"]){
image = [NSString stringWithFormat:@"%@_en",image];
} else if ([language isEqualToString:@"ar"]){
image = [NSString stringWithFormat:@"%@_ar",image];
} else if ([language isEqualToString:@"tr"]) { // 使
image = [NSString stringWithFormat:@"%@_en",image];
}
if (kImage(image) == nil){
return curImage;
}
return image;
}
- (UIImage *)imageByApplyingAlpha:(CGFloat)alpha {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextSetAlpha(ctx, alpha);
CGContextDrawImage(ctx, area, self.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return newImage;
return image;
}
@end

View File

@@ -0,0 +1,592 @@
//
// UIImage+Utils.m
// YYMobileFramework
//
// Created by wuwei on 14/6/20.
// Copyright (c) 2014年 YY Inc. All rights reserved.
//
#import "UIImage+Utils.h"
#import <ImageIO/ImageIO.h>
@implementation UIImage (Utils)
- (UIImage *)grayscaleImage
{
CGFloat width = self.size.width;
CGFloat height = self.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil,
width,
height,
8,
0,
colorSpace,
kCGImageAlphaNone);
// kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
if (context == NULL) {
return nil;
}
CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *grayscaleImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return grayscaleImage;
}
- (UIImage *)imageBlendInGray {
UIGraphicsBeginImageContext(self.size);
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextFillRect(context, bounds);
[self drawInRect:bounds blendMode:kCGBlendModeLuminosity alpha:1.0f];
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn) {
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
}
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
+ (UIImage *)imageWithColor:(UIColor *)color
{
return [self imageWithColor:color size:CGSizeMake(1, 1)];
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
if (!color || size.width <= 0 || size.height <= 0) return nil;
CGRect rect = CGRectMake(0.0f, 0.0f, size.width + 1, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (UIImage *)fixOrientation:(UIImage *)aImage {
// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
- (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
//异步生成纯色圆角图片
- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion {
// 异步绘制裁切
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 利用绘图建立上下文
UIGraphicsBeginImageContextWithOptions(size, true, 0);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// 填充颜色
[backColor setFill];
UIRectFill(rect);
// // 贝塞尔裁切
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
// [path addClip];
// [self drawInRect:rect];
// 获取结果
UIImage *resultImage = [UIGraphicsGetImageFromCurrentImageContext() circularImage];
// 关闭上下文
UIGraphicsEndImageContext();
// 主队列回调
dispatch_async(dispatch_get_main_queue(), ^{
completion(resultImage);
});
});
}
- (UIImage *)circularImage {
// 1. 开启图形上下文
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0);
// 2. 描述路径
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.size.width, self.size.height) byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(self.size.width, self.size.height)];
// UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// 3. 添加裁减区域
[path addClip];
// 4. 绘制图片
[self drawAtPoint:CGPointZero];
// 5. 从上下文获取图片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 6. 关闭上下文
UIGraphicsEndImageContext();
// 7. 设置图片
return image;
}
+ (UIImage *)gradientColorImageFromColors:(NSArray<UIColor *> *)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize{
NSMutableArray *ar = [NSMutableArray array];
for(UIColor *c in colors) {
[ar addObject:(id)c.CGColor];
}
UIGraphicsBeginImageContextWithOptions(imgSize, YES, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
CGPoint start;
CGPoint end;
switch (gradientType) {
case GradientTypeTopToBottom:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
case GradientTypeLeftToRight:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, 0.0);
break;
case GradientTypeUpleftToLowright:
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imgSize.width, imgSize.height);
break;
case GradientTypeUprightToLowleft:
start = CGPointMake(imgSize.width, 0.0);
end = CGPointMake(0.0, imgSize.height);
break;
default:
break;
}
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size {
//开启图形上下文
UIGraphicsBeginImageContext(size);
//绘制圆角矩形
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
//将Path添加到上下文中
CGContextAddPath(UIGraphicsGetCurrentContext(), path.CGPath);
//裁剪上下文
CGContextClip(UIGraphicsGetCurrentContext());
//将图片绘制到上下文中
[self drawInRect:rect];
//设置绘制模式
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathStroke);
//获取图片
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
//返回裁剪好的图片
return output;
}
// 给图片添加图片水印
+ (UIImage *)waterImageWithImage:(UIImage *)image waterImage:(UIImage *)waterImage waterImageRect:(CGRect)rect
{
//1.获取图片
//2.开启上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//3.绘制背景图片
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
//绘制水印图片到当前上下文
[waterImage drawInRect:rect];
//4.从上下文中获取新图片
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭图形上下文
UIGraphicsEndImageContext();
//返回图片
return newImage;
}
+ (CGSize)sizeWithImageOriginSize:(CGSize)originSize
minSize:(CGSize)imageMinSize
maxSize:(CGSize)imageMaxSiz {
CGSize size;
NSInteger imageWidth = originSize.width ,imageHeight = originSize.height;
NSInteger imageMinWidth = imageMinSize.width, imageMinHeight = imageMinSize.height;
NSInteger imageMaxWidth = imageMaxSiz.width, imageMaxHeight = imageMaxSiz.height;
if (imageWidth > imageHeight) //宽图
{
size.height = imageMinHeight; //高度取最小高度
size.width = imageWidth * imageMinHeight / imageHeight;
if (size.width > imageMaxWidth)
{
size.width = imageMaxWidth;
}
}
else if(imageWidth < imageHeight)//高图
{
size.width = imageMinWidth;
size.height = imageHeight *imageMinWidth / imageWidth;
if (size.height > imageMaxHeight){
size.height = imageMaxHeight;
}
}
else//方图
{
if (imageWidth > imageMaxWidth){
size.width = imageMaxWidth;
size.height = imageMaxHeight;
}else if(imageWidth > imageMinWidth){
size.width = imageWidth;
size.height = imageHeight;
}else{
size.width = imageMinWidth;
size.height = imageMinHeight;
}
}
return size;
}
- (UIImage *)cutImage:(CGSize)newSize{
CGFloat scale = newSize.height / self.size.height;
UIImage *scaleImage = [self originImage:self scaleToSize:CGSizeMake(self.size.width*scale, self.size.height*scale)];
//裁剪暂时有问题
return scaleImage;
}
- (UIImage *)resizeTo:(CGSize)size {
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
}];
}
- (UIImage *)cropRightAndBottomPixels:(NSUInteger)pixels {
// 获取原图像的大小
CGSize originalSize = self.size;
// 计算新的裁剪后的图像大小
CGSize newSize = CGSizeMake(originalSize.width - pixels, originalSize.height - pixels);
// 开始图像上下文
UIGraphicsBeginImageContextWithOptions(newSize, NO, self.scale);
// 绘制裁剪后的图像到上下文
[self drawAtPoint:CGPointZero];
// 获取裁剪后的图像
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
// 结束图像上下文
UIGraphicsEndImageContext();
return croppedImage;
}
- (UIImage*) originImage:(UIImage *)image scaleToSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
-(UIImage *)compressWithMaxLength:(NSUInteger)maxLength{
// Compress by quality
CGFloat compression = 1;
NSData *data = UIImageJPEGRepresentation(self, compression);
if (data.length < maxLength) return self;
CGFloat max = 1;
CGFloat min = 0;
for (int i = 0; i < 6; ++i) {
compression = (max + min) / 2;
data = UIImageJPEGRepresentation(self, compression);
//NSLog(@"Compression = %.1f", compression);
//NSLog(@"In compressing quality loop, image size = %ld KB", data.length / 1024);
if (data.length < maxLength * 0.9) {
min = compression;
} else if (data.length > maxLength) {
max = compression;
} else {
break;
}
}
//NSLog(@"After compressing quality, image size = %ld KB", data.length / 1024);
if (data.length < maxLength) return self;
UIImage *resultImage = [UIImage imageWithData:data];
// Compress by size
NSUInteger lastDataLength = 0;
while (data.length > maxLength && data.length != lastDataLength) {
lastDataLength = data.length;
CGFloat ratio = (CGFloat)maxLength / data.length;
//NSLog(@"Ratio = %.1f", ratio);
CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
(NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank
UIGraphicsBeginImageContext(size);
[resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
data = UIImageJPEGRepresentation(resultImage, compression);
//NSLog(@"In compressing size loop, image size = %ld KB", data.length / 1024);
}
if (data) {
return [UIImage imageWithData:data];;
} else {
return self;
}
}
- (UIImage *)roundedImageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)size{
UIGraphicsBeginImageContextWithOptions(size, NO, 1);
UIBezierPath *clippingPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:cornerRadius];
[clippingPath addClip];
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return roundedImage;
}
+(UIImage *)getImageFromView:(UIView *)view {
// 1. 创建一个新的图像上下文大小与view的bounds相匹配
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
// 2. 获取当前图像上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 3. 将view的层级渲染到上下文中
[view.layer renderInContext:context];
// 4. 从上下文中获取生成的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 5. 结束图像上下文
UIGraphicsEndImageContext();
// 6. 返回生成的图片
return image;
}
+ (NSString *)getImageTypeWithImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"jpeg";
case 0x89:
return @"png";
case 0x47:
return @"gif";
case 0x49:
case 0x4D:
return @"tiff";
case 0x52:
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"webp";
}
return nil;
}
return nil;
}
+(UIImage *)getLanguageImage:(NSString *)image{
NSString *curImage = image;
NSString *language = [NSBundle getLanguageText];
if (isMSZH()) {
// 不處理
} else if (isMSTR()) {
image = [NSString stringWithFormat:@"%@_tr", image];
} else if (isMSRTL()) {
image = [NSString stringWithFormat:@"%@_ar", image];
} else {
image = [NSString stringWithFormat:@"%@_en", image];
}
// 尝试获取带语言后缀的图片
UIImage *getImage = kImage(image);
// 若图片不存在,尝试使用英语图片作为默认
if (getImage == nil) {
NSString *defaultImageName = [NSString stringWithFormat:@"%@_en", curImage];
getImage = kImage(defaultImageName) ?: kImage(curImage);
}
return getImage;
}
+(NSString *)getLanguageText:(NSString *)image{
NSString *curImage = image;
NSString *language = [NSBundle getLanguageText];
if ([language isEqualToString:@"en"]){
image = [NSString stringWithFormat:@"%@_en",image];
} else if ([language isEqualToString:@"ar"]){
image = [NSString stringWithFormat:@"%@_ar",image];
} else if ([language isEqualToString:@"tr"]) { // 土耳其语默认使用英语内容
image = [NSString stringWithFormat:@"%@_en",image];
}
if (kImage(image) == nil){
return curImage;
}
return image;
}
- (UIImage *)imageByApplyingAlpha:(CGFloat)alpha {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextSetAlpha(ctx, alpha);
CGContextDrawImage(ctx, area, self.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end

View File

@@ -1,9 +1,7 @@
//
// NetImageConfig.h
// YUMI
//
// Created by zu on 2021/11/25.
//
#import <Foundation/Foundation.h>
#import "UIImageConstant.h"

View File

@@ -0,0 +1,22 @@
//
// NetImageConfig.h
// YUMI
//
// Created by zu on 2021/11/25.
//
#import <Foundation/Foundation.h>
#import "UIImageConstant.h"
NS_ASSUME_NONNULL_BEGIN
@interface NetImageConfig : NSObject
@property (nonatomic, assign) BOOL autoLoad;
@property (nonatomic, assign) ImageType imageType;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, strong) UIImage * placeHolder;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,9 +1,7 @@
//
// NetImageConfig.m
// YUMI
//
// Created by zu on 2021/11/25.
//
#import "NetImageConfig.h"

View File

@@ -0,0 +1,21 @@
//
// NetImageConfig.m
// YUMI
//
// Created by zu on 2021/11/25.
//
#import "NetImageConfig.h"
@implementation NetImageConfig
- (instancetype)init
{
self = [super init];
if (self) {
_autoLoad = YES;
}
return self;
}
@end

View File

@@ -1,9 +1,7 @@
//
// NetImageView.h
// YUMI
//
// Created by zu on 2021/11/2.
//
#import <UIKit/UIKit.h>
#import "UIImageConstant.h"
@@ -24,7 +22,7 @@ typedef NS_ENUM(NSInteger, NetImageState){
@property (nonatomic, assign, readonly) NetImageState state;
@property (nonatomic, copy) NSString* imageUrl;
@property (nonatomic, assign) NSInteger backgroundLightType; // 0: non; 1: gold; 2: gray; 3: purple
@property (nonatomic, assign) NSInteger backgroundLightType;
- (instancetype)initWithUrl:(NSString * _Nonnull)imageUrl;
- (instancetype)initWithConfig:(NetImageConfig * _Nullable)config;

View File

@@ -0,0 +1,45 @@
//
// NetImageView.h
// YUMI
//
// Created by zu on 2021/11/2.
//
#import <UIKit/UIKit.h>
#import "UIImageConstant.h"
#import "NetImageConfig.h"
NS_ASSUME_NONNULL_BEGIN
typedef void(^LoadCompletion)(UIImage *_Nullable image, NSURL * url);
typedef void(^LoadFail)(NSError *error);
typedef NS_ENUM(NSInteger, NetImageState){
NetImageStateUnload = 1,
NetImageStateLoading,
NetImageStateLoaded,
};
@interface NetImageView : UIImageView
@property (nonatomic, assign, readonly) NetImageState state;
@property (nonatomic, copy) NSString* imageUrl;
@property (nonatomic, assign) NSInteger backgroundLightType; // 0: non; 1: gold; 2: gray; 3: purple
- (instancetype)initWithUrl:(NSString * _Nonnull)imageUrl;
- (instancetype)initWithConfig:(NetImageConfig * _Nullable)config;
- (instancetype)initWithUrl:(NSString * _Nonnull)imageUrl config:(NetImageConfig * _Nullable)config;
- (UIImage *)lightImage:(NSInteger)type;
- (void)loadImage:(LoadCompletion _Nullable)completion;
- (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion;
- (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail;
- (void)updateConfigPlaceHolder:(UIImage *)image;
- (void)cancelLoadImage;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,9 +1,7 @@
//
// NetImageView.m
// YUMI
//
// Created by zu on 2021/11/2.
//
#import "NetImageView.h"
#import <UIImageView+WebCache.h>

View File

@@ -0,0 +1,151 @@
//
// NetImageView.m
// YUMI
//
// Created by zu on 2021/11/2.
//
#import "NetImageView.h"
#import <UIImageView+WebCache.h>
#import <SDImageCache.h>
@interface NetImageView()
@property (nonatomic, assign, readwrite) NetImageState state;
@property (nonatomic, copy) NSString * innerConfigUrl;
@property (nonatomic, strong) NetImageConfig * config;
@property (nonatomic, strong) UIImageView *lightImageView;
@end
@implementation NetImageView
- (instancetype)initWithUrl:(NSString *)url {
return [self initWithUrl:url config:nil];
}
- (instancetype)initWithConfig:(NetImageConfig *)config {
return [self initWithUrl:@"" config:config];
}
- (instancetype)initWithUrl:(NSString *)url config:(NetImageConfig *)config {
self = [super init];
if (self) {
_state = NetImageStateUnload;
_config = config;
if (_config.autoLoad) {
[self setImageUrl:url];
} else {
[self initImageUrl:url];
}
}
return self;
}
- (void)cancelLoadImage {
[self sd_cancelCurrentImageLoad];
}
- (void)initImageUrl:(NSString *)imageUrl {
_imageUrl = imageUrl;
_innerConfigUrl = [UIImageConstant configUrl:_imageUrl type:self.config.imageType radius:self.config.radius];
}
- (void)setBackgroundLightType:(NSInteger)backgroundLightType {
_backgroundLightType = backgroundLightType;
if (!_lightImageView) {
_lightImageView = [[UIImageView alloc] init];
[self insertSubview:_lightImageView atIndex:0];
[_lightImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
}
switch (_backgroundLightType) {
case 1:
_lightImageView.image = [UIImage imageNamed:@"room_pk_result_avatar_bg_yellow"];
break;
case 2:
_lightImageView.image = [UIImage imageNamed:@"room_pk_result_avatar_bg_gray"];
break;
case 3:
_lightImageView.image = [UIImage imageNamed:@"room_pk_result_avatar_bg_purple"];
break;
default:
_lightImageView.image = nil;
break;
}
}
- (UIImage *)lightImage:(NSInteger)type {
switch (type) {
case 1:
return [UIImage imageNamed:@"room_pk_result_avatar_bg_yellow"];
case 2:
return [UIImage imageNamed:@"room_pk_result_avatar_bg_gray"];
case 3:
return [UIImage imageNamed:@"room_pk_result_avatar_bg_purple"];
default:
return nil;
}
}
- (void)setImageUrl:(NSString *)imageUrl {
[self initImageUrl:imageUrl];
[self loadImage:nil fail:nil];
}
- (void)loadImage:(LoadCompletion)completion {
[self loadImage:completion fail:nil];
}
- (void)loadImageWithUrl:(NSString *)imageUrl completion:(LoadCompletion)completion {
[self initImageUrl:imageUrl];
[self loadImage:completion fail:nil];
}
- (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail{
[self initImageUrl:imageUrl];
[self loadImage:completion fail:fail];
}
- (void)loadImage:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail{
self.state = NetImageStateLoading;
@kWeakify(self);
[self sd_setImageWithURL:[NSURL URLWithString:self.innerConfigUrl]
placeholderImage:self.config.placeHolder
options:SDWebImageRetryFailed | SDWebImageQueryMemoryData | SDWebImageQueryDiskDataSync
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
@kStrongify(self);
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
self.state = NetImageStateUnload;
if (fail){
fail(error);
}
} else {
self.image = image;
self.state = NetImageStateLoaded;
if (completion) {
completion(image, imageURL);
};
}
});
}];
}
- (NetImageConfig *)config {
if (!_config) {
_config = [[NetImageConfig alloc] init];
}
return _config;
}
- (void)updateConfigPlaceHolder:(UIImage *)image {
self.config.placeHolder = image;
if (self.state == NetImageStateUnload) {
self.image = image;
}
}
@end

View File

@@ -1,9 +1,7 @@
//
// UIImageViewConstant.h
// YUMI
//
// Created by YUMI on 2021/9/17.
// 存放一些 加载图片 需要做的裁剪的key
#import <Foundation/Foundation.h>
@@ -13,35 +11,33 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - 图片相关的
UIKIT_EXTERN NSString * const kImageTypeRoomFace; //房间表情
UIKIT_EXTERN NSString * const kImageTypeRoomGift; //房间礼物
UIKIT_EXTERN NSString * const kImageTypeUserIcon; //用户头像60x60
UIKIT_EXTERN NSString * const kImageTypeUserLibaryDetail;//用户相册大图nil
UIKIT_EXTERN NSString * const kImageTypeCornerAvatar;//圆角图形会先把图形裁剪成正方形并且转换为png
UIKIT_EXTERN NSString * const kImageTypeUserInfoAlbum;//用户信息里面相册
UIKIT_EXTERN NSString * const kImageTypeUserCardLevel;///用户资料卡中 等级以高度20等比例缩放
UIKIT_EXTERN NSString * const kImageTypeMonentsPhoto;///动态中的图片
UIKIT_EXTERN NSString * const kImageTypeRoomFace;
UIKIT_EXTERN NSString * const kImageTypeRoomGift;
UIKIT_EXTERN NSString * const kImageTypeUserIcon;
UIKIT_EXTERN NSString * const kImageTypeUserLibaryDetail;
UIKIT_EXTERN NSString * const kImageTypeCornerAvatar;
UIKIT_EXTERN NSString * const kImageTypeUserInfoAlbum;
UIKIT_EXTERN NSString * const kImageTypeUserCardLevel;
UIKIT_EXTERN NSString * const kImageTypeMonentsPhoto;
typedef NS_ENUM(NSUInteger, ImageType){
ImageTypeRoomFace = 1, //房间表情
ImageTypeRoomGift, //房间礼物
ImageTypeUserIcon, //用户头像60x60
ImageTypeUserLibaryDetail, //用户相册大图
ImageTypeCornerAvatar, //圆角图形会先把图形裁剪成正方形并且转换为png
ImageTypeUserInfoAlbum, ///用户信息里面相册
ImageTypeUserCardLevel, /// 用户资料卡中 等级以高度20等比例缩放
ImageTypeMonentsPhoto, ///动态中的图片
ImageTypeRoomFace = 1,
ImageTypeRoomGift,
ImageTypeUserIcon,
ImageTypeUserLibaryDetail,
ImageTypeCornerAvatar,
ImageTypeUserInfoAlbum,
ImageTypeUserCardLevel,
ImageTypeMonentsPhoto,
};
///展位图
/// 头像的默认占位图
+ (UIImage *)defaultAvatarPlaceholder;
///空白头像缺省图
+ (UIImage *)defaultEmptyAvatarPlaceholder;
/// 空白图的占位图
+ (UIImage *)defaultEmptyPlaceholder;
+ (UIImage *)defaultEmptyPlaceholder_UFO;
/// banner的占位图
+ (UIImage *)defaultBannerPlaceholder;
+ (NSString*)configUrl:(NSString*)url type:(ImageType)type;

View File

@@ -0,0 +1,53 @@
//
// UIImageViewConstant.h
// YUMI
//
// Created by YUMI on 2021/9/17.
// 存放一些 加载图片 需要做的裁剪的key
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImageConstant : NSObject
#pragma mark - 图片相关的
UIKIT_EXTERN NSString * const kImageTypeRoomFace; //房间表情
UIKIT_EXTERN NSString * const kImageTypeRoomGift; //房间礼物
UIKIT_EXTERN NSString * const kImageTypeUserIcon; //用户头像60x60
UIKIT_EXTERN NSString * const kImageTypeUserLibaryDetail;//用户相册大图nil
UIKIT_EXTERN NSString * const kImageTypeCornerAvatar;//圆角图形会先把图形裁剪成正方形并且转换为png
UIKIT_EXTERN NSString * const kImageTypeUserInfoAlbum;//用户信息里面相册
UIKIT_EXTERN NSString * const kImageTypeUserCardLevel;///用户资料卡中 等级以高度20等比例缩放
UIKIT_EXTERN NSString * const kImageTypeMonentsPhoto;///动态中的图片
typedef NS_ENUM(NSUInteger, ImageType){
ImageTypeRoomFace = 1, //房间表情
ImageTypeRoomGift, //房间礼物
ImageTypeUserIcon, //用户头像60x60
ImageTypeUserLibaryDetail, //用户相册大图
ImageTypeCornerAvatar, //圆角图形会先把图形裁剪成正方形并且转换为png
ImageTypeUserInfoAlbum, ///用户信息里面相册
ImageTypeUserCardLevel, /// 用户资料卡中 等级以高度20等比例缩放
ImageTypeMonentsPhoto, ///动态中的图片
};
///展位图
/// 头像的默认占位图
+ (UIImage *)defaultAvatarPlaceholder;
///空白头像缺省图
+ (UIImage *)defaultEmptyAvatarPlaceholder;
/// 空白图的占位图
+ (UIImage *)defaultEmptyPlaceholder;
+ (UIImage *)defaultEmptyPlaceholder_UFO;
/// banner的占位图
+ (UIImage *)defaultBannerPlaceholder;
+ (NSString*)configUrl:(NSString*)url type:(ImageType)type;
+ (NSString*)configUrl:(NSString*)url radius:(CGFloat)radius;
+ (NSString*)configUrl:(NSString*)url type:(ImageType)type radius:(CGFloat)radius;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,45 +1,43 @@
//
// UIImageViewConstant.m
// YUMI
//
// Created by YUMI on 2021/9/17.
//
#import "UIImageConstant.h"
@implementation UIImageConstant
///
NSString * const kImageTypeRoomFace = @"";
///
NSString * const kImageTypeRoomGift = @"";
/// 150x150
NSString * const kImageTypeUserIcon = @"imageMogr2/auto-orient/thumbnail/150x150";
///
NSString * const kImageTypeUserLibaryDetail = @"imageMogr2/auto-orient/thumbnail/300x300";
NSString * const kImageTypeCornerAvatar = @"imageMogr2/auto-orient/thumbnail/300x300/format/png";
///
NSString * const kImageTypeUserInfoAlbum = @"imageMogr2/auto-orient/blur/375x375";
///
NSString * const kImageTypeUserCardLevel = @"imageMogr2/thumbnail/x40";
/// 400 * 400
NSString * const kImageTypeMonentsPhoto = @"imageMogr2/auto-orient/thumbnail/400x400";
///
+ (UIImage *)defaultAvatarPlaceholder {
return [UIImage imageNamed:@"common_avatar"];
}
///
+ (UIImage *)defaultEmptyAvatarPlaceholder {
return [UIImage imageNamed:@"common_avatar"];
}
///
+ (UIImage *)defaultEmptyPlaceholder {
return [UIImage imageNamed:@"common_empty"];
}
/// banner
+ (UIImage *)defaultBannerPlaceholder {
return [UIImage imageNamed:@"common_banner"];
}
@@ -107,10 +105,10 @@ NSString * const kImageTypeMonentsPhoto = @"imageMogr2/auto-orient/thumbnail/400
}
NSString *percentEscapeString(NSString *string) {
//
NSMutableCharacterSet *allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
// , %
[allowedCharacterSet removeCharactersInString:@"|"];
return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];

View File

@@ -0,0 +1,119 @@
//
// UIImageViewConstant.m
// YUMI
//
// Created by YUMI on 2021/9/17.
//
#import "UIImageConstant.h"
@implementation UIImageConstant
/// 房间表情
NSString * const kImageTypeRoomFace = @"";
/// 房间礼物
NSString * const kImageTypeRoomGift = @"";
/// 用户头像150x150
NSString * const kImageTypeUserIcon = @"imageMogr2/auto-orient/thumbnail/150x150";
/// 用户相册大图
NSString * const kImageTypeUserLibaryDetail = @"imageMogr2/auto-orient/thumbnail/300x300";
NSString * const kImageTypeCornerAvatar = @"imageMogr2/auto-orient/thumbnail/300x300/format/png";
/// 用户信息里面相册
NSString * const kImageTypeUserInfoAlbum = @"imageMogr2/auto-orient/blur/375x375";
/// 用户信息里面相册
NSString * const kImageTypeUserCardLevel = @"imageMogr2/thumbnail/x40";
/// 动态中的图片 400 * 400
NSString * const kImageTypeMonentsPhoto = @"imageMogr2/auto-orient/thumbnail/400x400";
/// 头像的默认占位图
+ (UIImage *)defaultAvatarPlaceholder {
return [UIImage imageNamed:@"common_avatar"];
}
///空白头像缺省图
+ (UIImage *)defaultEmptyAvatarPlaceholder {
return [UIImage imageNamed:@"common_avatar"];
}
/// 空白图的占位图
+ (UIImage *)defaultEmptyPlaceholder {
return [UIImage imageNamed:@"common_empty"];
}
/// banner的占位图
+ (UIImage *)defaultBannerPlaceholder {
return [UIImage imageNamed:@"common_banner"];
}
+ (UIImage *)defaultEmptyPlaceholder_UFO {
return [UIImage imageNamed:@"common_empty_UFO"];
}
+ (NSString *)configUrl:(NSString *)url type:(ImageType)type {
return [self configUrl:url type:type radius:0];
}
+ (NSString *)configUrl:(NSString *)url radius:(CGFloat)radius {
return [self configUrl:url type:-1 radius:radius];
}
+ (NSString *)configUrl:(NSString *)url type:(ImageType)type radius:(CGFloat)radius {
if (!url || url.length <= 0) return nil;
NSMutableString *urlString = [NSMutableString stringWithString:url];
NSString *configUrl = nil;
switch (type) {
case ImageTypeUserIcon:
configUrl = kImageTypeUserIcon;
break;
case ImageTypeCornerAvatar:
configUrl = kImageTypeCornerAvatar;
break;
case ImageTypeRoomFace:
configUrl = kImageTypeRoomFace;
break;
case ImageTypeUserLibaryDetail:
configUrl = kImageTypeUserLibaryDetail;
break;
case ImageTypeRoomGift:
configUrl = kImageTypeRoomGift;
break;
case ImageTypeUserInfoAlbum:
configUrl = kImageTypeUserInfoAlbum;
break;
case ImageTypeUserCardLevel:
configUrl = kImageTypeUserCardLevel;
break;
case ImageTypeMonentsPhoto:
configUrl = kImageTypeMonentsPhoto;
break;
default:
break;
}
if (configUrl) {
if ([url containsString:@"?"]) {
[urlString appendString:@"|"];
}else{
[urlString appendString:@"?"];
}
[urlString appendString:configUrl];
}
if (radius > 0) {
[urlString appendString:[NSString stringWithFormat:@"|roundPic/radius/%f", radius]];
}
return percentEscapeString(urlString);
}
NSString *percentEscapeString(NSString *string) {
// 创建一个包含所有不需要百分比编码的字符集
NSMutableCharacterSet *allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
// 手动删除你想要百分比编码的字符, 其余的非字符将会变成带 % 的转义符
[allowedCharacterSet removeCharactersInString:@"|"];
return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
}
@end

View File

@@ -1,9 +1,7 @@
//
// UIView+Corner.h
// YUMI
//
// Created by YUMI on 2022/6/15.
//
#import <UIKit/UIKit.h>
@@ -16,44 +14,15 @@ NS_ASSUME_NONNULL_BEGIN
bottomRightCorner:(CGFloat)bottemRight
size:(CGSize)size;
///**
// * 设置视图的圆角半径,边框宽度和颜色
// *
// * @param radius 圆角半径
// * @param corners 圆角位置 (可以组合使用 UIRectCornerTopLeft、UIRectCornerTopRight、UIRectCornerBottomLeft、UIRectCornerBottomRight)
// * @param borderWidth 边框宽度
// * @param borderColor 边框颜色
// */
//- (void)setCornerRadius:(CGFloat)radius
// corners:(UIRectCorner)corners
// borderWidth:(CGFloat)borderWidth
// borderColor:(UIColor *)borderColor;
/**
* 直接设置视图的圆角半径,应用到所有角
*
* @param radius 圆角半径
*/
- (void)setCornerRadius:(CGFloat)radius;
/**
* 直接设置视图的圆角半径和指定角
*
* @param radius 圆角半径
* @param corners 指定需要圆角的位置 (UIRectCornerTopLeft, UIRectCornerTopRight, etc.)
*/
- (void)setCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners;
- (void)setCornerRadius:(CGFloat)radius cornerMask:(CACornerMask)cornerMask;
/**
* 设置视图的指定圆角、圆角半径、边框宽度和边框颜色
*
* @param radius 圆角半径
* @param corners 需要圆角的角位置 (可以组合,例如 `kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner`)
* @param borderWidth 边框宽度
* @param borderColor 边框颜色
*/
- (void)setCornerRadius:(CGFloat)radius
corners:(CACornerMask)corners
borderWidth:(CGFloat)borderWidth

View File

@@ -0,0 +1,67 @@
//
// UIView+Corner.h
// YUMI
//
// Created by YUMI on 2022/6/15.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (Corner)
- (void)setCornerWithLeftTopCorner:(CGFloat)leftTop
rightTopCorner:(CGFloat)rigtTop
bottomLeftCorner:(CGFloat)bottemLeft
bottomRightCorner:(CGFloat)bottemRight
size:(CGSize)size;
///**
// * 设置视图的圆角半径,边框宽度和颜色
// *
// * @param radius 圆角半径
// * @param corners 圆角位置 (可以组合使用 UIRectCornerTopLeft、UIRectCornerTopRight、UIRectCornerBottomLeft、UIRectCornerBottomRight)
// * @param borderWidth 边框宽度
// * @param borderColor 边框颜色
// */
//- (void)setCornerRadius:(CGFloat)radius
// corners:(UIRectCorner)corners
// borderWidth:(CGFloat)borderWidth
// borderColor:(UIColor *)borderColor;
/**
* 直接设置视图的圆角半径,应用到所有角
*
* @param radius 圆角半径
*/
- (void)setCornerRadius:(CGFloat)radius;
/**
* 直接设置视图的圆角半径和指定角
*
* @param radius 圆角半径
* @param corners 指定需要圆角的位置 (UIRectCornerTopLeft, UIRectCornerTopRight, etc.)
*/
- (void)setCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners;
- (void)setCornerRadius:(CGFloat)radius cornerMask:(CACornerMask)cornerMask;
/**
* 设置视图的指定圆角、圆角半径、边框宽度和边框颜色
*
* @param radius 圆角半径
* @param corners 需要圆角的角位置 (可以组合,例如 `kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner`)
* @param borderWidth 边框宽度
* @param borderColor 边框颜色
*/
- (void)setCornerRadius:(CGFloat)radius
corners:(CACornerMask)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor;
- (void)setAllCornerRadius:(CGFloat)radius
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,9 +1,7 @@
//
// UIView+Corner.m
// YUMI
//
// Created by YUMI on 2022/6/15.
//
#import "UIView+Corner.h"
@@ -21,18 +19,18 @@
maskPath.lineWidth = 1.0;
maskPath.lineCapStyle = kCGLineCapRound;
maskPath.lineJoinStyle = kCGLineJoinRound;
[maskPath moveToPoint:CGPointMake(bottemRight, height)]; //
[maskPath moveToPoint:CGPointMake(bottemRight, height)];
[maskPath addLineToPoint:CGPointMake(width - bottemRight, height)];
[maskPath addQuadCurveToPoint:CGPointMake(width, height- bottemRight) controlPoint:CGPointMake(width, height)]; //
[maskPath addLineToPoint:CGPointMake(width, rigtTop)]; //线
[maskPath addQuadCurveToPoint:CGPointMake(width, height- bottemRight) controlPoint:CGPointMake(width, height)];
[maskPath addLineToPoint:CGPointMake(width, rigtTop)];
[maskPath addQuadCurveToPoint:CGPointMake(width - rigtTop, 0) controlPoint:CGPointMake(width, 0)]; //
[maskPath addLineToPoint:CGPointMake(leftTop, 0)]; //线
[maskPath addQuadCurveToPoint:CGPointMake(width - rigtTop, 0) controlPoint:CGPointMake(width, 0)];
[maskPath addLineToPoint:CGPointMake(leftTop, 0)];
[maskPath addQuadCurveToPoint:CGPointMake(0, leftTop) controlPoint:CGPointMake(0, 0)]; //
[maskPath addLineToPoint:CGPointMake(0, height - bottemLeft)]; //线
[maskPath addQuadCurveToPoint:CGPointMake(bottemLeft, height) controlPoint:CGPointMake(0, height)]; //
[maskPath addQuadCurveToPoint:CGPointMake(0, leftTop) controlPoint:CGPointMake(0, 0)];
[maskPath addLineToPoint:CGPointMake(0, height - bottemLeft)];
[maskPath addQuadCurveToPoint:CGPointMake(bottemLeft, height) controlPoint:CGPointMake(0, height)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = CGRectMake(0, 0, size.width, size.height);
@@ -40,43 +38,17 @@
self.layer.mask = maskLayer;
}
//- (void)setCornerRadius:(CGFloat)radius
// corners:(UIRectCorner)corners
// borderWidth:(CGFloat)borderWidth
// borderColor:(UIColor *)borderColor {
//
// // UIBezierPath
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
// byRoundingCorners:corners
// cornerRadii:CGSizeMake(radius, radius)];
//
// // CAShapeLayer path
// CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
// maskLayer.path = path.CGPath;
// self.layer.mask = maskLayer;
//
// //
// if (borderWidth > 0 && borderColor) {
// CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
// borderLayer.path = path.CGPath;
// borderLayer.lineWidth = borderWidth;
// borderLayer.strokeColor = borderColor.CGColor;
// borderLayer.fillColor = UIColor.clearColor.CGColor;
// borderLayer.frame = self.bounds;
// [self.layer addSublayer:borderLayer];
// }
//}
- (void)setCornerRadius:(CGFloat)radius {
self.layer.cornerRadius = radius;
self.layer.masksToBounds = YES; //
self.layer.masksToBounds = YES;
}
- (void)setCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners {
if (corners == UIRectCornerAllCorners) {
[self setCornerRadius:radius];
} else {
// 使 `CAShapeLayer` `UIBezierPath`使
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
@@ -98,12 +70,12 @@
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor {
//
self.layer.cornerRadius = radius;
self.layer.maskedCorners = corners;
self.layer.masksToBounds = YES; //
self.layer.masksToBounds = YES;
//
self.layer.borderWidth = borderWidth;
self.layer.borderColor = borderColor.CGColor;
}
@@ -111,12 +83,12 @@
- (void)setAllCornerRadius:(CGFloat)radius
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor {
//
self.layer.cornerRadius = radius;
self.layer.maskedCorners = UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight;
self.layer.masksToBounds = YES; //
self.layer.masksToBounds = YES;
//
self.layer.borderWidth = borderWidth;
self.layer.borderColor = borderColor.CGColor;
}

View File

@@ -0,0 +1,125 @@
//
// UIView+Corner.m
// YUMI
//
// Created by YUMI on 2022/6/15.
//
#import "UIView+Corner.h"
@implementation UIView (Corner)
- (void)setCornerWithLeftTopCorner:(CGFloat)leftTop
rightTopCorner:(CGFloat)rigtTop
bottomLeftCorner:(CGFloat)bottemLeft
bottomRightCorner:(CGFloat)bottemRight
size:(CGSize)size {
CGFloat width = size.width;
CGFloat height = size.height;
UIBezierPath *maskPath = [UIBezierPath bezierPath];
maskPath.lineWidth = 1.0;
maskPath.lineCapStyle = kCGLineCapRound;
maskPath.lineJoinStyle = kCGLineJoinRound;
[maskPath moveToPoint:CGPointMake(bottemRight, height)]; //左下角
[maskPath addLineToPoint:CGPointMake(width - bottemRight, height)];
[maskPath addQuadCurveToPoint:CGPointMake(width, height- bottemRight) controlPoint:CGPointMake(width, height)]; //右下角的圆弧
[maskPath addLineToPoint:CGPointMake(width, rigtTop)]; //右边直线
[maskPath addQuadCurveToPoint:CGPointMake(width - rigtTop, 0) controlPoint:CGPointMake(width, 0)]; //右上角圆弧
[maskPath addLineToPoint:CGPointMake(leftTop, 0)]; //顶部直线
[maskPath addQuadCurveToPoint:CGPointMake(0, leftTop) controlPoint:CGPointMake(0, 0)]; //左上角圆弧
[maskPath addLineToPoint:CGPointMake(0, height - bottemLeft)]; //左边直线
[maskPath addQuadCurveToPoint:CGPointMake(bottemLeft, height) controlPoint:CGPointMake(0, height)]; //左下角圆弧
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = CGRectMake(0, 0, size.width, size.height);
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
//- (void)setCornerRadius:(CGFloat)radius
// corners:(UIRectCorner)corners
// borderWidth:(CGFloat)borderWidth
// borderColor:(UIColor *)borderColor {
//
// // 创建 UIBezierPath 并应用圆角
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
// byRoundingCorners:corners
// cornerRadii:CGSizeMake(radius, radius)];
//
// // 创建 CAShapeLayer 并设置 path
// CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
// maskLayer.path = path.CGPath;
// self.layer.mask = maskLayer;
//
// // 设置边框
// if (borderWidth > 0 && borderColor) {
// CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
// borderLayer.path = path.CGPath;
// borderLayer.lineWidth = borderWidth;
// borderLayer.strokeColor = borderColor.CGColor;
// borderLayer.fillColor = UIColor.clearColor.CGColor;
// borderLayer.frame = self.bounds;
// [self.layer addSublayer:borderLayer];
// }
//}
- (void)setCornerRadius:(CGFloat)radius {
self.layer.cornerRadius = radius;
self.layer.masksToBounds = YES; // 确保视图内容不会超出边界
}
- (void)setCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners {
if (corners == UIRectCornerAllCorners) {
[self setCornerRadius:radius];
} else {
// 如果是部分圆角,使用 `CAShapeLayer` 和 `UIBezierPath`,但仅在必要时使用
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = path.CGPath;
self.layer.mask = maskLayer;
}
}
- (void)setCornerRadius:(CGFloat)radius cornerMask:(CACornerMask)cornerMask {
self.layer.maskedCorners = cornerMask;
self.layer.cornerRadius = radius;
self.layer.masksToBounds = YES;
}
- (void)setCornerRadius:(CGFloat)radius
corners:(CACornerMask)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor {
// 设置指定角的圆角
self.layer.cornerRadius = radius;
self.layer.maskedCorners = corners;
self.layer.masksToBounds = YES; // 确保内容不会超出边界
// 设置边框
self.layer.borderWidth = borderWidth;
self.layer.borderColor = borderColor.CGColor;
}
- (void)setAllCornerRadius:(CGFloat)radius
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor {
// 设置指定角的圆角
self.layer.cornerRadius = radius;
self.layer.maskedCorners = UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight;
self.layer.masksToBounds = YES; // 确保内容不会超出边界
// 设置边框
self.layer.borderWidth = borderWidth;
self.layer.borderColor = borderColor.CGColor;
}
@end

View File

@@ -1,9 +1,7 @@
//
// UIView+GradientLayer.h
// YuMi
//
// Created by P on 2024/11/18.
//
#import <UIKit/UIKit.h>
@@ -11,23 +9,16 @@ NS_ASSUME_NONNULL_BEGIN
@interface UIView (GradientLayer)
/// 为视图添加渐变背景
/// @param colors 渐变颜色数组 (NSArray<UIColor *> *)
/// @param startPoint 渐变起点 (CGPoint)
/// @param endPoint 渐变终点 (CGPoint)
/// @param cornerRadius 圆角半径 (CGFloat)
- (void)addGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
cornerRadius:(CGFloat)cornerRadius;
/// 移除渐变背景
- (void)removeGradientBackground;
/// 更新渐变背景
/// @param colors 渐变颜色数组 (NSArray<UIColor *> *)
/// @param startPoint 渐变起点 (CGPoint)
/// @param endPoint 渐变终点 (CGPoint)
- (void)updateGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint;

View File

@@ -0,0 +1,37 @@
//
// UIView+GradientLayer.h
// YuMi
//
// Created by P on 2024/11/18.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (GradientLayer)
/// 为视图添加渐变背景
/// @param colors 渐变颜色数组 (NSArray<UIColor *> *)
/// @param startPoint 渐变起点 (CGPoint)
/// @param endPoint 渐变终点 (CGPoint)
/// @param cornerRadius 圆角半径 (CGFloat)
- (void)addGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
cornerRadius:(CGFloat)cornerRadius;
/// 移除渐变背景
- (void)removeGradientBackground;
/// 更新渐变背景
/// @param colors 渐变颜色数组 (NSArray<UIColor *> *)
/// @param startPoint 渐变起点 (CGPoint)
/// @param endPoint 渐变终点 (CGPoint)
- (void)updateGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,9 +1,7 @@
//
// UIView+GradientLayer.m
// YuMi
//
// Created by P on 2024/11/18.
//
#import "UIView+GradientLayer.h"
#import <QuartzCore/QuartzCore.h>
@@ -13,15 +11,15 @@
static NSString * const kGradientLayerName = @"GradientLayer";
static void *kGradientObserverKey = &kGradientObserverKey;
//
- (void)addGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
cornerRadius:(CGFloat)cornerRadius {
// KVO
[self removeGradientBackground];
//
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.name = kGradientLayerName;
gradientLayer.colors = [self cgColorsFromUIColors:colors];
@@ -29,16 +27,16 @@ static void *kGradientObserverKey = &kGradientObserverKey;
gradientLayer.endPoint = endPoint;
gradientLayer.cornerRadius = cornerRadius;
// frame
gradientLayer.frame = self.bounds;
[self.layer insertSublayer:gradientLayer atIndex:0];
// KVO
[self setupLayoutObserverForGradientLayer:gradientLayer];
}
//
- (void)updateGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint {
@@ -50,22 +48,22 @@ static void *kGradientObserverKey = &kGradientObserverKey;
}
}
//
- (void)removeGradientBackground {
//
CAGradientLayer *gradientLayer = [self gradientLayer];
if (gradientLayer) {
[gradientLayer removeFromSuperlayer];
}
// KVO
if ([self hasAddedObserver]) {
[self removeObserver:self forKeyPath:@"bounds"];
[self setHasAddedObserver:NO];
}
}
//
- (CAGradientLayer *)gradientLayer {
for (CALayer *layer in self.layer.sublayers) {
if ([layer.name isEqualToString:kGradientLayerName] &&
@@ -76,7 +74,7 @@ static void *kGradientObserverKey = &kGradientObserverKey;
return nil;
}
// KVO
- (void)setupLayoutObserverForGradientLayer:(CAGradientLayer *)gradientLayer {
if (![self hasAddedObserver]) {
[self addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew context:(__bridge void *)gradientLayer];
@@ -84,20 +82,20 @@ static void *kGradientObserverKey = &kGradientObserverKey;
}
}
// KVO
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"bounds"]) {
CAGradientLayer *gradientLayer = (__bridge CAGradientLayer *)context;
gradientLayer.frame = self.bounds; // frame
gradientLayer.frame = self.bounds;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
//
- (NSArray *)cgColorsFromUIColors:(NSArray<UIColor *> *)colors {
NSMutableArray *cgColors = [NSMutableArray array];
for (UIColor *color in colors) {
@@ -106,12 +104,12 @@ static void *kGradientObserverKey = &kGradientObserverKey;
return [cgColors copy];
}
// KVO
- (BOOL)hasAddedObserver {
return [objc_getAssociatedObject(self, kGradientObserverKey) boolValue];
}
// KVO
- (void)setHasAddedObserver:(BOOL)added {
objc_setAssociatedObject(self, kGradientObserverKey, @(added), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

View File

@@ -0,0 +1,120 @@
//
// UIView+GradientLayer.m
// YuMi
//
// Created by P on 2024/11/18.
//
#import "UIView+GradientLayer.h"
#import <QuartzCore/QuartzCore.h>
@implementation UIView (GradientLayer)
static NSString * const kGradientLayerName = @"GradientLayer";
static void *kGradientObserverKey = &kGradientObserverKey;
// 添加渐变背景
- (void)addGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint
cornerRadius:(CGFloat)cornerRadius {
// 确保移除已有的渐变背景和 KVO 监听
[self removeGradientBackground];
// 创建渐变图层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.name = kGradientLayerName;
gradientLayer.colors = [self cgColorsFromUIColors:colors];
gradientLayer.startPoint = startPoint;
gradientLayer.endPoint = endPoint;
gradientLayer.cornerRadius = cornerRadius;
// 初次设置 frame
gradientLayer.frame = self.bounds;
[self.layer insertSublayer:gradientLayer atIndex:0];
// 添加 KVO 监听
[self setupLayoutObserverForGradientLayer:gradientLayer];
}
// 动态更新渐变背景
- (void)updateGradientBackgroundWithColors:(NSArray<UIColor *> *)colors
startPoint:(CGPoint)startPoint
endPoint:(CGPoint)endPoint {
CAGradientLayer *gradientLayer = [self gradientLayer];
if (gradientLayer) {
gradientLayer.colors = [self cgColorsFromUIColors:colors];
gradientLayer.startPoint = startPoint;
gradientLayer.endPoint = endPoint;
}
}
// 移除渐变背景和监听
- (void)removeGradientBackground {
// 移除渐变图层
CAGradientLayer *gradientLayer = [self gradientLayer];
if (gradientLayer) {
[gradientLayer removeFromSuperlayer];
}
// 移除 KVO 监听
if ([self hasAddedObserver]) {
[self removeObserver:self forKeyPath:@"bounds"];
[self setHasAddedObserver:NO];
}
}
// 获取渐变图层
- (CAGradientLayer *)gradientLayer {
for (CALayer *layer in self.layer.sublayers) {
if ([layer.name isEqualToString:kGradientLayerName] &&
[layer isKindOfClass:[CAGradientLayer class]]) {
return (CAGradientLayer *)layer;
}
}
return nil;
}
// 添加 KVO 监听
- (void)setupLayoutObserverForGradientLayer:(CAGradientLayer *)gradientLayer {
if (![self hasAddedObserver]) {
[self addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew context:(__bridge void *)gradientLayer];
[self setHasAddedObserver:YES];
}
}
// KVO 监听回调
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"bounds"]) {
CAGradientLayer *gradientLayer = (__bridge CAGradientLayer *)context;
gradientLayer.frame = self.bounds; // 更新渐变图层的 frame
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
// 工具方法:转换颜色数组
- (NSArray *)cgColorsFromUIColors:(NSArray<UIColor *> *)colors {
NSMutableArray *cgColors = [NSMutableArray array];
for (UIColor *color in colors) {
[cgColors addObject:(id)color.CGColor];
}
return [cgColors copy];
}
// 工具方法:判断是否已注册 KVO
- (BOOL)hasAddedObserver {
return [objc_getAssociatedObject(self, kGradientObserverKey) boolValue];
}
// 工具方法:设置是否已注册 KVO
- (void)setHasAddedObserver:(BOOL)added {
objc_setAssociatedObject(self, kGradientObserverKey, @(added), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

View File

@@ -103,6 +103,6 @@
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;
///为图片增加毛玻璃,value模糊程度
+(UIImage *)setBlurImage:(UIImage *)image value:(CGFloat)value;
@end

View File

@@ -0,0 +1,108 @@
/*
File: UIImage+ImageEffects.h
Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code youll want to look out to find out how to use vImage to efficiently calculate a blur.
Version: 1.0
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2013 Apple Inc. All Rights Reserved.
Copyright © 2013 Apple Inc. All rights reserved.
WWDC 2013 License
NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013
Session. Please refer to the applicable WWDC 2013 Session for further
information.
IMPORTANT: This Apple software is supplied to you by Apple Inc.
("Apple") in consideration of your agreement to the following terms, and
your use, installation, modification or redistribution of this Apple
software constitutes acceptance of these terms. If you do not agree with
these terms, please do not use, install, modify or redistribute this
Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a non-exclusive license, under
Apple's copyrights in this original Apple software (the "Apple
Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES
NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
EA1002
5/3/2013
*/
#import <UIKit/UIKit.h>
@interface UIImage (ImageEffects)
- (UIImage *)applyLightEffect;
- (UIImage *)applyExtraLightEffect;
- (UIImage *)applyDarkEffect;
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;
///为图片增加毛玻璃,value模糊程度
+(UIImage *)setBlurImage:(UIImage *)image value:(CGFloat)value;
@end

View File

@@ -146,17 +146,17 @@
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
{
// Check pre-conditions.
if (self.size.width < 1 || self.size.height < 1) {
// NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
return nil;
}
if (!self.CGImage) {
// NSLog (@"*** error: image must be backed by a CGImage: %@", self);
return nil;
}
if (maskImage && !maskImage.CGImage) {
// NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
return nil;
}
@@ -187,22 +187,12 @@
effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
if (hasBlur) {
// A description of how to compute the box kernel width from the Gaussian
// radius (aka standard deviation) appears in the SVG spec:
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
//
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
// successive box-blurs build a piece-wise quadratic convolution kernel, which
// approximates the Gaussian kernel to within roughly 3%.
//
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
//
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
//
CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
if (radius % 2 != 1) {
radius += 1; // force radius to be odd so that the three box-blur methodology works.
radius += 1;
}
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
@@ -240,16 +230,16 @@
UIGraphicsEndImageContext();
}
// Set up output context.
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef outputContext = UIGraphicsGetCurrentContext();
CGContextScaleCTM(outputContext, 1.0, -1.0);
CGContextTranslateCTM(outputContext, 0, -self.size.height);
// Draw base image.
CGContextDrawImage(outputContext, imageRect, self.CGImage);
// Draw effect image.
if (hasBlur) {
CGContextSaveGState(outputContext);
if (maskImage) {
@@ -259,7 +249,7 @@
CGContextRestoreGState(outputContext);
}
// Add in color tint.
if (tintColor) {
CGContextSaveGState(outputContext);
CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
@@ -267,7 +257,7 @@
CGContextRestoreGState(outputContext);
}
// Output image is ready.
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
@@ -277,28 +267,25 @@
CIContext *context = [CIContext contextWithOptions:nil];
CIImage * sourceImage = [CIImage imageWithCGImage:image.CGImage];//CIImage
CIImage * sourceImage = [CIImage imageWithCGImage:image.CGImage];
///仿
CIFilter * clamp = [CIFilter filterWithName:@"CIAffineClamp"];
CIFilter * clamp = [CIFilter filterWithName:@"CIAffineClamp"];//
[clamp setValue:sourceImage forKey:kCIInputImageKey];//
[clamp setValue:sourceImage forKey:kCIInputImageKey];
CIImage *clampResult = [clamp valueForKey:kCIOutputImageKey];
///
CIFilter* gaussianBlur = [CIFilter filterWithName:@"CIGaussianBlur"];
[gaussianBlur setValue:clampResult forKey:kCIInputImageKey];
[gaussianBlur setValue:[NSNumber numberWithFloat:value] forKey:@"inputRadius"];//
[gaussianBlur setValue:[NSNumber numberWithFloat:value] forKey:@"inputRadius"];
CIImage * gaussianBlurResult = [gaussianBlur valueForKey:kCIOutputImageKey];
///
CGImageRef cgImage = [context createCGImage:gaussianBlurResult fromRect:[sourceImage extent]];
UIImage * resultImage = [UIImage imageWithCGImage:cgImage];

View File

@@ -0,0 +1,309 @@
/*
File: UIImage+ImageEffects.m
Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code youll want to look out to find out how to use vImage to efficiently calculate a blur.
Version: 1.0
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2013 Apple Inc. All Rights Reserved.
Copyright © 2013 Apple Inc. All rights reserved.
WWDC 2013 License
NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013
Session. Please refer to the applicable WWDC 2013 Session for further
information.
IMPORTANT: This Apple software is supplied to you by Apple Inc.
("Apple") in consideration of your agreement to the following terms, and
your use, installation, modification or redistribution of this Apple
software constitutes acceptance of these terms. If you do not agree with
these terms, please do not use, install, modify or redistribute this
Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a non-exclusive license, under
Apple's copyrights in this original Apple software (the "Apple
Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES
NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
EA1002
5/3/2013
*/
#import "UIImage+ImageEffects.h"
#import <Accelerate/Accelerate.h>
#import <float.h>
@implementation UIImage (ImageEffects)
- (UIImage *)applyLightEffect
{
UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
return [self applyBlurWithRadius:30 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
}
- (UIImage *)applyExtraLightEffect
{
UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
}
- (UIImage *)applyDarkEffect
{
UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
}
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
{
const CGFloat EffectColorAlpha = 0.6;
UIColor *effectColor = tintColor;
int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
if (componentCount == 2) {
CGFloat b;
if ([tintColor getWhite:&b alpha:NULL]) {
effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
}
}
else {
CGFloat r, g, b;
if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
}
}
return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
}
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
{
// Check pre-conditions.
if (self.size.width < 1 || self.size.height < 1) {
// NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
return nil;
}
if (!self.CGImage) {
// NSLog (@"*** error: image must be backed by a CGImage: %@", self);
return nil;
}
if (maskImage && !maskImage.CGImage) {
// NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
return nil;
}
CGRect imageRect = { CGPointZero, self.size };
UIImage *effectImage = self;
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
if (hasBlur || hasSaturationChange) {
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef effectInContext = UIGraphicsGetCurrentContext();
CGContextScaleCTM(effectInContext, 1.0, -1.0);
CGContextTranslateCTM(effectInContext, 0, -self.size.height);
CGContextDrawImage(effectInContext, imageRect, self.CGImage);
vImage_Buffer effectInBuffer;
effectInBuffer.data = CGBitmapContextGetData(effectInContext);
effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
vImage_Buffer effectOutBuffer;
effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
if (hasBlur) {
// A description of how to compute the box kernel width from the Gaussian
// radius (aka standard deviation) appears in the SVG spec:
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
//
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
// successive box-blurs build a piece-wise quadratic convolution kernel, which
// approximates the Gaussian kernel to within roughly 3%.
//
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
//
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
//
CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
if (radius % 2 != 1) {
radius += 1; // force radius to be odd so that the three box-blur methodology works.
}
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
}
BOOL effectImageBuffersAreSwapped = NO;
if (hasSaturationChange) {
CGFloat s = saturationDeltaFactor;
CGFloat floatingPointSaturationMatrix[] = {
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
0, 0, 0, 1,
};
const int32_t divisor = 256;
NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
int16_t saturationMatrix[matrixSize];
for (NSUInteger i = 0; i < matrixSize; ++i) {
saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
}
if (hasBlur) {
vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
effectImageBuffersAreSwapped = YES;
}
else {
vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
}
}
if (!effectImageBuffersAreSwapped)
effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (effectImageBuffersAreSwapped)
effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
// Set up output context.
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef outputContext = UIGraphicsGetCurrentContext();
CGContextScaleCTM(outputContext, 1.0, -1.0);
CGContextTranslateCTM(outputContext, 0, -self.size.height);
// Draw base image.
CGContextDrawImage(outputContext, imageRect, self.CGImage);
// Draw effect image.
if (hasBlur) {
CGContextSaveGState(outputContext);
if (maskImage) {
CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
}
CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
CGContextRestoreGState(outputContext);
}
// Add in color tint.
if (tintColor) {
CGContextSaveGState(outputContext);
CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
CGContextFillRect(outputContext, imageRect);
CGContextRestoreGState(outputContext);
}
// Output image is ready.
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
+(UIImage *)setBlurImage:(UIImage *)image value:(CGFloat)value{
CIContext *context = [CIContext contextWithOptions:nil];
CIImage * sourceImage = [CIImage imageWithCGImage:image.CGImage];//将图片转换成CIImage
///图片仿射滤镜
CIFilter * clamp = [CIFilter filterWithName:@"CIAffineClamp"];//设置绘制类型
[clamp setValue:sourceImage forKey:kCIInputImageKey];//设置要绘制的图片
CIImage *clampResult = [clamp valueForKey:kCIOutputImageKey];
///高斯模糊滤镜
CIFilter* gaussianBlur = [CIFilter filterWithName:@"CIGaussianBlur"];
[gaussianBlur setValue:clampResult forKey:kCIInputImageKey];
[gaussianBlur setValue:[NSNumber numberWithFloat:value] forKey:@"inputRadius"];//设置模糊值
CIImage * gaussianBlurResult = [gaussianBlur valueForKey:kCIOutputImageKey];
///转化获取图片
CGImageRef cgImage = [context createCGImage:gaussianBlurResult fromRect:[sourceImage extent]];
UIImage * resultImage = [UIImage imageWithCGImage:cgImage];
return resultImage;
}
@end

View File

@@ -1,31 +1,21 @@
//
// YMCurrentVCStackManager.h
// YMBaseUIKit
//
// Created by 卫明何 on 2018/8/9.
// Copyright © 2018年 YUIMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface XCCurrentVCStackManager : NSObject
+ (instancetype)shareManager;
/**
当前的导航控制器
@return 导航控制器
*/
- (UINavigationController *)currentNavigationController;
/**
当前最顶层控制器
@return 当前最顶层控制器
*/
- (UIViewController *)getCurrentVC;
@end
// Created by 卫明何 on 2018/8/9.
// Copyright © 2018年 YUIMI. All rights reserved.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface XCCurrentVCStackManager : NSObject
+ (instancetype)shareManager;
- (UINavigationController *)currentNavigationController;
- (UIViewController *)getCurrentVC;
@end

View File

@@ -0,0 +1,31 @@
//
// YMCurrentVCStackManager.h
// YMBaseUIKit
//
// Created by 卫明何 on 2018/8/9.
// Copyright © 2018年 YUIMI. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface XCCurrentVCStackManager : NSObject
+ (instancetype)shareManager;
/**
当前的导航控制器
@return 导航控制器
*/
- (UINavigationController *)currentNavigationController;
/**
当前最顶层控制器
@return 当前最顶层控制器
*/
- (UIViewController *)getCurrentVC;
@end

View File

@@ -1,10 +1,8 @@
//
// YMCurrentVCStackManager.m
// YMBaseUIKit
//
// Created by on 2018/8/9.
// Copyright © 2018 YUIMI. All rights reserved.
//
#import "XCCurrentVCStackManager.h"
@@ -22,7 +20,7 @@ NSString * const kRoomChatPushViewKey = @"kRoomChatPushViewKey";
}
- (UIViewController *)getCurrentVC {
///
[[NSNotificationCenter defaultCenter] postNotificationName:kRoomChatPushViewKey object:nil];
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
@@ -33,19 +31,19 @@ NSString * const kRoomChatPushViewKey = @"kRoomChatPushViewKey";
- (UIViewController *)getCurrentVCFrom:(UIViewController *)rootVC {
UIViewController *currentVC;
if ([rootVC presentedViewController]) {
// presented
rootVC = [rootVC presentedViewController];
}
if ([rootVC isKindOfClass:[UITabBarController class]]) {
// UITabBarController
currentVC = [self getCurrentVCFrom:[(UITabBarController *)rootVC selectedViewController]];
} else if ([rootVC isKindOfClass:[UINavigationController class]]) {
// UINavigationController
currentVC = [self getCurrentVCFrom:[(UINavigationController *)rootVC visibleViewController]];
} else {
//
currentVC = rootVC;
}
return currentVC;
@@ -80,7 +78,6 @@ NSString * const kRoomChatPushViewKey = @"kRoomChatPushViewKey";
}
//
- (UINavigationController *)getCurrentNCFrom:(UIViewController *)vc{
if ([vc isKindOfClass:NSClassFromString(@"MMDrawerController")]) {
vc = (UIViewController *)[vc valueForKey:@"centerViewController"];

View File

@@ -0,0 +1,123 @@
//
// YMCurrentVCStackManager.m
// YMBaseUIKit
//
// Created by 卫明何 on 2018/8/9.
// Copyright © 2018年 YUIMI. All rights reserved.
//
#import "XCCurrentVCStackManager.h"
NSString * const kRoomChatPushViewKey = @"kRoomChatPushViewKey";
@implementation XCCurrentVCStackManager
+ (instancetype)shareManager {
static dispatch_once_t onceToken = 0;
static id instance;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (UIViewController *)getCurrentVC {
///兼容房间内私聊的
[[NSNotificationCenter defaultCenter] postNotificationName:kRoomChatPushViewKey object:nil];
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
UIViewController *currentVC = [self getCurrentVCFrom:rootViewController];
return currentVC;
}
- (UIViewController *)getCurrentVCFrom:(UIViewController *)rootVC {
UIViewController *currentVC;
if ([rootVC presentedViewController]) {
// 视图是被presented出来的
rootVC = [rootVC presentedViewController];
}
if ([rootVC isKindOfClass:[UITabBarController class]]) {
// 根视图为UITabBarController
currentVC = [self getCurrentVCFrom:[(UITabBarController *)rootVC selectedViewController]];
} else if ([rootVC isKindOfClass:[UINavigationController class]]) {
// 根视图为UINavigationController
currentVC = [self getCurrentVCFrom:[(UINavigationController *)rootVC visibleViewController]];
} else {
// 根视图为非导航类
currentVC = rootVC;
}
return currentVC;
}
- (UIViewController *)currentViewController {
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
UIViewController *vc = keyWindow.rootViewController;
while (vc.presentedViewController) {
vc = vc.presentedViewController;
if ([vc isKindOfClass:[UINavigationController class]]) {
vc = [(UINavigationController *)vc visibleViewController];
}
else if ([vc isKindOfClass:[UITabBarController class]]) {
vc = [(UITabBarController *)vc selectedViewController];
}
}
return vc;
}
- (UINavigationController *)currentNavigationController {
return [self currentNC];
}
- (UINavigationController *)currentNC{
if (![[UIApplication sharedApplication].windows.lastObject isKindOfClass:[UIWindow class]]) {
NSAssert(0, @"未获取到导航控制器");
return nil;
}
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
return [self getCurrentNCFrom:rootViewController];
}
//递归
- (UINavigationController *)getCurrentNCFrom:(UIViewController *)vc{
if ([vc isKindOfClass:NSClassFromString(@"MMDrawerController")]) {
vc = (UIViewController *)[vc valueForKey:@"centerViewController"];
}
if ([vc isKindOfClass:[UITabBarController class]]) {
UINavigationController *nc = ((UITabBarController *)vc).selectedViewController;
return [self getCurrentNCFrom:nc];
}
else if ([vc isKindOfClass:[UINavigationController class]]) {
if (((UINavigationController *)vc).presentedViewController) {
return [self getCurrentNCFrom:((UINavigationController *)vc).presentedViewController];
}
return [self getCurrentNCFrom:((UINavigationController *)vc).topViewController];
}
else if ([vc isKindOfClass:[UIViewController class]]) {
if (vc.presentedViewController) {
return [self getCurrentNCFrom:vc.presentedViewController];
}
else {
if (!vc.navigationController) {
if (vc.presentingViewController) {
[vc dismissViewControllerAnimated:NO completion:nil];
return [self getCurrentNCFrom:vc.presentingViewController];
} else {
NSAssert(0, @"未获取到导航控制器");
return nil;
}
} else {
return vc.navigationController;
}
}
}
else {
NSAssert(0, @"未获取到导航控制器");
return nil;
}
}
@end

View File

@@ -1,148 +1,76 @@
//
// YMHUDTool.h
// TTPlay
//
// Created by YM on 2022/5/15.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
showGIFLoading使用注意:
1.谁负责showLoading, 谁负责hideHUD
2.showLoading是指定了加载在那个View, hideHUD时请指定hide那个view的hud
*/
@interface XNDJTDDLoadingTool : NSObject
/**
隐藏HUD
*/
+ (void)hideHUD;
/**
隐藏HUD, 如果view为nil, 则默认隐藏主窗口的HUD
@param view view
*/
+ (void)hideHUDInView:(nullable UIView *)view;
+(void)hideOnlyView:(UIView *)view;
+(void)showOnlyView:(UIView *)view;
/**
显示成功message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showSuccessWithMessage:(NSString *)message;
/**
显示成功message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
显示成功message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
/**
显示错误message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showErrorWithMessage:(NSString *)message;
/**
显示错误message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
显示错误message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
/**
在窗口上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
*/
+ (void)showGIFLoading;
/**
在指定的view上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
@param view 显示在哪个view上
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view;
/**
在指定的view上显示自定义GIFLoading
@param view 显示在哪个view上
@param bgColor 背景颜色, 遮盖
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled;
/**
加载下一个个播房
*/
+ (void)showAnchorLoading;
+ (void)showAnchorLoading:(UIView *)view;
/**
在窗口上显示菊花
*/
+ (void)showLoading;
/**
在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view;
/**
在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled;
/**
在窗口上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message;
/**
在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled;
+(void)showOnlyView:(UIView *)view enabled:(BOOL)enabled;
@end
NS_ASSUME_NONNULL_END
// Created by YM on 2022/5/15.
// Copyright © 2023 YUMI. All rights reserved.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface XNDJTDDLoadingTool : NSObject
+ (void)hideHUD;
+ (void)hideHUDInView:(nullable UIView *)view;
+(void)hideOnlyView:(UIView *)view;
+(void)showOnlyView:(UIView *)view;
+ (void)showSuccessWithMessage:(NSString *)message;
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view;
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
+ (void)showErrorWithMessage:(NSString *)message;
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view;
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
+ (void)showGIFLoading;
+ (void)showGIFLoadingInView:(nullable UIView *)view;
+ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled;
+ (void)showAnchorLoading;
+ (void)showAnchorLoading:(UIView *)view;
+ (void)showLoading;
+ (void)showLoadingInView:(nullable UIView *)view;
+ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled;
+ (void)showLoadingWithMessage:(NSString *)message;
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view;
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled;
+(void)showOnlyView:(UIView *)view enabled:(BOOL)enabled;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,148 @@
//
// YMHUDTool.h
// TTPlay
//
// Created by YM on 2022/5/15.
// Copyright © 2023 YUMI. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
showGIFLoading使用注意:
1.谁负责showLoading, 谁负责hideHUD
2.showLoading是指定了加载在那个View, hideHUD时请指定hide那个view的hud
*/
@interface XNDJTDDLoadingTool : NSObject
/**
隐藏HUD
*/
+ (void)hideHUD;
/**
隐藏HUD, 如果view为nil, 则默认隐藏主窗口的HUD
@param view view
*/
+ (void)hideHUDInView:(nullable UIView *)view;
+(void)hideOnlyView:(UIView *)view;
+(void)showOnlyView:(UIView *)view;
/**
显示成功message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showSuccessWithMessage:(NSString *)message;
/**
显示成功message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
显示成功message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
/**
显示错误message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showErrorWithMessage:(NSString *)message;
/**
显示错误message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
显示错误message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled;
/**
在窗口上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
*/
+ (void)showGIFLoading;
/**
在指定的view上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
@param view 显示在哪个view上
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view;
/**
在指定的view上显示自定义GIFLoading
@param view 显示在哪个view上
@param bgColor 背景颜色, 遮盖
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled;
/**
加载下一个个播房
*/
+ (void)showAnchorLoading;
+ (void)showAnchorLoading:(UIView *)view;
/**
在窗口上显示菊花
*/
+ (void)showLoading;
/**
在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view;
/**
在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled;
/**
在窗口上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message;
/**
在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view;
/**
在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled;
+(void)showOnlyView:(UIView *)view enabled:(BOOL)enabled;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,10 +1,8 @@
//
// YMHUDTool.m
// TTPlay
//
// Created by YM on 2022/5/15.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "XNDJTDDLoadingTool.h"
#import "GCDHelper.h"
@@ -13,7 +11,7 @@
#define kDelayTime 2.5
@interface XNDJTDDLoadingTool ()
///
@property (class,nonatomic,copy) NSArray *animationImages;
@end
@@ -21,11 +19,7 @@
static NSArray * _animationImages = nil;
/**
HUD, viewnil, HUD
@param view view
*/
+ (void)hideHUDInView:(nullable UIView *)view {
dispatch_main_sync_safe(^{
if (view) {
@@ -68,39 +62,22 @@ static NSArray * _animationImages = nil;
return nil;
}
/**
HUD
*/
+ (void)hideHUD {
[self hideHUDInView:nil];
}
/**
message, , 2.5s,
@param message
*/
+ (void)showSuccessWithMessage:(NSString *)message {
[self showSuccessWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
message, 2.5s,
@param message
@param view view
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showSuccessWithMessage:message inView:view delay:kDelayTime enabled:NO];
}
/**
message
@param message
@param view view
@param afterDelay
@param enabled no: yes:
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled {
if (message.length == 0) { return; }
@@ -110,13 +87,13 @@ static NSArray * _animationImages = nil;
if (!inView) {
inView = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view]; //
[self hideHUDInView:view];
MBProgressHUD *hud = [self normalProgressHUD:view];
hud.userInteractionEnabled = enabled;
hud.mode = MBProgressHUDModeText;
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.margin = 8;
//
hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
hud.label.text = message;
hud.label.numberOfLines = 0;
@@ -126,40 +103,24 @@ static NSArray * _animationImages = nil;
});
}
/**
message, , 2.5s,
@param message
*/
+ (void)showErrorWithMessage:(NSString *)message {
[self showErrorWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
message, 2.5s,
@param message
@param view view
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showErrorWithMessage:message inView:view delay:kDelayTime enabled:NO];
}
/**
message
@param message
@param view view
@param afterDelay
@param enabled no: yes:
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled {
if (message.length == 0) { return; }
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view]; //
[self hideHUDInView:view];
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [self normalProgressHUD:view];
@@ -167,7 +128,7 @@ static NSArray * _animationImages = nil;
hud.mode = MBProgressHUDModeText;
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.margin = 8;
//
hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
hud.label.text = message;
hud.label.numberOfLines = 0;
@@ -177,52 +138,40 @@ static NSArray * _animationImages = nil;
});
}
/**
*
*/
+ (void)showLoading {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingInView:[UIApplication sharedApplication].keyWindow];
}
/**
* view
*/
+ (void)showLoadingInView:(nullable UIView *)view {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingInView:view enabled:YES];
}
/**
* view
*/
+ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingWithMessage:@"" inView:view enabled:enabled];
}
/**
* +
*/
+ (void)showLoadingWithMessage:(NSString *)message {
[self showLoadingWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
* view+
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showLoadingWithMessage:message inView:view enabled:YES];
}
/**
* view+
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled {
@@ -246,28 +195,17 @@ static NSArray * _animationImages = nil;
}
/**
GIFLoading, 0.35,
*/
+ (void)showGIFLoading {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
}
/**
viewGIFLoading, 0.35,
@param view view
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view {
[self showGIFLoadingInView:view bgColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.35] enabled:YES];
}
/**
viewGIFLoading
@param view view
@param bgColor ,
@param enabled no: yes:
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled {
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
@@ -324,7 +262,6 @@ static NSArray * _animationImages = nil;
}
+ (UIView *)loadingView {
UIView *bgView = [UIView new];
bgView.backgroundColor = [UIColor clearColor];
@@ -334,7 +271,6 @@ static NSArray * _animationImages = nil;
}
+(void)hideOnlyView:(UIView *)view{
dispatch_main_sync_safe(^{

View File

@@ -0,0 +1,391 @@
//
// YMHUDTool.m
// TTPlay
//
// Created by YM on 2022/5/15.
// Copyright © 2023 YUMI. All rights reserved.
//
#import "XNDJTDDLoadingTool.h"
#import "GCDHelper.h"
#import <MBProgressHUD/MBProgressHUD.h>
#import "MvpViewController.h"
#define kDelayTime 2.5
@interface XNDJTDDLoadingTool ()
///
@property (class,nonatomic,copy) NSArray *animationImages;
@end
@implementation XNDJTDDLoadingTool
static NSArray * _animationImages = nil;
/**
隐藏HUD, 如果view为nil, 则默认隐藏主窗口的HUD
@param view view
*/
+ (void)hideHUDInView:(nullable UIView *)view {
dispatch_main_sync_safe(^{
if (view) {
MBProgressHUD *hud = [XNDJTDDLoadingTool HUDForView:view];
if (hud != nil && hud.mode != MBProgressHUDModeText) {
hud.removeFromSuperViewOnHide = YES;
[hud hideAnimated:NO];
}
MBProgressHUD *windowHud = [XNDJTDDLoadingTool HUDForView:kWindow];
if (windowHud != nil && windowHud.mode != MBProgressHUDModeText) {
windowHud.removeFromSuperViewOnHide = YES;
[windowHud hideAnimated:NO];
}
} else {
MBProgressHUD *windowHud = [XNDJTDDLoadingTool HUDForView:kWindow];
if (windowHud != nil && windowHud.mode != MBProgressHUDModeText) {
windowHud.removeFromSuperViewOnHide = YES;
[windowHud hideAnimated:NO];
}
}
});
}
+ (MBProgressHUD *)HUDForView:(UIView *)view {
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:[MBProgressHUD class]]) {
MBProgressHUD *hud = (MBProgressHUD *)subview;
id hasFinished = [hud valueForKey:@"hasFinished"];
if (hasFinished != nil) {
if([hasFinished boolValue] == NO){
return hud;
}
}
}
}
return nil;
}
/**
隐藏HUD
*/
+ (void)hideHUD {
[self hideHUDInView:nil];
}
/**
显示成功message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showSuccessWithMessage:(NSString *)message {
[self showSuccessWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
显示成功message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showSuccessWithMessage:message inView:view delay:kDelayTime enabled:NO];
}
/**
显示成功message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled {
if (message.length == 0) { return; }
__block UIView *inView = view;
dispatch_main_sync_safe(^{
if (!inView) {
inView = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view]; // 先隐藏
MBProgressHUD *hud = [self normalProgressHUD:view];
hud.userInteractionEnabled = enabled;
hud.mode = MBProgressHUDModeText;
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.margin = 8;
// 方框背景颜色
hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
hud.label.text = message;
hud.label.numberOfLines = 0;
hud.label.textColor = [UIColor whiteColor];
hud.label.font = [UIFont systemFontOfSize:14];
[hud hideAnimated:YES afterDelay:afterDelay];
});
}
/**
显示错误message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件
@param message 文字
*/
+ (void)showErrorWithMessage:(NSString *)message {
[self showErrorWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
显示错误message, 2.5s后消失, 默认不拦截点击事件
@param message 文字
@param view 显示在哪个view上
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showErrorWithMessage:message inView:view delay:kDelayTime enabled:NO];
}
/**
显示错误message
@param message 文字
@param view 显示在哪个view上
@param afterDelay 延迟消失时间
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled {
if (message.length == 0) { return; }
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view]; // 先隐藏
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [self normalProgressHUD:view];
hud.userInteractionEnabled = enabled;
hud.mode = MBProgressHUDModeText;
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.margin = 8;
// 方框背景颜色
hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
hud.label.text = message;
hud.label.numberOfLines = 0;
hud.label.textColor = [UIColor whiteColor];
hud.label.font = [UIFont systemFontOfSize:14];
[hud hideAnimated:YES afterDelay:afterDelay];
});
}
/**
* 在窗口上显示菊花
*/
+ (void)showLoading {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingInView:[UIApplication sharedApplication].keyWindow];
}
/**
* 在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingInView:view enabled:YES];
}
/**
* 在view上显示菊花
*/
+ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
return;
[self showLoadingWithMessage:@"" inView:view enabled:enabled];
}
/**
* 在窗口上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message {
[self showLoadingWithMessage:message inView:[UIApplication sharedApplication].keyWindow];
}
/**
* 在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view {
[self showLoadingWithMessage:message inView:view enabled:YES];
}
/**
* 在view上显示菊花+文字
*/
+ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled {
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view];
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.userInteractionEnabled = enabled;
hud.bezelView.color = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
hud.removeFromSuperViewOnHide = YES;
if (message.length) {
hud.label.text = message;
hud.label.numberOfLines = 0;
hud.label.textColor = [UIColor blackColor];
hud.label.font = [UIFont systemFontOfSize:14];
}
});
}
/**
在窗口上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
*/
+ (void)showGIFLoading {
[self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow];
}
/**
在指定的view上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件
@param view 显示在哪个view上
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view {
[self showGIFLoadingInView:view bgColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.35] enabled:YES];
}
/**
在指定的view上显示自定义GIFLoading
@param view 显示在哪个view上
@param bgColor 背景颜色, 遮盖
@param enabled 是否可以拦截事件 no:不拦截 yes:拦截
*/
+ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled {
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideHUDInView:view];
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.minSize = CGSizeMake(100, 150);
hud.userInteractionEnabled = NO;
hud.mode = MBProgressHUDModeCustomView;
[hud.bezelView addSubview:[self loadingView]];
hud.backgroundColor = bgColor;
hud.bezelView.color = [UIColor clearColor];
hud.removeFromSuperViewOnHide = YES;
});
}
+ (void)showAnchorLoading {
UIView *view = [UIApplication sharedApplication].delegate.window;
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.userInteractionEnabled = NO;
hud.mode = MBProgressHUDModeCustomView;
hud.minSize = CGSizeMake(100, 150);
[hud.bezelView addSubview:[self loadingView ]];
hud.bezelView.color = [UIColor clearColor];
hud.removeFromSuperViewOnHide = YES;
});
}
+ (void)showAnchorLoading:(UIView *)view {
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.userInteractionEnabled = NO;
hud.mode = MBProgressHUDModeCustomView;
hud.minSize = CGSizeMake(100, 150);
[hud.bezelView addSubview:[self loadingView ]];
hud.bezelView.color = [UIColor clearColor];
hud.removeFromSuperViewOnHide = YES;
});
}
#pragma mark - private
+ (MBProgressHUD *)normalProgressHUD:(UIView *)view {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.removeFromSuperViewOnHide = YES;
return hud;
}
+ (UIView *)loadingView {
UIView *bgView = [UIView new];
bgView.backgroundColor = [UIColor clearColor];
bgView.frame = CGRectMake(0, 0, 100, 150);
return bgView;
}
+(void)hideOnlyView:(UIView *)view{
dispatch_main_sync_safe(^{
if (view) {
UIView *getView;
if([view isKindOfClass:[MvpViewController class]]){
getView = ((MvpViewController *)view).view;
}else{
getView = view;
}
MBProgressHUD *hud = [XNDJTDDLoadingTool HUDForView:getView];
if (hud != nil && hud.mode != MBProgressHUDModeText) {
hud.removeFromSuperViewOnHide = YES;
[hud hideAnimated:NO];
}
}
});
}
+(void)showOnlyView:(UIView *)view{
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideOnlyView:view];
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.minSize = CGSizeMake(100, 150);
hud.userInteractionEnabled = NO;
hud.mode = MBProgressHUDModeCustomView;
[hud.bezelView addSubview:[self loadingView]];
hud.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.35];
hud.bezelView.color = [UIColor clearColor];
hud.removeFromSuperViewOnHide = YES;
});
}
+(void)showOnlyView:(UIView *)view enabled:(BOOL)enabled{
if (!view) {
view = [UIApplication sharedApplication].keyWindow;
}
[self hideOnlyView:view];
dispatch_main_sync_safe(^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.minSize = CGSizeMake(100, 150);
hud.userInteractionEnabled = enabled;
hud.mode = MBProgressHUDModeCustomView;
[hud.bezelView addSubview:[self loadingView]];
hud.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.35];
hud.bezelView.color = [UIColor clearColor];
hud.removeFromSuperViewOnHide = YES;
});
}
@end

View File

@@ -1,27 +1,17 @@
//
// EPImageUploader.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import UIKit
import Foundation
/// Swift 使 QCloudCOSXML SDK
/// EPSDKManager
class EPImageUploader {
init() {}
///
/// - Parameters:
/// - images:
/// - bucket: QCloud bucket
/// - customDomain:
/// - progress: (, )
/// - success:
/// - failure:
func performBatchUpload(
_ images: [UIImage],
bucket: String,
@@ -32,7 +22,7 @@ class EPImageUploader {
) {
let total = images.count
let queue = DispatchQueue(label: "com.yumi.imageupload", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 3) // 3
let semaphore = DispatchSemaphore(value: 3)
var uploadedCount = 0
var resultList: [[String: Any]] = []
var hasError = false
@@ -42,7 +32,7 @@ class EPImageUploader {
queue.async {
semaphore.wait()
//
lock.lock()
if hasError {
lock.unlock()
@@ -51,7 +41,7 @@ class EPImageUploader {
}
lock.unlock()
//
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
lock.lock()
hasError = true
@@ -63,25 +53,25 @@ class EPImageUploader {
return
}
//
let format = UIImage.getImageType(withImageData: imageData) ?? "jpeg"
//
let uuid = NSString.createUUID()
let fileName = "image/\(uuid).\(format)"
// 使 QCloud SDK
let request = QCloudCOSXMLUploadObjectRequest<AnyObject>()
request.bucket = bucket
request.object = fileName
request.body = imageData as NSData
//
request.sendProcessBlock = { bytesSent, totalBytesSent, totalBytesExpectedToSend in
// 使
}
//
request.finishBlock = { [weak self] result, error in
guard let self = self else {
semaphore.signal()
@@ -89,7 +79,7 @@ class EPImageUploader {
}
if let error = error {
//
lock.lock()
if !hasError {
hasError = true
@@ -103,12 +93,12 @@ class EPImageUploader {
semaphore.signal()
}
} else if let result = result as? QCloudUploadObjectResult {
//
lock.lock()
if !hasError {
uploadedCount += 1
// URL UploadFile.m line 217-223
let uploadedURL = self.parseUploadURL(result.location, customDomain: customDomain)
let imageInfo: [String: Any] = [
@@ -122,12 +112,12 @@ class EPImageUploader {
let currentUploaded = uploadedCount
lock.unlock()
//
DispatchQueue.main.async {
progress(currentUploaded, total)
}
//
if currentUploaded == total {
DispatchQueue.main.async {
success(resultList)
@@ -142,17 +132,13 @@ class EPImageUploader {
}
}
//
QCloudCOSTransferMangerService.defaultCOSTransferManager().uploadObject(request)
}
}
}
/// URL UploadFile.m line 217-223
/// - Parameters:
/// - location: QCloud URL
/// - customDomain:
/// - Returns: URL
private func parseUploadURL(_ location: String, customDomain: String) -> String {
let components = location.components(separatedBy: ".com/")
if components.count == 2 {

View File

@@ -0,0 +1,163 @@
//
// EPImageUploader.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import UIKit
import Foundation
/// 图片批量上传工具(纯 Swift 内部类,直接使用 QCloudCOSXML SDK
/// 不对外暴露,由 EPSDKManager 内部调用
class EPImageUploader {
init() {}
/// 批量上传图片(内部方法)
/// - Parameters:
/// - images: 要上传的图片数组
/// - bucket: QCloud bucket 名称
/// - customDomain: 自定义域名
/// - progress: 进度回调 (已上传数, 总数)
/// - success: 成功回调
/// - failure: 失败回调
func performBatchUpload(
_ images: [UIImage],
bucket: String,
customDomain: String,
progress: @escaping (Int, Int) -> Void,
success: @escaping ([[String: Any]]) -> Void,
failure: @escaping (String) -> Void
) {
let total = images.count
let queue = DispatchQueue(label: "com.yumi.imageupload", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 3) // 最多同时上传 3 张
var uploadedCount = 0
var resultList: [[String: Any]] = []
var hasError = false
let lock = NSLock()
for (_, image) in images.enumerated() {
queue.async {
semaphore.wait()
// 检查是否已经失败
lock.lock()
if hasError {
lock.unlock()
semaphore.signal()
return
}
lock.unlock()
// 压缩图片
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
lock.lock()
hasError = true
lock.unlock()
semaphore.signal()
DispatchQueue.main.async {
failure(YMLocalizedString("error.image_compress_failed"))
}
return
}
// 获取图片格式
let format = UIImage.getImageType(withImageData: imageData) ?? "jpeg"
// 生成文件名
let uuid = NSString.createUUID()
let fileName = "image/\(uuid).\(format)"
// 直接使用 QCloud SDK 上传
let request = QCloudCOSXMLUploadObjectRequest<AnyObject>()
request.bucket = bucket
request.object = fileName
request.body = imageData as NSData
// 监听上传进度(可选)
request.sendProcessBlock = { bytesSent, totalBytesSent, totalBytesExpectedToSend in
// 单个文件的上传进度(当前不使用)
}
// 监听上传结果
request.finishBlock = { [weak self] result, error in
guard let self = self else {
semaphore.signal()
return
}
if let error = error {
// 上传失败
lock.lock()
if !hasError {
hasError = true
lock.unlock()
semaphore.signal()
DispatchQueue.main.async {
failure(error.localizedDescription)
}
} else {
lock.unlock()
semaphore.signal()
}
} else if let result = result as? QCloudUploadObjectResult {
// 上传成功
lock.lock()
if !hasError {
uploadedCount += 1
// 解析上传 URL参考 UploadFile.m line 217-223
let uploadedURL = self.parseUploadURL(result.location, customDomain: customDomain)
let imageInfo: [String: Any] = [
"resUrl": uploadedURL,
"width": image.size.width,
"height": image.size.height,
"format": format
]
resultList.append(imageInfo)
let currentUploaded = uploadedCount
lock.unlock()
// 进度回调
DispatchQueue.main.async {
progress(currentUploaded, total)
}
// 全部完成
if currentUploaded == total {
DispatchQueue.main.async {
success(resultList)
}
}
} else {
lock.unlock()
}
semaphore.signal()
} else {
semaphore.signal()
}
}
// 执行上传
QCloudCOSTransferMangerService.defaultCOSTransferManager().uploadObject(request)
}
}
}
/// 解析上传返回的 URL参考 UploadFile.m line 217-223
/// - Parameters:
/// - location: QCloud 返回的原始 URL
/// - customDomain: 自定义域名
/// - Returns: 解析后的 URL
private func parseUploadURL(_ location: String, customDomain: String) -> String {
let components = location.components(separatedBy: ".com/")
if components.count == 2 {
return "\(customDomain)/\(components[1])"
}
return location
}
}

View File

@@ -1,19 +1,17 @@
//
// EPProgressHUD.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import UIKit
import Foundation
/// Loading MBProgressHUD
@objc class EPProgressHUD: NSObject {
private static var currentHUD: MBProgressHUD?
/// window iOS 13+
private static var keyWindow: UIWindow? {
if #available(iOS 13.0, *) {
return UIApplication.shared.connectedScenes
@@ -25,20 +23,17 @@ import Foundation
}
}
///
/// - Parameters:
/// - uploaded:
/// - total:
@objc static func showProgress(_ uploaded: Int, total: Int) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
if let hud = currentHUD {
// HUD
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
hud.progress = Float(uploaded) / Float(total)
} else {
// HUD
let hud = MBProgressHUD.showAdded(to: window, animated: true)
hud.mode = .determinateHorizontalBar
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
@@ -49,8 +44,7 @@ import Foundation
}
}
///
/// - Parameter message:
@objc static func showError(_ message: String) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
@@ -64,8 +58,7 @@ import Foundation
}
}
///
/// - Parameter message:
@objc static func showSuccess(_ message: String) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
@@ -79,7 +72,7 @@ import Foundation
}
}
/// HUD
@objc static func dismiss() {
DispatchQueue.main.async {
guard let hud = currentHUD else { return }

View File

@@ -0,0 +1,92 @@
//
// EPProgressHUD.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import UIKit
import Foundation
/// 带进度的 Loading 组件(基于 MBProgressHUD
@objc class EPProgressHUD: NSObject {
private static var currentHUD: MBProgressHUD?
/// 获取当前活跃的 window兼容 iOS 13+
private static var keyWindow: UIWindow? {
if #available(iOS 13.0, *) {
return UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
/// 显示上传进度
/// - Parameters:
/// - uploaded: 已上传数量
/// - total: 总数量
@objc static func showProgress(_ uploaded: Int, total: Int) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
if let hud = currentHUD {
// 更新现有 HUD
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
hud.progress = Float(uploaded) / Float(total)
} else {
// 创建新 HUD
let hud = MBProgressHUD.showAdded(to: window, animated: true)
hud.mode = .determinateHorizontalBar
hud.label.text = String(format: YMLocalizedString("upload.progress_format"), uploaded, total)
hud.progress = Float(uploaded) / Float(total)
hud.removeFromSuperViewOnHide = true
currentHUD = hud
}
}
}
/// 显示错误提示
/// - Parameter message: 错误信息
@objc static func showError(_ message: String) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
let hud = MBProgressHUD.showAdded(to: window, animated: true)
hud.mode = .text
hud.label.text = message
hud.label.numberOfLines = 0
hud.removeFromSuperViewOnHide = true
hud.hide(animated: true, afterDelay: 2.0)
}
}
/// 显示成功提示
/// - Parameter message: 成功信息
@objc static func showSuccess(_ message: String) {
DispatchQueue.main.async {
guard let window = keyWindow else { return }
let hud = MBProgressHUD.showAdded(to: window, animated: true)
hud.mode = .text
hud.label.text = message
hud.label.numberOfLines = 0
hud.removeFromSuperViewOnHide = true
hud.hide(animated: true, afterDelay: 2.0)
}
}
/// 关闭 HUD
@objc static func dismiss() {
DispatchQueue.main.async {
guard let hud = currentHUD else { return }
hud.hide(animated: true)
currentHUD = nil
}
}
}

View File

@@ -1,13 +1,11 @@
//
// EPQCloudConfig.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import Foundation
/// QCloud UploadFileModel
struct EPQCloudConfig {
let secretId: String
let secretKey: String
@@ -20,10 +18,9 @@ struct EPQCloudConfig {
let appId: String
let accelerate: Int
/// API dictionary
/// API: GET tencent/cos/getToken
init?(dictionary: [String: Any]) {
//
guard let secretId = dictionary["secretId"] as? String,
let secretKey = dictionary["secretKey"] as? String,
let sessionToken = dictionary["sessionToken"] as? String,
@@ -42,13 +39,13 @@ struct EPQCloudConfig {
self.customDomain = customDomain
self.appId = appId
// 使
self.startTime = (dictionary["startTime"] as? Int64) ?? 0
self.expireTime = (dictionary["expireTime"] as? Int64) ?? 0
self.accelerate = (dictionary["accelerate"] as? Int) ?? 0
}
///
var isExpired: Bool {
return Date().timeIntervalSince1970 > Double(expireTime)
}

View File

@@ -0,0 +1,56 @@
//
// EPQCloudConfig.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import Foundation
/// QCloud 配置数据模型(对应 UploadFileModel
struct EPQCloudConfig {
let secretId: String
let secretKey: String
let sessionToken: String
let bucket: String
let region: String
let customDomain: String
let startTime: Int64
let expireTime: Int64
let appId: String
let accelerate: Int
/// 从 API 返回的 dictionary 初始化
/// API: GET tencent/cos/getToken
init?(dictionary: [String: Any]) {
// 必填字段检查
guard let secretId = dictionary["secretId"] as? String,
let secretKey = dictionary["secretKey"] as? String,
let sessionToken = dictionary["sessionToken"] as? String,
let bucket = dictionary["bucket"] as? String,
let region = dictionary["region"] as? String,
let customDomain = dictionary["customDomain"] as? String,
let appId = dictionary["appId"] as? String else {
return nil
}
self.secretId = secretId
self.secretKey = secretKey
self.sessionToken = sessionToken
self.bucket = bucket
self.region = region
self.customDomain = customDomain
self.appId = appId
// 可选字段使用默认值
self.startTime = (dictionary["startTime"] as? Int64) ?? 0
self.expireTime = (dictionary["expireTime"] as? Int64) ?? 0
self.accelerate = (dictionary["accelerate"] as? Int) ?? 0
}
/// 检查配置是否过期
var isExpired: Bool {
return Date().timeIntervalSince1970 > Double(expireTime)
}
}

View File

@@ -1,15 +1,11 @@
//
// EPSDKManager.swift
// YuMi
//
// Created by AI on 2025-10-11.
//
import Foundation
/// SDK
/// SDK
/// QCloud
@objc class EPSDKManager: NSObject, QCloudSignatureProvider, QCloudCredentailFenceQueueDelegate {
// MARK: - Singleton
@@ -18,22 +14,22 @@ import Foundation
// MARK: - Properties
// QCloud
private var qcloudConfig: EPQCloudConfig?
// QCloud
private var isQCloudInitializing = false
// QCloud
private var qcloudInitCallbacks: [(Bool, String?) -> Void] = []
// QCloud
private var credentialFenceQueue: QCloudCredentailFenceQueue?
// 线
private let lock = NSLock()
//
private let uploader = EPImageUploader()
// MARK: - Initialization
@@ -44,12 +40,7 @@ import Foundation
// MARK: - Public API ()
///
/// - Parameters:
/// - images:
/// - progress: (, )
/// - success:
/// - failure:
@objc func uploadImages(
_ images: [UIImage],
progress: @escaping (Int, Int) -> Void,
@@ -61,7 +52,7 @@ import Foundation
return
}
// QCloud
ensureQCloudReady { [weak self] isReady, errorMsg in
guard let self = self, isReady else {
DispatchQueue.main.async {
@@ -70,7 +61,7 @@ import Foundation
return
}
// uploader
self.uploader.performBatchUpload(
images,
bucket: self.qcloudConfig?.bucket ?? "",
@@ -82,8 +73,7 @@ import Foundation
}
}
/// QCloud
/// - Returns: true
@objc func isQCloudReady() -> Bool {
lock.lock()
defer { lock.unlock() }
@@ -96,42 +86,41 @@ import Foundation
// MARK: - Internal Methods
/// QCloud
private func ensureQCloudReady(completion: @escaping (Bool, String?) -> Void) {
if isQCloudReady() {
completion(true, nil)
return
}
//
initializeQCloud(completion: completion)
}
/// QCloud Token SDK
private func initializeQCloud(completion: @escaping (Bool, String?) -> Void) {
lock.lock()
//
if isQCloudInitializing {
qcloudInitCallbacks.append(completion)
lock.unlock()
return
}
//
if let config = qcloudConfig, !config.isExpired {
lock.unlock()
completion(true, nil)
return
}
//
isQCloudInitializing = true
qcloudInitCallbacks.append(completion)
lock.unlock()
// API QCloud Token
// API: GET tencent/cos/getToken
Api.getQCloudInfo { [weak self] (data, code, msg) in
guard let self = self else { return }
@@ -141,24 +130,24 @@ import Foundation
let dict = data?.data as? [String: Any],
let config = EPQCloudConfig(dictionary: dict) {
//
self.qcloudConfig = config
// QCloud SDK
self.configureQCloudSDK(with: config)
//
self.isQCloudInitializing = false
let callbacks = self.qcloudInitCallbacks
self.qcloudInitCallbacks.removeAll()
self.lock.unlock()
// SDK
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
callbacks.forEach { $0(true, nil) }
}
} else {
//
self.isQCloudInitializing = false
let callbacks = self.qcloudInitCallbacks
self.qcloudInitCallbacks.removeAll()
@@ -172,7 +161,7 @@ import Foundation
}
}
/// QCloud SDK UploadFile.m line 42-64
private func configureQCloudSDK(with config: EPQCloudConfig) {
let configuration = QCloudServiceConfiguration()
configuration.appID = config.appId
@@ -181,7 +170,7 @@ import Foundation
endpoint.regionName = config.region
endpoint.useHTTPS = true
// UploadFile.m line 56-59
if config.accelerate == 1 {
endpoint.suffix = "cos.accelerate.myqcloud.com"
}
@@ -189,18 +178,18 @@ import Foundation
configuration.endpoint = endpoint
configuration.signatureProvider = self
// COS
QCloudCOSXMLService.registerDefaultCOSXML(with: configuration)
QCloudCOSTransferMangerService.registerDefaultCOSTransferManger(with: configuration)
//
credentialFenceQueue = QCloudCredentailFenceQueue()
credentialFenceQueue?.delegate = self
}
// MARK: - QCloudSignatureProvider Protocol
/// UploadFile.m line 67-104
func signature(
with fields: QCloudSignatureFields,
request: QCloudBizHTTPRequest,
@@ -228,7 +217,7 @@ import Foundation
// MARK: - QCloudCredentailFenceQueueDelegate Protocol
/// UploadFile.m line 107-133
func fenceQueue(
_ queue: QCloudCredentailFenceQueue,
requestCreatorWithContinue continueBlock: @escaping QCloudCredentailFenceQueueContinue

Some files were not shown because too many files have changed in this diff Show More