This commit is contained in:
启星
2025-08-12 14:27:12 +08:00
parent 9d18b353b1
commit 1bd5e77c45
8785 changed files with 978163 additions and 2 deletions

View File

@@ -0,0 +1,17 @@
//
// NSURLSession+BJX.h
// BJXGame
//
// Created by apple on 2022/11/7.
// Copyright © 2022 鑫创. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSURLSession (QX)
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,37 @@
//
// NSURLSession+BJX.m
// BJXGame
//
// Created by apple on 2022/11/7.
// Copyright © 2022 . All rights reserved.
//
#import "NSURLSession+QX.h"
#import <objc/runtime.h>
@implementation NSURLSession (QX)
+ (void)load{
Method method1 = class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:));
Method method2 = class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:));
method_exchangeImplementations(method1, method2);
Method method3 =class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:delegate:delegateQueue:));
Method method4 =class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:delegate:delegateQueue:));
method_exchangeImplementations(method3, method4);
}
+ (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration delegate:(nullable id)delegate delegateQueue:(nullable NSOperationQueue*)queue{
if(configuration) configuration.connectionProxyDictionary=@{};
return [self px_sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
}
+ (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration{
if(configuration) configuration.connectionProxyDictionary=@{};
return [self px_sessionWithConfiguration:configuration];
}
@end

View File

@@ -0,0 +1,26 @@
//
// QXAlertViewController.h
// QXLive
//
// Created by 启星 on 2025/4/27.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger) {
///从底部向上弹出view 弹出形式类似于actionsheet
PopViewTypeBottomToUpActionSheet = 0,
///从中间弹出
PopViewTypePopFromCenter,
///从顶部向中间弹窗
PopViewTypeTopToCenter,
}PopViewType;
@interface QXAlertViewController : UIViewController
@property (nonatomic,strong)UIView *alertView;
@property (nonatomic,assign)PopViewType popType;
@property (nonatomic,assign)BOOL tapDismiss;
-(void)hideViewFinishBlock:(void(^)(void))closeBlock;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,152 @@
//
// QXAlertViewController.m
// QXLive
//
// Created by on 2025/4/27.
//
#import "QXAlertViewController.h"
@interface QXAlertViewController ()<UIGestureRecognizerDelegate>
@property(nonatomic,copy)void (^closeBlock)(void);
@end
@implementation QXAlertViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.3];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self beginShow];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];
}
-(void)beginShow{
_alertView.centerX = self.view.centerX;
switch (_popType) {
case PopViewTypeBottomToUpActionSheet:{
_alertView.y = SCREEN_HEIGHT;
[self.view addSubview:_alertView];
[UIView animateWithDuration:0.3 animations:^{
self->_alertView.y = (SCREEN_HEIGHT-self->_alertView.height);
} completion:^(BOOL finished) {
}];
}
break;
case PopViewTypePopFromCenter:{
_alertView.centerX = self.view.centerX;
_alertView.centerY = self.view.centerY;
[self.view addSubview:_alertView];
_alertView.transform = CGAffineTransformMakeScale(0.1, 0.1);
//
[UIView animateWithDuration:0.3
delay:0
usingSpringWithDamping:0.9
initialSpringVelocity:0.1
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self->_alertView.transform = CGAffineTransformIdentity;
} completion:nil];
}
break;
case PopViewTypeTopToCenter:{
_alertView.y = -SCREEN_HEIGHT;
[self.view addSubview:_alertView];
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self->_alertView.y = (SCREEN_HEIGHT-self->_alertView.height)/2.0;
} completion:^(BOOL finished) {
}];
}
break;
default:
break;
}
}
-(void)tapAction{
if (!self.tapDismiss) {
return;
}
[self hideViewFinishBlock:self.closeBlock];
}
-(void)hideViewFinishBlock:(void (^)(void))closeBlock{
MJWeakSelf
switch (_popType) {
case PopViewTypeBottomToUpActionSheet:{
[UIView animateWithDuration:0.3 animations:^{
self->_alertView.y = SCREEN_HEIGHT;
} completion:^(BOOL finished) {
[self->_alertView removeFromSuperview];
[self dismissViewControllerAnimated:NO completion:^{
if (closeBlock) {
closeBlock();
}
}];
}];
}
break;
case PopViewTypePopFromCenter:{
[UIView animateWithDuration:0.3
delay:0
usingSpringWithDamping:0.9
initialSpringVelocity:1
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self->_alertView.transform = CGAffineTransformMakeScale(0.001, 0.001);
} completion:^(BOOL finished) {
[self->_alertView removeFromSuperview];
[self dismissViewControllerAnimated:NO completion:^{
if (closeBlock) {
closeBlock();
}
}];
}];
}
break;
case PopViewTypeTopToCenter:{
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self->_alertView.y = SCREEN_HEIGHT;
} completion:^(BOOL finished) {
[self->_alertView removeFromSuperview];
[self dismissViewControllerAnimated:NO completion:^{
if (closeBlock) {
closeBlock();
}
}];
}];
}
break;
default:
break;
}
}
-(void)setPopType:(PopViewType)popType{
_popType = popType;
}
-(void)setAlertView:(UIView *)alertView{
if (_alertView) {
[_alertView removeFromSuperview];
_alertView = nil;
}
_alertView = alertView;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return touch.view == self.view;
}
@end

View File

@@ -0,0 +1,16 @@
//
// QXBadgeButton.h
// QXLive
//
// Created by 启星 on 2025/7/28.
//
#import <UIKit/UIKit.h>
#import "TIMCommonModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface QXBadgeButton : UIButton
@property (nonatomic,strong)TUIUnReadView *unreadView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,64 @@
//
// QXBadgeButton.m
// QXLive
//
// Created by on 2025/7/28.
//
#import "QXBadgeButton.h"
@implementation QXBadgeButton
- (instancetype)init
{
self = [super init];
if (self) {
[self addSubview:self.unreadView];
[self.unreadView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self);
make.right.equalTo(self);
make.width.mas_equalTo(kScale375(17));
make.height.mas_equalTo(kScale375(17));
}];
[self.unreadView.unReadLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.unreadView);
make.size.mas_equalTo(self.unreadView.unReadLabel);
}];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.unreadView];
[self.unreadView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self);
make.right.equalTo(self);
make.width.mas_equalTo(kScale375(17));
make.height.mas_equalTo(kScale375(17));
}];
[self.unreadView.unReadLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.unreadView);
make.size.mas_equalTo(self.unreadView.unReadLabel);
}];
}
return self;
}
-(TUIUnReadView *)unreadView{
if (!_unreadView) {
_unreadView = [[TUIUnReadView alloc] init];
_unreadView.userInteractionEnabled = NO;
[_unreadView setNum:0];
[_unreadView.unReadLabel sizeToFit];
_unreadView.layer.cornerRadius = kScale375(10);
[_unreadView.layer masksToBounds];
}
return _unreadView;
}
@end

16
QXLive/Base/QXBaseModel.h Normal file
View File

@@ -0,0 +1,16 @@
//
// QXBaseModel.h
// QXLive
//
// Created by 启星 on 2025/5/13.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface QXBaseModel : NSObject
@end
NS_ASSUME_NONNULL_END

12
QXLive/Base/QXBaseModel.m Normal file
View File

@@ -0,0 +1,12 @@
//
// QXBaseModel.m
// QXLive
//
// Created by on 2025/5/13.
//
#import "QXBaseModel.h"
@implementation QXBaseModel
@end

View File

@@ -0,0 +1,16 @@
//
// QXBaseNavigationController.h
// QXLive
//
// Created by 启星 on 2025/4/24.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface QXBaseNavigationController : UINavigationController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,79 @@
//
// QXBaseNavigationController.m
// QXLive
//
// Created by on 2025/4/24.
//
#import "QXBaseNavigationController.h"
@interface QXBaseNavigationController ()<UINavigationControllerDelegate,UIGestureRecognizerDelegate>
@end
@implementation QXBaseNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.interactivePopGestureRecognizer.delegate = self;
self.delegate = self;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.viewControllers.count == 1) {
viewController.hidesBottomBarWhenPushed = YES;
}
// if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
// self.interactivePopGestureRecognizer.enabled = YES;
// }
[super pushViewController:viewController animated:animated];
}
//-(UIViewController *)popViewControllerAnimated:(BOOL)animated{
// if (self.childViewControllers.count == 2) {
// if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
// self.interactivePopGestureRecognizer.enabled = YES;
// }
// }
// return [super popViewControllerAnimated:animated];
//}
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.childViewControllers.count > 1) {
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
if ([self.visibleViewController isKindOfClass:NSClassFromString(@"QXRoomViewController")]) {
self.interactivePopGestureRecognizer.enabled = NO;
}else{
self.interactivePopGestureRecognizer.enabled = YES;
}
}
}else{
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (self.viewControllers.count == 1) {
return NO;
}
return YES;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end

View File

@@ -0,0 +1,26 @@
//
// QXBaseViewController.h
// QXLive
//
// Created by 启星 on 2025/4/24.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface QXBaseViewController : UIViewController
@property (nonatomic,strong)NSMutableArray *dataArray;
@property (nonatomic,assign)NSInteger page;
@property (nonatomic,assign)BOOL bgImageHidden;
//交给子类来实现
-(void)setNavgationItems;
-(void)initSubViews;
- (void)getData;
/**
imageurl :图片名称或链接
*/
-(void)updateBgImage:(NSString*)imageUrl;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,79 @@
//
// QXBaseViewController.m
// QXLive
//
// Created by on 2025/4/24.
//
#import "QXBaseViewController.h"
@interface QXBaseViewController ()
@property (nonatomic,strong)UIImageView *bgImageView;
@property (nonatomic,strong)NSString *imageUrl;
@end
@implementation QXBaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view insertSubview:self.bgImageView atIndex:0];
[self updateBgImage:QXConfig.backgroundImage];
[self initSubViews];
[self setNavgationItems];
[self getData];
self.page = 1;
}
- (void)initSubViews{
}
- (void)getData{
}
-(void)setNavgationItems{
UIButton*backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[backBtn setImage:[UIImage imageNamed:@"back"] forState:(UIControlStateNormal)];
backBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeading;
[backBtn addTarget:self action:@selector(backAction) forControlEvents:(UIControlEventTouchUpInside)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backBtn];
}
-(void)backAction{
if (self.navigationController.viewControllers.count > 1) {
[self.navigationController popViewControllerAnimated:YES];
return;
}else{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
}
-(void)setBgImageHidden:(BOOL)bgImageHidden{
_bgImageHidden = bgImageHidden;
self.bgImageView.hidden = bgImageHidden;
}
-(void)updateBgImage:(NSString *)imageUrl{
_imageUrl = imageUrl;
if (![imageUrl isExist]) {
return;
}
if ([imageUrl hasPrefix:@"http"]) {
[self.bgImageView sd_setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:nil];
}else{
self.bgImageView.image = [UIImage imageNamed:imageUrl];
}
}
-(UIImageView *)bgImageView{
if (!_bgImageView) {
_bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
_bgImageView.contentMode = UIViewContentModeScaleAspectFill;
}
return _bgImageView;
}
-(NSMutableArray *)dataArray{
if (!_dataArray) {
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
-(void)dealloc{
QXLOG(@"%@已销毁",NSStringFromClass([self class]));
}
@end

View File

@@ -0,0 +1,23 @@
//
// TBaseWebViewController.h
// YSDTrucksProject
//
// Created by 党凯 on 2020/7/3.
// Copyright © 2020 党凯. All rights reserved.
//
#import "QXBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface QXBaseWebViewController : QXBaseViewController
//网页链接
@property(nonatomic,copy)NSString *urlStr;
//html内容
@property(nonatomic,copy)NSString *content;
/** 进度条颜色 */
@property (nonatomic, assign) UIColor *progressColor;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,243 @@
//
// QXBaseWebViewController.m
// YSDTrucksProject
//
// Created by on 2020/7/3.
// Copyright © 2020 . All rights reserved.
//
#import "QXBaseWebViewController.h"
#import <WebKit/WebKit.h>
static void *WKWebBrowserContext = &WKWebBrowserContext;
@interface QXBaseWebViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler>
@property(nonatomic,strong)WKWebView *contentWebView;
@property(nonatomic,strong)UIProgressView *progressView;
//@property (nonatomic,strong)TitleView *titleView;
@end
@implementation QXBaseWebViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// if (@available(iOS 11.0, *)) {
// self.contentWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
// } else {
// self.automaticallyAdjustsScrollViewInsets = NO;
// }
[self.view addSubview:self.contentWebView];
[self.view addSubview:self.progressView];
[self layoutConstraints];
[self loadData];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
-(void)setNavgationItems{
[super setNavgationItems];
}
- (void)layoutConstraints {
[self.contentWebView setFrame:CGRectMake(0, NavContentHeight, SCREEN_WIDTH, SCREEN_HEIGHT-(NavContentHeight))];
[self.progressView setFrame:CGRectMake(0, NavContentHeight, SCREEN_WIDTH
, 2)];
}
- (void)loadData {
if (self.urlStr.length) {
if ([self.urlStr hasPrefix:@"http"]) {
NSURL* url=[NSURL URLWithString:self.urlStr];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
[self.contentWebView loadRequest:request];
}else{
NSURL* url=[NSURL fileURLWithPath:self.urlStr];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
[self.contentWebView loadRequest:request];
}
return;
}
if (self.content.length) {
[self.contentWebView loadHTMLString:self.content baseURL:nil];
return;
}
}
#pragma mark - WKNavigationDelegate
//
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
//
self.progressView.hidden = NO;
}
//
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
//
if (self.title.length == 0) {
self.title = self.contentWebView.title;
}
// if ([self.titleString containsString:@"转账"]) {
// //
// NSString *fontFamilyStr = @"document.getElementsByTagName('body')[0].style.fontFamily='Arial';";
// [webView evaluateJavaScript:fontFamilyStr completionHandler:nil];
// //
// [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= '#333333'" completionHandler:nil];
// //
// [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '150%'"completionHandler:nil];
// }
}
//
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
//
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
//
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
}
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"message.name====%@ body=%@",message.name,message.body);
if([message.name isEqualToString:@"toRegister"]){
// RegistVC *vc = [[RegistVC alloc] init];
// vc.isRegist = YES;
// [self.navigationController pushViewController:vc animated:YES];
// NSMutableArray *vcArr = [[NSMutableArray alloc]initWithArray:self.navigationController.viewControllers];
// for (UIViewController *vc in vcArr) {
// if ([vc isKindOfClass:[self class]]) {
// [vcArr removeObject:vc];
// break;
// }
// }
// self.navigationController.viewControllers = vcArr;
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//
if (navigationAction.targetFrame == nil) {
[webView loadRequest:navigationAction.request];
}
NSURL *URL = navigationAction.request.URL;
[self dealSomeThing:URL];
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)dealSomeThing:(NSURL *)url{
NSString *scheme = [url scheme];
NSString *resourceSpecifier = [url resourceSpecifier];
if ([scheme isEqualToString:@"tel"]) {
NSString *callPhone = [NSString stringWithFormat:@"tel://%@", resourceSpecifier];
/// iOS 10
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:callPhone]];
});
}
}
//
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
}
//KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == self.contentWebView) {
[self.progressView setAlpha:1.0f];
BOOL animated = self.contentWebView.estimatedProgress > self.progressView.progress;
[self.progressView setProgress:self.contentWebView.estimatedProgress animated:animated];
// Once complete, fade out UIProgressView
if(self.contentWebView.estimatedProgress >= 1.0f) {
[UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.progressView setAlpha:0.0f];
} completion:^(BOOL finished) {
[self.progressView setProgress:0.0f animated:NO];
}];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - getters and setters
- (WKWebView *)contentWebView {
if (!_contentWebView) {
//
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];
//
configuration.selectionGranularity = YES;
// webpr
configuration.processPool = [[WKProcessPool alloc] init];
//, jsoc(OCURL)
WKUserContentController * UserContentController = [[WKUserContentController alloc]init];
//
configuration.suppressesIncrementalRendering = YES;
//
[UserContentController addScriptMessageHandler:self name:@"toRegister"];
configuration.preferences.javaScriptEnabled = YES;
configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
configuration.userContentController = UserContentController;
// iOS9iOS8
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
//
configuration.allowsAirPlayForMediaPlayback = YES;
// 线
configuration.allowsInlineMediaPlayback = YES;
// NOios8
_contentWebView.allowsBackForwardNavigationGestures = YES;
}
_contentWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
_contentWebView.backgroundColor = [UIColor whiteColor];
_contentWebView.opaque = YES;
//
[_contentWebView sizeToFit];
_contentWebView.scrollView.showsVerticalScrollIndicator = NO;
//
_contentWebView.navigationDelegate = self;
//kvo
[_contentWebView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:WKWebBrowserContext];
}
return _contentWebView;
}
- (UIProgressView *)progressView {
if (!_progressView) {
_progressView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleDefault];
[_progressView setTrackTintColor:[UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1.0]];
_progressView.progressTintColor = QXConfig.themeColor;
}
return _progressView;
}
-(void)setProgressColor:(UIColor *)progressColor{
_progressView.progressTintColor = progressColor;
}
// dealloc
- (void)dealloc {
if (self) {
[self.contentWebView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end

116
QXLive/Base/QXGlobal.h Normal file
View File

@@ -0,0 +1,116 @@
//
// QXGlobal.h
// YSDTrucksProject
//
// Created by 党凯 on 2020/6/30.
// Copyright © 2020 党凯. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QXAlertViewController.h"
#import "QXLoginModel.h"
#import "QXMiniRoomView.h"
#import "QXDayTaskModel.h"
#import "QXRoomViewController.h"
typedef void (^closeBlock)(void);
typedef void (^showFinishBlock)(void);
typedef NS_ENUM(NSInteger) {
/// 房主
QXRoomRoleTypeOwner = 0,
/// 主持
QXRoomRoleTypeCompere,
/// 管理员
QXRoomRoleTypeManager,
/// 观众
QXRoomRoleTypeAudience,
}QXRoomRoleType;
@interface QXGlobal : NSObject
+(instancetype)shareGlobal;
// 是否登录
@property (nonatomic,readonly,assign)BOOL isLogin;
@property (nonatomic,assign)BOOL isShowLoginVC;
/// 房间id
@property (nonatomic,strong,readonly)NSString *roomId;
// 是否实名
@property (nonatomic,assign)BOOL isRealName;
/// 钻石兑币比例
@property (nonatomic,strong)NSString *coin_exchange_rate;
/// 提现手续费
@property (nonatomic,strong)NSString *withdrawal_service_fee;
/// 购买金币比例
@property (nonatomic,strong)NSString *rmb_coin_ratio;
@property (nonatomic,strong)QXLoginModel *loginModel;
// 初始化时是否支持一键登录
@property (nonatomic,assign)BOOL canOneLogin;
@property (nonatomic,strong)QXAlertViewController *alertViewController;
@property (nonatomic,strong) QXMiniRoomView *miniView;
/// 每日任务id
@property (nonatomic,strong) QXDayTaskListModel *taskModel;
@property (nonatomic,strong) QXRoomViewController *roomVC;
-(void)miniRoomWithRoomId:(NSString*)roomId roomCover:(NSString*)roomCover;
-(BOOL)isOpenRecharge;
-(UIWindow*)getKeyWindow;
/// 退出登录
-(void)logOut;
-(void)removeLocalData;
/// 保存个人信息
-(void)saveLoginData:(NSString*)userInfoJson;
/// 更新个人信息
-(void)updateUserInfoWithMolde:(QXLoginModel *)loginModel;
-(void)removeMemory;
-(void)toLogin;
-(void)showView:(UIView *)view popType:(PopViewType)popType tapDismiss:(BOOL)tapDismiss finishBlock:(showFinishBlock)finishBlock;
-(void)showView:(UIView *)view controller:(UIViewController*)controller popType:(PopViewType)popType tapDismiss:(BOOL)tapDismiss finishBlock:(showFinishBlock)finishBlock;
-(void)hideViewBlock:(closeBlock)closeBlock;
/**
去聊天
*/
-(void)chatWithUserID:(NSString*)userId
nickname:(NSString*)nickname
avatar:(NSString*)avatar
navagationController:(UINavigationController*)navagationController;
/**
去群聊
*/
-(void)chatWithGroupId:(NSString*)groupId
cover:(NSString*)cover
name:(NSString*)name
navagationController:(UINavigationController*)navagationController;
/**
加入房间
*/
-(void)joinRoomWithRoomId:(NSString*)roomId
isRejoin:(BOOL)isRejoin
navagationController:(UINavigationController*)navagationController;
/**
退出房间
*/
-(void)quitRoomWithRoomId:(NSString*)roomId;
/// 去完成任务
-(void)finishTask;
-(void)vibrationFeedback;
@end

297
QXLive/Base/QXGlobal.m Normal file
View File

@@ -0,0 +1,297 @@
//
// QXGlobal.m
// TrucksProject
//
// Created by on 2020/6/30.
// Copyright © 2020 . All rights reserved.
//
#import "QXGlobal.h"
#import "AppDelegate.h"
#import "QXLoginViewController.h"
#import "QXBaseNavigationController.h"
#import "TUILogin.h"
#import "QXChatViewController.h"
#import "QXRoomViewController.h"
#import "QXMineNetwork.h"
#import "QXGiftPlayerManager.h"
#import "QXAgoraEngine.h"
#import "QXRoomMessageManager.h"
#import "QXTimer.h"
#import <TIMPush/TIMPushManager.h>
#import "QXManagerMqtt.h"
@interface QXGlobal()
@property (nonatomic,assign)BOOL isLogin;
@property (nonatomic,strong)NSString *roomId;
@property (nonatomic,strong)QXTimer *timer;
@property (nonatomic,assign)NSInteger taskTime;
@end
@implementation QXGlobal
+ (instancetype)shareGlobal {
static QXGlobal *global = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
global = [[self alloc] init];
global.isShowLoginVC = NO;
});
return global;
}
-(UIWindow *)getKeyWindow{
AppDelegate*appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
UIWindow *window = appDelegate.window;
return window;
}
-(void)logOut{
[TUILogin logout:^{
QXLOG(@"腾讯im退出登录成功");
} fail:^(int code, NSString * _Nullable msg) {
QXLOG(@"腾讯im退出登录失败");
}];
self.isLogin = NO;
[self removeLocalData];
[self removeMemory];
AppDelegate *appdelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
///
[TIMPushManager removePushListener:appdelegate];
///
[[V2TIMManager sharedInstance] removeConversationListener:appdelegate];
[TIMPushManager unRegisterPush:^{
//success
} fail:^(int code, NSString * _Nonnull desc) {
//error
}];
if (self.isShowLoginVC == YES) {
return;
}
QXLoginViewController *vc = [[QXLoginViewController alloc] init];
QXBaseNavigationController *na = [[QXBaseNavigationController alloc] initWithRootViewController:vc];
na.modalPresentationStyle = UIModalPresentationFullScreen;
[self.getKeyWindow.rootViewController presentViewController:na animated:YES completion:nil];
self.isShowLoginVC = YES;
/// mqtt
[[QXManagerMqtt sharedInstance] disconnect];
}
-(void)saveLoginData:(NSString *)userInfoJson{
[[NSUserDefaults standardUserDefaults] setObject:userInfoJson forKey:kUserLoginData];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)updateUserInfoWithMolde:(QXLoginModel *)loginModel{
NSString *jsonStr = [loginModel yy_modelToJSONString];
[self saveLoginData:jsonStr];
}
//-(void)setLoginModel:(QXLoginModel *)loginModel{
// _loginModel = loginModel;
// if (loginModel == nil) {
// self.isLogin = NO;
// }else{
// self.isLogin = YES;
// }
//}
-(BOOL)isRealName{
return [QXGlobal shareGlobal].loginModel.auth == 1;
}
-(QXLoginModel *)loginModel{
if (!_loginModel) {
NSString *jsonStr = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLoginData];
if (jsonStr.length > 0) {
_loginModel = [QXLoginModel yy_modelWithJSON:jsonStr];
self.isLogin = YES;
}else{
_loginModel = nil;
self.isLogin = NO;
}
}
return _loginModel;
}
-(void)toLogin{
}
-(BOOL)isLogin{
NSString *jsonStr = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLoginData];
return jsonStr.length > 0;
}
-(void)removeLocalData{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kUserLoginData];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)removeMemory{
QXGlobal.shareGlobal.loginModel = nil;
}
-(void)showView:(UIView *)view popType:(PopViewType)popType tapDismiss:(BOOL)tapDismiss finishBlock:(showFinishBlock)finishBlock{
self.alertViewController = [[QXAlertViewController alloc] init];
self.alertViewController.popType = popType;
self.alertViewController.alertView = view;
self.alertViewController.tapDismiss = tapDismiss;
self.alertViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[[QXGlobal shareGlobal].getKeyWindow.rootViewController presentViewController:self.alertViewController animated:NO completion:finishBlock];
}
-(void)showView:(UIView *)view controller:(UIViewController*)controller popType:(PopViewType)popType tapDismiss:(BOOL)tapDismiss finishBlock:(showFinishBlock)finishBlock{
self.alertViewController = [[QXAlertViewController alloc] init];
self.alertViewController.popType = popType;
self.alertViewController.alertView = view;
self.alertViewController.tapDismiss = tapDismiss;
self.alertViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[controller presentViewController:self.alertViewController animated:NO completion:finishBlock];
}
-(void)hideViewBlock:(closeBlock)closeBlock{
[self.alertViewController hideViewFinishBlock:closeBlock];
}
-(BOOL)isOpenRecharge{
BOOL isOpenRecharge = [[NSUserDefaults standardUserDefaults] boolForKey:kIsOpenRecharge];
return isOpenRecharge;
}
-(void)finishTask{
if (QXGlobal.shareGlobal.taskModel != nil) {
[QXMineNetwork dayTaskFinishedWithTaskId:QXGlobal.shareGlobal.taskModel.task_id successBlock:^(NSDictionary * _Nonnull dict) {
NSString *is_completed = [NSString stringWithFormat:@"%@",dict[@"is_completed"]];
if (is_completed.intValue == 1) {
QXGlobal.shareGlobal.taskModel = nil;
QXGlobal.shareGlobal.taskTime = 0;
if (self->_timer) {
[self->_timer invalidate];
self->_timer = nil;
}
}
} failBlock:^(NSError * _Nonnull error, NSString * _Nonnull msg) {
}];
}
}
-(void)chatWithUserID:(NSString *)userId nickname:(NSString *)nickname avatar:(NSString *)avatar navagationController:(UINavigationController *)navagationController{
QXChatViewController *vc = [[QXChatViewController alloc] init];
TUIChatConversationModel *data = [[TUIChatConversationModel alloc] init];
data.userID = [NSString stringWithFormat:@"u%@",userId];
data.title = nickname;
data.faceUrl = avatar;
vc.data = data;
[navagationController pushViewController:vc animated:YES];
}
-(void)chatWithGroupId:(NSString *)groupId cover:(NSString *)cover name:(NSString *)name navagationController:(UINavigationController *)navagationController{
QXChatViewController *vc = [[QXChatViewController alloc] init];
TUIChatConversationModel *data = [[TUIChatConversationModel alloc] init];
if ([groupId containsString:@"g"]) {
data.groupID = groupId;
}else{
data.groupID = [NSString stringWithFormat:@"g%@",groupId];
}
data.faceUrl = cover;
data.title = name;
vc.data = data;
[navagationController pushViewController:vc animated:YES];
}
-(void)joinRoomWithRoomId:(NSString *)roomId isRejoin:(BOOL)isRejoin navagationController:(UINavigationController *)navagationController{
if (!QXGlobal.shareGlobal.isLogin) {
[[QXGlobal shareGlobal] logOut];
return;
}
navagationController = (QXBaseNavigationController*)KEYWINDOW.rootViewController;
navagationController.interactivePopGestureRecognizer.enabled = NO;
if (_miniView) {
[_miniView removeFromSuperview];
_miniView = nil;
}
if ([roomId isEqualToString:self.roomId]) {
if ([navagationController.viewControllers containsObject:self.roomVC]) {
return;
}else{
self.roomVC.hidesBottomBarWhenPushed = YES;
[navagationController pushViewController:self.roomVC animated:YES];
}
return;
}
if (self.roomId && ![self.roomId isEqualToString:roomId]) {
[self quitRoomWithRoomId:self.roomId];
}
if (isRejoin) {
if (self.roomVC!=nil) {
self.roomVC.hidesBottomBarWhenPushed = YES;
[navagationController pushViewController:self.roomVC animated:YES];
}else{
self.roomVC = [[QXRoomViewController alloc] init];
self.roomVC.roomId = roomId;
self.roomVC.isReJoin = isRejoin;
self.roomId = roomId;
self.roomVC.hidesBottomBarWhenPushed = YES;
[navagationController pushViewController:self.roomVC animated:YES];
}
}else{
if ([roomId isEqualToString:self.roomVC.roomId]) {
[navagationController pushViewController:self.roomVC animated:YES];
}else{
self.roomVC = [[QXRoomViewController alloc] init];
self.roomVC.roomId = roomId;
self.roomVC.isReJoin = isRejoin;
self.roomId = roomId;
self.roomVC.hidesBottomBarWhenPushed = YES;
[navagationController pushViewController:self.roomVC animated:YES];
}
}
if (QXGlobal.shareGlobal.taskModel != nil) {
///
if (QXGlobal.shareGlobal.taskModel.task_id.intValue == 9) {
///
if (_timer == nil) {
_timer = [QXTimer scheduledTimerWithTimeInterval:1 repeats:YES queue:dispatch_get_main_queue() block:^{
QXGlobal.shareGlobal.taskTime++;
if (QXGlobal.shareGlobal.taskTime%60 == 0) {
[[QXGlobal shareGlobal] finishTask];
}
}];
}
}
}
}
-(void)quitRoomWithRoomId:(NSString *)roomId{
///
[[QXGiftPlayerManager shareManager] destroyEffectSvga];
/// 退
[[QXAgoraEngine sharedEngine] leaveChannel];
[[QXAgoraEngine sharedEngine] ktv_DestoryKtvPlayer];
[[QXAgoraEngine sharedEngine] destroyEngine];
/// 退
[[QXRoomMessageManager shared] quitGroupWithRoomId:roomId];
self.roomId = nil;
_roomVC = nil;
/// http退
[QXMineNetwork quitRoomWithRoomId:roomId user_id:[QXGlobal shareGlobal].loginModel.user_id successBlock:^(NSDictionary * _Nonnull dict) {
} failBlock:^(NSError * _Nonnull error, NSString * _Nonnull msg) {
}];
if (_timer) {
[_timer invalidate];
_timer = nil;
}
}
-(void)miniRoomWithRoomId:(NSString*)roomId roomCover:(NSString*)roomCover{
self.miniView.roomId = self.roomId;
self.miniView.roomCoverImage = roomCover;
[self.miniView show];
}
-(QXMiniRoomView *)miniView{
if (!_miniView) {
_miniView = [[QXMiniRoomView alloc] init];
}
return _miniView;
}
-(void)vibrationFeedback{
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
[generator impactOccurred];
}
@end

View File

@@ -0,0 +1,38 @@
//
// QXLocationManager.h
// YSDTrucksProject
//
// Created by 党凯 on 2020/7/17.
// Copyright © 2020 党凯. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol QXLocationManagerDelegate <NSObject>
@optional
-(void)locationSuccessWithCity:(NSString*_Nonnull)city province:(NSString*_Nonnull)province area:(NSString*_Nonnull)area address:(NSString*_Nonnull)address;
@end
NS_ASSUME_NONNULL_BEGIN
@interface QXLocationManager : NSObject
@property (nonatomic,weak)id<QXLocationManagerDelegate>delegate;
+(instancetype)shareManager;
/**
开始定位
*/
-(void)startLoction;
/**
结束定位
*/
-(void)stopLoction;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,155 @@
//
// QXLocationManager.m
// YSDTrucksProject
//
// Created by on 2020/7/17.
// Copyright © 2020 . All rights reserved.
//
#import "QXLocationManager.h"
@interface QXLocationManager()<CLLocationManagerDelegate>
@property (nonatomic,strong)CLLocationManager *lcManager;
@end
@implementation QXLocationManager
+(instancetype)shareManager{
static QXLocationManager *manager = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
manager = [[QXLocationManager alloc] init];
});
return manager;
}
- (instancetype)init
{
self = [super init];
if (self) {
// [AMapServices sharedServices].apiKey = mapKey;
// _locationManager = [[AMapLocationManager alloc] init];
// //
// [_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];
// // 2s2s
// _locationManager.locationTimeout =2;
// // 2s2s
// _locationManager.reGeocodeTimeout = 2;
// _locationManager.delegate = self;
// if ([CLLocationManager authorizationStatus]) {
//
self.lcManager = [[CLLocationManager alloc] init];
self.lcManager.delegate = self; //
// ()
self.lcManager.distanceFilter = 100;
self.lcManager.desiredAccuracy = kCLLocationAccuracyBest; // ()
[self.lcManager requestWhenInUseAuthorization];//
// [self.lcManager startUpdatingLocation]; //
// }else{
//
// //
//
// }
}
return self;
}
//-(void)amapLocationManager:(AMapLocationManager *)manager doRequireLocationAuth:(CLLocationManager *)locationManager{
// if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
// [locationManager requestAlwaysAuthorization];
// }
//}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(@"定位到了");
__weak typeof(self)weakSelf = self;
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:[locations firstObject] completionHandler:^(NSArray<CLPlacemark *> *_Nullable placemarks, NSError * _Nullable error) {
CLPlacemark *place = [placemarks firstObject];
//place
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(locationSuccessWithCity:province:area:address:)]) {
///
NSString *province = place.administrativeArea;
///
NSString *city = place.locality;
///
NSString *area = place.subLocality;
[weakSelf.delegate locationSuccessWithCity:place.locality province:province area:area address:place.name];
}
}];
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(@"获取定位失败");
}
-(void)startLoction{
if (![self checkAuthorization]) {
UIAlertController *al = [UIAlertController alertControllerWithTitle:@"您没有打开定位权限" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
[al addAction:[UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
}]];
[al addAction:[UIAlertAction actionWithTitle:@"去设置" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
}]];
[KEYWINDOW.rootViewController presentViewController:al animated:YES completion:nil];
// [self.locationManager requestLocationWithReGeocode:YES completionBlock:^(CLLocation *location, AMapLocationReGeocode *regeocode, NSError *error) {
// resultBlock(location,regeocode,error);
// }];
[self.lcManager startUpdatingLocation];
}else{
// [self.locationManager requestLocationWithReGeocode:YES completionBlock:^(CLLocation *location, AMapLocationReGeocode *regeocode, NSError *error) {
// resultBlock(location,regeocode,error);
// }];
[self.lcManager startUpdatingLocation];
}
}
-(void)stopLoction{
[self.lcManager stopUpdatingLocation];
}
//
-(BOOL)checkAuthorization{
BOOL result = NO;
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
switch (status) {
//
case kCLAuthorizationStatusDenied:
result = NO;
break;
//使
case kCLAuthorizationStatusNotDetermined:
result = YES;
break;
//
case kCLAuthorizationStatusRestricted:
result = NO;
break;
//App使
case kCLAuthorizationStatusAuthorizedAlways:
result = YES;
break;
//使使
case kCLAuthorizationStatusAuthorizedWhenInUse:
result = YES;
break;
default:
break;
}
return result;
}
@end

View File

@@ -0,0 +1,17 @@
//
// WKWebView+QX.h
// YBLive
//
// Created by 启星 on 2025/4/16.
// Copyright © 2025 cat. All rights reserved.
//
#import <WebKit/WebKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface WKWebView (QX)
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,50 @@
//
// WKWebView+QX.m
// YBLive
//
// Created by on 2025/4/16.
// Copyright © 2025 cat. All rights reserved.
//
#import "WKWebView+QX.h"
@implementation WKWebView (QX)
+ (void)load{
Method method1 = class_getInstanceMethod([self class],@selector(loadRequest:));
Method method2 = class_getInstanceMethod([self class],@selector(qx_loadRequest:));
method_exchangeImplementations(method1, method2);
}
-(void)qx_loadRequest:(NSURLRequest*)request{
if ([self isProxyEnabled]) {
[self qx_loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https:www.baidu.com"]]];
}else{
[self qx_loadRequest:request];
}
}
-(BOOL)isProxyEnabled{
NSArray *criticalDomains = @[
H5ServerUrl,
ServerUrl,
];
NSDictionary *proxySettings = (__bridge NSDictionary *)CFNetworkCopySystemProxySettings();
for (NSString *domain in criticalDomains) {
NSArray *proxies = (__bridge NSArray *)CFNetworkCopyProxiesForURL(
(__bridge CFURLRef)[NSURL URLWithString:domain],
(__bridge CFDictionaryRef)proxySettings
);
// ...
for (NSDictionary *proxy in proxies) {
NSString *proxyType = proxy[(NSString *)kCFProxyTypeKey];
if (![proxyType isEqualToString:(NSString *)kCFProxyTypeNone]) {
return YES; // VPN
}
}
}
return NO;
}
@end