Get type of NSNumber

本文介绍了一种使用 Objective-C 的 NSNumber 类来确定其内部值类型的实用方法。通过调用 objCType 方法并比较返回的字符串与预定义的类型编码,可以准确判断 NSNumber 对象所包含的数据类型。

I recommend using the -[NSNumber objCType] method.

It allows you to do:

NSNumber * n = [NSNumber numberWithBool:YES];
if (strcmp([n objCType], @encode(BOOL)) == 0) {
   
NSLog(@"this is a bool");
} else if (strcmp([n objCType], @encode(int)) == 0) {
   
NSLog(@"this is an int");
}

For more information on type encodings, check out the Objective-C Runtime Reference.

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// // TPSSAppContext.mm // SurveillanceCore // // Created by ChenYongan on 6/13/16. // Copyright © 2016 TP-LINK. All rights reserved. // #import "TPSSAppContext.h" #import "TPSSNotification.h" // FIXME //#import "TPSSLocalizationConstants.h" #include "IPCAppContext.h" #include "tpwlog.h" #include <set> #include "TPECCommonMethods.h" #import "TPSSAppContext+Private.h" #import <TPFoundation/TPLocalizationUtils.h> #define APPCONTEXT_LOG_TAG "TPSSAppContext:: " static BasicToken getBasicToken(const char *pcToken) { BasicToken token = BasicToken(); if (pcToken != NULL){ strlcpy(token.pcToken, [[TPECCommonMethods AES256DencryptWithTokenString:[NSString stringWithUTF8String: pcToken]] UTF8String], TPWCOMM_MAX_TOKEN_LENGTH); } return token; } TPBasicTokenCallMethod getBasicTokenMethod = { getBasicToken }; static struct tm LocalTime(long long time) { NSDate *date = [NSDate dateWithTimeIntervalSince1970:time]; NSDateComponents *dateComponents = [NSCalendar.currentCalendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:date]; struct tm tm = { 0 }; tm.tm_year = (int)dateComponents.year - 1900; tm.tm_mon = (int)dateComponents.month - 1; tm.tm_mday = (int)dateComponents.day; tm.tm_hour = (int)dateComponents.hour; tm.tm_min = (int)dateComponents.minute; tm.tm_sec = (int)dateComponents.second; time_t t = mktime(&tm); return *localtime(&t); } @interface TPSSAppContext () @property (nonatomic, assign, readwrite) BOOL didRequestLogout; @property (nonatomic, assign, readwrite) TPAPPTargetType curTarget; @end @implementation TPSSAppContext + (instancetype)sharedContext { static TPSSAppContext *appContext = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ appContext = [[TPSSAppContext alloc] init]; }); return appContext; } - (instancetype)init { self = [super init]; if (self) { pLocalTimeFunction = LocalTime; pDecryptBasicsToken = getBasicTokenMethod.getBasicToken; _pAppContext = (IPCAPPCONTEXT *)[self createLowLevelAppContext]; // 加载target type,加载对应target的target config #ifdef APP_VIGI self.curTarget = TPAPPTargetTypeSurveillanceHome; NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"targetConfig" ofType:@"plist"]; self.targetConfig = [[NSDictionary alloc] initWithContentsOfFile:bundlePath]; #else self.curTarget = TPAPPTargetTypeOmadaSurveillance; NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"OmadaSurveillance-targetConfig" ofType:@"plist"]; self.targetConfig = [[NSDictionary alloc] initWithContentsOfFile:bundlePath]; #endif _pAppContext->SetCurTarget((APPTargetType)self.curTarget); _pAppContext->SetTimeDifference((int)NSTimeZone.localTimeZone.secondsFromGMT * 1000); _pAppContext->RegisterEventFlingerCallback(EventCallback, ExitCallback, (__bridge void *)self); //因要设置图片、视频存储路径,该path已通过fishhook修改为library path NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *configPath = [paths objectAtIndex:0]; configPath = [configPath stringByAppendingPathComponent:@"AppConfig"]; if (![[NSFileManager defaultManager] fileExistsAtPath:configPath]) { [[NSFileManager defaultManager] createDirectoryAtPath:configPath withIntermediateDirectories:NO attributes:nil error:nil]; } _pAppContext->SetAppDataPath([configPath UTF8String]); NSString *filePath = [paths objectAtIndex:0]; filePath = [filePath stringByAppendingPathComponent:@"AppFiles"]; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { [[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:NO attributes:nil error:nil]; } _pAppContext->SetExternalDataPath([filePath UTF8String]); #if !TARGET_OS_IOS NSString *picturePath = [TPSSAppContext innerDownloadPath:TPGuardDownloadPathTypePicture context:_pAppContext]; NSString *videoPath = [TPSSAppContext innerDownloadPath:TPGuardDownloadPathTypeVideo context:_pAppContext]; _pAppContext->SetAlbumPath(picturePath.length > 0 ? picturePath.UTF8String : filePath.UTF8String, TP_LOCALALBUM_PATH_TYPE_PICTURE); _pAppContext->SetAlbumPath(videoPath.length > 0 ? videoPath.UTF8String : filePath.UTF8String, TP_LOCALALBUM_PATH_TYPE_VIDEO); #endif NSString *cachePath = [paths objectAtIndex:0]; cachePath = [cachePath stringByAppendingPathComponent:@"AppCache"]; if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) { [[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:nil]; } _pAppContext->SetCachePath([cachePath UTF8String]); #ifdef DEBUG TPWLogInit(); TPWLogEnableModule(1, TPWLOG_MODULE_COMM); TPWLogEnableModule(1, TPWLOG_MODULE_PLAYER); TPWLogEnableModule(1, TPWLOG_MODULE_IPCAPP); TPWLogEnableModule(0, TPWLOG_MODULE_NET_CLIENT); TPWLogEnableModule(1, TPWLOG_MODULE_STATISTICS); TPWLogSetLevel(TPWLOG_LEVEL_VERBOSE); TPWLogEnableTimestamp(1); TPWLogSetTimestampFormat(TPWLOG_TIMESTAMP_FORMAT_DATETIME_MIL); #endif self.didRequestLogout = NO; [self setupPresetDSTMap]; [TPSSAppContext setPresetTimeZone:_pAppContext]; _currentSiteDevicelist = [NSArray array]; _localDeviceMac = [NSMutableSet set]; } return self; } - (void)dealloc { // wait unit stop finish, and then delete _pAppContext->AppReqStop(&_uiRequestID, 1); delete _pAppContext; } #pragma mark - low level context - (void *)createLowLevelAppContext { return new IPCAPPCONTEXT(); } - (void *)lowLevelAppContext { return _pAppContext; } - (BOOL)allowCelluar { if (_pAppContext) { return _pAppContext->GetWindowControllerAllowCelluar(); } return NO; } #pragma mark - getter - (TPSSAppContextStatus)status { switch (_pAppContext->GetAppContextStatus()) { case IPCAPP_STARTED: return TPSSAppContextStatusStarted; case IPCAPP_STOPPED: return TPSSAppContextStatusStopped; } } - (TPSSAppContextConfig)config { #ifdef BETA_EXPORT_SALE_CLOUD return TPSSAppContextConfigTestBeta; #endif return TPSSAppContextConfigNormal; } - (NSArray <TPSSDeviceForDeviceList *> *)searchSiteList { if (_searchSiteList == nil) { _searchSiteList = [NSArray new]; } return _searchSiteList; } #pragma mark - START/STOP - (TPSSCode)start { unsigned int uiRequestID; int iRet = _pAppContext->AppReqStart(&uiRequestID, 0); return REQUEST_RESULT(uiRequestID, iRet); } - (TPSSCode)syncStart { unsigned int uiRequestID; int iRet = _pAppContext->AppReqStart(&uiRequestID, 1); return REQUEST_RESULT(uiRequestID, iRet); } - (TPSSCode)stop { unsigned int uiRequestID; int iRet = _pAppContext->AppReqStop(&uiRequestID, 0); return REQUEST_RESULT(uiRequestID, iRet); } - (TPSSCode)syncStop { unsigned int uiRequestID; int iRet = _pAppContext->AppReqStop(&uiRequestID, 1); return REQUEST_RESULT(uiRequestID, iRet); } - (void)setupPresetDSTMap { static NSString *dstStartTimeKey = @"start_time"; static NSString *dstEndTimeKey = @"end_time"; static NSString *dstOffsetKey = @"dst_saving"; NSString *path = [[NSBundle mainBundle] pathForResource:@"daylight_saving" ofType:@"json"]; NSData *jsonData = [NSData dataWithContentsOfFile:path]; NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil]; map<string, map<string, TPWDSTInfo>> *pDSTMap = _pAppContext->GetDSTMap(); for (NSString *dstName in dict.allKeys) { NSDictionary *dstDict = dict[dstName]; map<string, TPWDSTInfo> DSTInfoMap; for (NSString *year in dstDict.allKeys) { NSDictionary *dstInfoDict = dstDict[year]; TPWDSTInfo DSTInfo = { 0 }; DSTInfo.llStartTime = [dstInfoDict[dstStartTimeKey] longLongValue]; DSTInfo.llEndTime = [dstInfoDict[dstEndTimeKey] longLongValue]; DSTInfo.iDSTOffset = [dstInfoDict[dstOffsetKey] intValue]; DSTInfoMap[year.UTF8String] = DSTInfo; } (*pDSTMap)[dstName.UTF8String] = DSTInfoMap; } } #pragma mark - Notification handler static void EventCallback(int iQueueID, TPMESSAGE * pMessage, void * pArgs) { @autoreleasepool { TPSSAppContext *ac = (__bridge TPSSAppContext *)pArgs; [ac _handleEventWithQueueId:iQueueID andMessage:pMessage]; } } - (void)_handleEventWithQueueId:(int)iQueueId andMessage:(TPMESSAGE *)pMessage { NSNotification *notification; TPSSEventType eventType; if (TPSequenceNumberGetPrefix(pMessage->iID) == IPC_BROADCAST_SEQ_PREFIX) { eventType = TPSSEventTypeBroadcast; } else { eventType = TPSSEventTypeResponse; } notification = [NSNotification notificationWithMessage:pMessage eventType:eventType]; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotification:notification]; }); } static void ExitCallback(void * pArgs) { @autoreleasepool { TPSSAppContext *ac = (__bridge TPSSAppContext *)pArgs; [ac _handleExit]; } } - (void)_handleExit { } #pragma mark - network - (void)setNetworkType:(NSInteger)networkType provider:(NSString *)provider { _pAppContext->SetNetworkType((int)networkType, [provider UTF8String]); } - (void)connectivityChanged { _pAppContext->AppConnectivityChanged(); } - (void)pipeManagerOptimize { _pAppContext->GetNetworkPipeManager()->Optimize(); _pAppContext->GetNetworkPipeManager()->ReconnectAllPreconn(); } - (void)pipeManagerChangeMaxPunchingNum:(NSInteger)playWindowNum deviceIDArray:(NSArray<NSNumber *> *)deviceIDArray { //后续可根据需求进行定制 if (!deviceIDArray || playWindowNum == 0) { return; } std::set<long long> *llDeviceIDSet = new std::set<long long>(); for (int i = 0; i < deviceIDArray.count; i++) { llDeviceIDSet->insert([deviceIDArray[i] longLongValue]); } _pAppContext->GetNetworkPipeManager()->ChangeMaxPunchingNum((int)playWindowNum, llDeviceIDSet); } - (void)resetDeviceLocalValid { _pAppContext->AppResetDeviceLocalValid(); } #pragma mark - error message - (NSString *)legacyErrorMessageForIndex:(SInt32)errorIndex { char pcMessage[IPC_ERROR_MSG_STASH_ENTRY_BUFF_SIZE] = { 0 }; _pAppContext->GetErrorMessage(pcMessage, NULL, NULL, (int)errorIndex); NSString *errorMessage = [NSString stringWithUTF8String:pcMessage]; return [TPSSAppContext errorMsgForLocalizedKey:errorMessage]; } - (TPSSError *)legacyErrorForIndex:(SInt32)errorIndex { int iRval = 0; int iErrorCode = 0; int iCode = 0; char pcMessage[IPC_ERROR_MSG_STASH_ENTRY_BUFF_SIZE] = { 0 }; _pAppContext->GetErrorMessage(pcMessage, &iErrorCode, &iRval, (int)errorIndex); if (iErrorCode != IPC_EC_RVAL) { iCode = iErrorCode; } else { iCode = iRval; } return [TPSSError errorWithCode:iCode andMessage:[NSString stringWithUTF8String:pcMessage]]; } + (NSString *)errorMsgForLocalizedKey:(NSString *)localizedKey { NSString *localizedString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"IPCAppStringResourceDefines"]; if (![localizedString isEqualToString:localizedKey]) { return localizedString; } NSString *localizedErrorString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"commonErrormsg"]; if (![localizedErrorString isEqualToString:localizedKey]) { return localizedErrorString; } else if ([localizedErrorString isEqualToString:localizedKey]) { NSString *diffLocalizedString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"Localizable_diff"]; if (![diffLocalizedString isEqualToString:localizedKey]) { return diffLocalizedString; } } return localizedKey; } #pragma mark - task - (TPSSCode)cancelTask:(TPSSCode)requestID { int iRet = _pAppContext->AppCancelTask(requestID); return iRet; } - (TPSSTaskInfo *)taskInfoByID:(NSUInteger)requestID { TASKINFO *pTaskinfo = new TASKINFO; _pAppContext->GetTaskInfo((unsigned int)requestID, pTaskinfo); TPSSTaskInfo *taskinfo = [[TPSSTaskInfo alloc] initWithTaskinfo:pTaskinfo]; delete pTaskinfo; return taskinfo; } #pragma mark - tool method - (void)updateDidRequestLogout:(BOOL)didRequestLogout { self.didRequestLogout = didRequestLogout; } #pragma mark - device type //桥接层的DeviceType值转化成C层的DeviceType值 - (int)determainDeviceType:(TPSSDeviceType)deviceType { int deviceTypeC; switch (deviceType) { case TPSSDeviceTypeIPC: deviceTypeC = TPW_DEVICE_TYPE_IPC; break; case TPSSDeviceTypeNVR: deviceTypeC = TPW_DEVICE_TYPE_NVR; break; case TPSSDeviceTypeSolar: deviceTypeC = TPW_DEVICE_TYPE_SOLAR; break; default: deviceTypeC = TPW_DEVICE_TYPE_IPC; break; } return deviceTypeC; } //桥接层的DeviceSubType值转化成C层的DeviceSubType值 - (int)determainDeviceSubType:(TPSSDeviceSubType)subType { int deviceSubType; switch (subType) { case TPSSDeviceSubTypeNVR: deviceSubType = TPW_DEVICE_TYPE_NVR; break; case TPSSDeviceSubTypeCameraDisplay: deviceSubType = TPW_DEVICE_TYPE_CAMERA_DISPLAY; break; case TPSSDeviceSubTypeDoorBellCamera: deviceSubType = TPW_DEVICE_TYPE_DOORBELL_CAMERA; break; case TPSSDeviceSubTypeSolar: deviceSubType = TPW_DEVICE_TYPE_SOLAR; break; default: deviceSubType = TPW_DEVICE_TYPE_IPC; break; } return deviceSubType; } + (NSString *)downloadPathKey:(TPGuardDownloadPathType)type { return [NSString stringWithFormat:@"download_path_%@", @(type)]; } + (void)setDownloadPath:(NSString *)path type:(TPGuardDownloadPathType)type { if (path.length == 0 || type == TPGuardDownloadPathTypeCount) { return; } NSURL *fileURL = [NSURL fileURLWithPath:path]; if (fileURL) { NSData *bookmarkData = [self createBookmarkForURL:fileURL]; [[NSUserDefaults standardUserDefaults] setObject:bookmarkData forKey:[self downloadPathKey:type]]; } else { return; } auto &context = [TPSSAppContext sharedContext]->_pAppContext; if (type == TPGuardDownloadPathTypePicture) { context->SetAlbumPath(path.UTF8String, TP_LOCALALBUM_PATH_TYPE_PICTURE); } else if (type == TPGuardDownloadPathTypeVideo) { context->SetAlbumPath(path.UTF8String, TP_LOCALALBUM_PATH_TYPE_VIDEO); } } + (NSString *)innerDownloadPath:(TPGuardDownloadPathType)type context:(IPCAPPCONTEXT *)pContext { NSString *path = [[NSUserDefaults standardUserDefaults] valueForKey:[self downloadPathKey:type]]; NSData *boomarkData = [[NSUserDefaults standardUserDefaults] dataForKey:[self downloadPathKey:type]]; if (boomarkData) { NSURL *fileURL = [self resolveBookmarkData:boomarkData]; if (fileURL.path.length > 0) { return fileURL.path; } } if (path.length == 0 && pContext != NULL) { path = [NSString stringWithUTF8String: pContext->GetExternalDataPath()]; } return path.length > 0 ? path : @""; } + (NSString *)downloadPath:(TPGuardDownloadPathType)type { return [self innerDownloadPath:type context:[TPSSAppContext sharedContext]->_pAppContext]; } + (void)setPresetTimeZone:(IPCAPPCONTEXT *)pContext { NSString *filePath = [[NSBundle mainBundle] pathForResource:@"timezone" ofType:@"json"]; NSData *data = [NSData dataWithContentsOfFile:filePath]; if (!data || pContext == NULL) { return; } NSError *error; NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; NSArray *timeZones = jsonObject[@"timezones"]; if (![timeZones isKindOfClass:[NSArray class]]) { return; } map<string, TPWTimeZoneInfo> *map = pContext->GetTimeZoneMap(); for (NSDictionary *item in timeZones) { if (![item isKindOfClass:[NSDictionary class]]) { continue; } NSString *name = item[@"name"]; NSString *zone = item[@"timezone"]; int offset = [item[@"offset"] intValue]; if (name.length == 0 || zone.length == 0) { continue; } TPWTimeZoneInfo info = TPWTimeZoneInfo(); info.iTimeOffset = offset / 1000; strlcpy(info.pcTimezone, name.cString, MIN(name.cStringLength, TPW_URL_MAX_LENGTH)); strlcpy(info.pcZoneId, zone.cString, MIN(zone.cStringLength, TPW_URL_MAX_LENGTH)); map->insert(make_pair(zone.UTF8String, info)); } } + (NSURL *)resolveBookmarkData:(NSData *)data { NSError *error; BOOL isStale = NO; NSURL *url = [NSURL URLByResolvingBookmarkData:data options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; if (error) { NSLog(@"解析书签失败: %@", error); return nil; } if (isStale) { NSLog(@"书签已过期,重新生成"); NSData *newBookmark = [self createBookmarkForURL:url]; if (newBookmark) { [[NSUserDefaults standardUserDefaults] setObject:newBookmark forKey:@"savedBookmark"]; } } if ([url startAccessingSecurityScopedResource]) { return url; } return nil; } + (nullable NSData *)createBookmarkForURL:(NSURL *)url { NSError *error; NSData *bookmarkData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (error) { NSLog(@"创建书签失败: %@", error); } return bookmarkData; } @end TPSSAppContext类在这里了
09-20
这是代码import UIKit protocol CustomMenuItem { } protocol CustomMenuView: UIView { associatedtype MenuItemView: CustomMenuItemView var items: [MenuItemView.MenuItem] { get } func itemClickCallback(index: Int) } protocol CustomMenuItemView: UIView { associatedtype MenuItem: CustomMenuItem init(item: MenuItem) var action: (() -> Void)? { get set } } extension CustomMenuView { func setup() { backgroundColor = .tpbCard let count = items.count let container = UIView() addSubview(container) container.frame = bounds.inset(by: UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)) container.autoresizingMask = [.flexibleWidth, .flexibleHeight] var previousView: UIView? = nil var height: CGFloat = 0 for i in 0..<count { let menuView = MenuItemView(item: items[i]) menuView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(menuView) let cons1 = NSLayoutConstraint(item: menuView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0) let cons2 = NSLayoutConstraint(item: menuView, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let cons3: NSLayoutConstraint if let prev = previousView { cons3 = NSLayoutConstraint(item: menuView, attribute: .top, relatedBy: .equal, toItem: prev, attribute: .bottom, multiplier: 1, constant: 0) } else { cons3 = NSLayoutConstraint(item: menuView, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) } menuView.action = { [weak self] in self?.itemClickCallback(index: i) } NSLayoutConstraint.activate([cons1, cons2, cons3]) menuView.layoutIfNeeded() height += menuView.frame.size.height previousView = menuView } frame.size.height = 8 + height + 8 } } class AnyMenuItemView<MenuItem: CustomMenuItem>: TapGestureSwallowingControl, CustomMenuItemView { var action: (() -> Void)? let item: MenuItem required init(item: MenuItem) { self.item = item super.init(frame: .zero) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { backgroundColor = .tpbBackgroundInCard return true } override func endTracking(_ touch: UITouch?, with event: UIEvent?) { stopTracking(with: event) } override func cancelTracking(with event: UIEvent?) { stopTracking(with: event) } private func stopTracking(with event: UIEvent?) { backgroundColor = .tpbCard if let loc = event?.touches(for: self)?.first?.location(in: self), bounds.contains(loc) { action?() } } } class DeviceGroupMenuView: UIView, CustomMenuView { typealias MenuItemView = DeviceGroupMenuItemView enum Item: Int { //Fix: 隐藏Home/Away Mode // case alarmMode = 0 case viewMode = 0 case reorder case search } //Fix: 隐藏Home/Away Mode // let items: [DeviceGroupMenuItem] = [.alarmMode, .viewMode, .reorder, .search] var items: [DeviceGroupMenuItem] = [.viewMode, .reorder, .search] { didSet { setup() } } var action: ((Item) -> Void)? override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { if items[index] == .viewMode { action?(.viewMode) } else if items[index] == .reorder { action?(.reorder) } else if items[index] == .search { action?(.search) } } } class DeviceGroupMenuItem: NSObject, CustomMenuItem { @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool init(image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true) { self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled super.init() } //Fix: 隐藏Home/Away Mode // static let alarmMode: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_home_active").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListModeHome), subtitle: LocalizedString(key: deviceListSwitchModeToOut)) static let viewMode: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("deviceList_display_bigPic").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListSwitchView), subtitle: "") static let reorder: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_nvr_channel_reorder"), title: LocalizedString(key: deviceListReorder)) static let search: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_search"), title: LocalizedString(key: deviceListSearch)) } class DeviceGroupMenuItemView: AnyMenuItemView<DeviceGroupMenuItem> { var observers = [NSKeyValueObservation]() required init(item: DeviceGroupMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in self?.informationLabel.text = object.subtitle } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 1 // titleLabel.lineBreakMode = .byWordWrapping return titleLabel }() private lazy var informationLabel: UILabel = { let label = UILabel(frame: .zero) label.textColor = UIColor.tpbTextThirdContent label.font = UIFont.projectFont(ofSize: 13) label.textAlignment = .right label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping return label }() private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -16) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 4) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 5) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4]) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) container.isUserInteractionEnabled = false } } // MARK: GeneralMenuView class GeneralMenuView: UIView, CustomMenuView { typealias MenuItemView = GeneralMenuItemView var items: [GeneralMenuItem] = [] { didSet { setup() } } /// 点击后的动作 /// index: 第几个menuItem var action: ((_ itemId: String) -> Void)? override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { action?(items[safe: index]?.itemId ?? "") } } class GeneralMenuItem: NSObject, CustomMenuItem { var itemId: String @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool init(itemId: String, image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true) { self.itemId = itemId self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled super.init() } } class GeneralMenuItemView: AnyMenuItemView<GeneralMenuItem> { var observers = [NSKeyValueObservation]() var informationLabelWidthConstraint: NSLayoutConstraint? required init(item: GeneralMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in if object.subtitle == nil || object.subtitle == "" { self?.informationLabelWidthConstraint?.isActive = true } else { self?.informationLabelWidthConstraint?.isActive = false self?.informationLabel.text = object.subtitle } } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping return titleLabel }() private lazy var informationLabel: UILabel = { let label = UILabel(frame: .zero) label.textColor = UIColor.tpbTextThirdContent label.font = UIFont.projectFont(ofSize: 13) label.textAlignment = .right label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping return label }() private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -16) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 12) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 5) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) informationLabelWidthConstraint = NSLayoutConstraint(item: informationLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4]) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) container.isUserInteractionEnabled = false } } class DeviceListMenuView: UIView, CustomMenuView { enum Item: Int { case alarmMode = 0 //Fix: 隐藏云存储 // case cloudStorage //Fix: 隐藏设备分享 // case share case upgrade // case reonboard case unbind case setting case collect } typealias MenuItemView = DeviceListMenuItemView let items: [DeviceListMenuItem] = DeviceListMenuItem.allItems static var deviceChannel: (device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo?)? var action: ((Item, TPSSDeviceForDeviceList, TPSSChannelInfo?) -> Void)? = nil override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { action?(Item(rawValue: index)!, DeviceListMenuView.deviceChannel!.device, DeviceListMenuView.deviceChannel!.channel) } } class DeviceListMenuItem: NSObject, CustomMenuItem { @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool @objc dynamic var hidden: Bool @objc dynamic var subtitleTextColor: UIColor @objc dynamic var subtitleBackgroundColor: UIColor @objc dynamic var alpha: Float init(image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true, hidden: Bool = false, subtitleTextColor: UIColor = .tpbTextPrimary, subtitleBackgroundColor: UIColor = .tpbCard, alpha: Float = 1) { self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled self.hidden = hidden self.subtitleTextColor = subtitleTextColor self.subtitleBackgroundColor = subtitleBackgroundColor self.alpha = alpha super.init() } static let alarmMode: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListEventAlarmOn)) //Fix: 隐藏云存储 // static let cloudStorage: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("cloud_service"), title: LocalizedString(key: deviceListCloudStorage), subtitle: LocalizedString(key: deviceListSwitchViewToList)) //Fix: 隐藏设备分享 // static let share: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_share_nor"), title: LocalizedString(key: deviceListShare)) static let upgrade: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_update_reddot_nor"), title: LocalizedString(key: deviceListUpgrade)) // static let reonboard: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("common_reonboarding_wifi"), title: LocalizedString(key: deviceListReonboarding)) static let setting: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("common_setting_nor"), title: LocalizedString(key: deviceListMoreSetting)) static let unbind: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_unbind"), title: LocalizedString(key: deviceUnbindButtonTitle)) static let collect: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("device_not_collect_list"), title: LocalizedString(key: deviceCollectFavorite)) //Fix: 隐藏云存储 //Fix: 隐藏设备分享 // static let allItems: [DeviceListMenuItem] = [.alarmMode, .cloudStorage, .share, .upgrade, .reonboard, .setting] static let allItems: [DeviceListMenuItem] = [.alarmMode, .upgrade, unbind, .setting, .collect] } extension Array where Element == DeviceListMenuItem { var preferredheight: CGFloat { return CGFloat(filter {!$0.hidden}.count) * 44 + 16 } } class DeviceListMenuItemView: AnyMenuItemView<DeviceListMenuItem> { var observers = [NSKeyValueObservation]() required init(item: DeviceListMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: item.hidden ? 0 : 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in if let subtitle = object.subtitle { self?.informationButton.isHidden = false self?.informationLabel.text = subtitle } else { self?.informationButton.isHidden = true self?.hasInformationLayout(false) } }, item.observe(\.subtitleTextColor, options: .initial) { [weak self] (object, _) in self?.informationLabel.textColor = object.subtitleTextColor }, item.observe(\.subtitleBackgroundColor, options: .initial) { [weak self] (object, _) in self?.informationButton.backgroundColor = object.subtitleBackgroundColor }, item.observe(\.hidden, options: .initial) { [weak self] (object, _) in self?.isHidden = object.hidden self?.invalidateIntrinsicContentSize() }, item.observe(\.alpha, options: .initial) { [weak self] (object, _) in self?.alpha = CGFloat(object.alpha) } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) return titleLabel }() private lazy var informationButton: UIButton = { let btn = UIButton(type: .system) btn.tintColor = .tpbTextPrimary btn.backgroundColor = .tpbCard btn.titleLabel?.font = UIFont.projectFont(ofSize: 13) btn.layer.cornerRadius = 12 // btn.contentEdgeInsets = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) NSLayoutConstraint(item: btn, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 24).isActive = true return btn }() private lazy var informationLabel: UILabel = { let informationLabel = UILabel() informationLabel.textColor = UIColor.tpbTextPrimary informationLabel.font = UIFont.projectFont(ofSize: 13) informationLabel.textAlignment = .left informationLabel.numberOfLines = 1 // informationLabel.setContentCompressionResistancePriority(.required, for: .horizontal) return informationLabel }() private func hasInformationLayout(_ hasInfo: Bool) { let titleLabelCons4 = NSLayoutConstraint(item: titleLabel, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0) titleLabelCons4.isActive = !hasInfo } private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -22) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 4) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationButton.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationButton) informationLabel.translatesAutoresizingMaskIntoConstraints = false informationButton.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 12) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: informationButton, attribute: .trailing, multiplier: 1, constant: -12) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: informationButton, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: informationButton, attribute: .bottom, multiplier: 1, constant: 0) let informationLabelCons5 = NSLayoutConstraint(item: informationLabel, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: 40) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4, informationLabelCons5]) let informationButtonCons1 = NSLayoutConstraint(item: informationButton, attribute: .leading, relatedBy: .equal, toItem: informationLabel, attribute: .leading, multiplier: 1, constant: -12) let informationButtonCons2 = NSLayoutConstraint(item: informationButton, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationButtonCons3 = NSLayoutConstraint(item: informationButton, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationButtonCons1, informationButtonCons2, informationButtonCons3]) container.isUserInteractionEnabled = false } } extension DeviceListMenuView { private class func setDeviceListMenuItemAlpha(_ alpha: Float, for itemList: [DeviceListMenuItem]) { if alpha >= 0 && alpha <= 1 { itemList.forEach({$0.alpha = alpha}) } } class func configure(device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo?, displayMode: TPSSDeviceGroupDisplayMode = .card) { deviceChannel = (device, channel) //OnlineDeviceListViewController调用configure函数是配置两个item title以满足多语言切换刷新文案 DeviceListMenuItem.upgrade.title = LocalizedString(key: deviceListUpgrade) DeviceListMenuItem.setting.title = LocalizedString(key: deviceListMoreSetting) DeviceListMenuItem.collect.title = LocalizedString(key: deviceCollectFavorite) DeviceListMenuItem.unbind.title = LocalizedString(key: deviceUnbindButtonTitle) //太阳能\NVR\local;card模式此处不显示收藏,老版本vms也不支持 let isVms = TPAppContextFactory.shared().isVmsLogin DeviceListMenuItem.collect.hidden = (device.deviceType == .solar || device.listType == .local || displayMode == .card || (TPSSAppContext.shared.loginType == .personal ? false : TPSSAppContext.shared.isVmsDeviceListFavoriteOldVersion) || ( device.deviceType == .NVR && channel == nil) ) ? true : false //根据设备收否被收藏来设置收藏按钮状态 var isCollected = false if device.deviceType == .IPC { isCollected = isVms ? device.isVMSFavorited : device.isCollected } else { if channel != nil { isCollected = isVms ? channel!.isVMSFavorited : device.isCollected && device.collectChannels.contains(channel!.channelId as NSNumber) } else { isCollected = isVms ? device.isVMSFavorited : device.isCollected } } DeviceListMenuItem.collect.image = isCollected ? TPImageLiteral("device_have_collect_list") : TPImageLiteral("device_not_collect_list") //太阳能设备适配 if device.deviceType == .IPC || device.deviceType == .solar { // 消息报警 var showMessage = device.listType == .remote && device.supportMessage && device.displayOnline && !device.isSharedDevice if let cn = channel { showMessage = cn.supportsMessagePush && cn.online && device.displayOnline && !device.isSharedDevice } //太阳能设备适配 if device.deviceType == .solar { //太阳能设备不支持通知开关 showMessage = false // showMessage = device.listType == .remote && device.displayOnline } if !showMessage { DeviceListMenuItem.alarmMode.hidden = true } else { DeviceListMenuItem.alarmMode.hidden = false let messageEnable = channel?.messageEnable ?? device.messageEnable DeviceListMenuItem.alarmMode.image = messageEnable ? TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal) : TPImageLiteral("devicelist_alarm_off") DeviceListMenuItem.alarmMode.title = messageEnable ? LocalizedString(key: deviceListEventAlarmOn) : LocalizedString(key: deviceListEventAlarmOff) } if device.deviceSubType == .doorBellCamera { DeviceListMenuItem.alarmMode.hidden = true } // 如果是分享的设备且没有管理权限, cell设为透明, 不许设置 if device.isSharedFromOthers && device.shareDevicePermissionInfo.deviceSettingWrite == false { setDeviceListMenuItemAlpha(0.5, for: [.alarmMode, .upgrade, .unbind]) } else { setDeviceListMenuItemAlpha(1, for: [.alarmMode, .upgrade, .unbind]) } // 云存储 //Fix: 隐藏云存储 // if !device.supportCloudStorage || device.isSharedDevice { // DeviceListMenuItem.cloudStorage.hidden = true // } else { // let cloudStorageService = appContext.currentServiceInfo(forDevice: device.cloudDeviceId, // channel: max(0, channelID), // serviceType: .cloudStorage) // // DeviceListMenuItem.cloudStorage.hidden = false // // if cloudStorageService.isVaild { // DeviceListMenuItem.cloudStorage.subtitle = cloudStorageService.cloudStorageStateText // DeviceListMenuItem.cloudStorage.subtitleTextColor = cloudStorageService.stateColor // DeviceListMenuItem.cloudStorage.subtitleBackgroundColor = cloudStorageService.stateBackgroundColor // } else { // DeviceListMenuItem.cloudStorage.subtitle = nil // } // } // 分享 //Fix: 隐藏设备分享 // let appContext = TPSSAppContext.shared // // let channelID = channel?.channelId.intValue ?? -1 // if device.isSharedDevice || !device.supportsShare { // DeviceListMenuItem.share.hidden = true // } else { // DeviceListMenuItem.share.hidden = false // let isEnterpriseShare = appContext.isValidEnterpriseShareDevice(device.cloudDeviceId, channelD: max(0, channelID)) // let shareService: TPSSCloudStorageService // if isEnterpriseShare { // shareService = appContext.enterpriseShareService // } else { // shareService = appContext.currentServiceInfo(forDevice: device.cloudDeviceId, // channel: max(0, channelID), // serviceType: .paidShare) // } // let stateText = shareService.shareStateText(isEnterpriseShare: isEnterpriseShare) // if shareService.isVaild && !stateText.isEmpty && (shareService.serviceState != .expired || UserDefaults.standard.needShowRenewalHint(for: device.cloudDeviceId, channelID: max(0, channelID), isEnterpriseShare: isEnterpriseShare)) { // DeviceListMenuItem.share.subtitle = stateText // DeviceListMenuItem.share.subtitleTextColor = shareService.stateColor // DeviceListMenuItem.share.subtitleBackgroundColor = shareService.stateBackgroundColor // } else { // DeviceListMenuItem.share.subtitle = nil // } // } // // // 重新配置WiFi // if (device.displayOnline || !device.supportsReonboarding) || channel != nil { // DeviceListMenuItem.reonboard.hidden = true // } else { // DeviceListMenuItem.reonboard.hidden = false // } // 固件升级 if device.listType != .remote { DeviceListMenuItem.upgrade.hidden = true } else { if let cn = channel { if let version = cn.shortReleaseFirmwareVersion, cn.hasNewFirmware && !device.isSharedDevice { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: channel) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } else { if let version = device.shortReleaseFirmwareVersion, device.online && device.needUpgrade && !device.isSharedDevice { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: nil) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } } //未初始化设备只有非本地设备首页可显示解绑按钮 if device.listType == .local || device.factoryStatus != .yes || !device.online { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } } else if device.deviceType == .NVR { // DeviceListMenuItem.alarmMode.hidden = true // DeviceListMenuItem.upgrade.hidden = true guard let channelInfo = channel else { DeviceListMenuItem.alarmMode.hidden = true DeviceListMenuItem.upgrade.hidden = true if device.listType == .local || device.factoryStatus != .yes || !device.online { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } return } // 如果是分享的通道且没有管理权限, cell设为透明, 不许设置 if channelInfo.isSharedFromOthers && channelInfo.shareDevicePermissionInfo.deviceSettingWrite == false { setDeviceListMenuItemAlpha(0.5, for: [.alarmMode, .upgrade, .unbind]) } else { setDeviceListMenuItemAlpha(1, for: [.alarmMode, .upgrade, .unbind]) } // 消息报警 let showMessage = (device.listType == .remote) && channelInfo.supportsMessagePush && channelInfo.online && device.displayOnline && !device.isSharedDevice && channelInfo.active if !showMessage { DeviceListMenuItem.alarmMode.hidden = true } else { DeviceListMenuItem.alarmMode.hidden = false let messageEnable = channelInfo.messageEnable DeviceListMenuItem.alarmMode.image = messageEnable ? TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal) : TPImageLiteral("devicelist_alarm_off") DeviceListMenuItem.alarmMode.title = messageEnable ? LocalizedString(key: deviceListEventAlarmOn) : LocalizedString(key: deviceListEventAlarmOff) } if device.deviceSubType == .doorBellCamera { DeviceListMenuItem.alarmMode.hidden = true } // 固件升级 if device.listType != .remote { DeviceListMenuItem.upgrade.hidden = true } else { if let version = channelInfo.shortReleaseFirmwareVersion, channelInfo.hasNewFirmware && !device.isSharedDevice && channelInfo.active { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: channelInfo) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } //未初始化设备只有非本地设备首页可显示解绑按钮;!channelInfo.active仅针对初始化状态,channel都没设备,不显示解绑(非初始化状态device.factoryStatus != .yes直接隐藏unbind) if device.listType == .local || device.factoryStatus != .yes || !device.online || !channelInfo.active { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } } } }
12-06
基于径向基函数神经网络RBFNN的自适应滑模控制学习(Matlab代码实现)内容概要:本文介绍了基于径向基函数神经网络(RBFNN)的自适应滑模控制方法,并提供了相应的Matlab代码实现。该方法结合了RBF神经网络的非线性逼近能力和滑模控制的强鲁棒性,用于解决复杂系统的控制问题,尤其适用于存在不确定性和外部干扰的动态系统。文中详细阐述了控制算法的设计思路、RBFNN的结构与权重更新机制、滑模面的构建以及自适应律的推导过程,并通过Matlab仿真验证了所提方法的有效性和稳定性。此外,文档还列举了大量相关的科研方向和技术应用,涵盖智能优化算法、机器学习、电力系统、路径规划等多个领域,展示了该技术的广泛应用前景。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的研究生、科研人员及工程技术人员,特别是从事智能控制、非线性系统控制及相关领域的研究人员; 使用场景及目标:①学习和掌握RBF神经网络与滑模控制相结合的自适应控制策略设计方法;②应用于电机控制、机器人轨迹跟踪、电力电子系统等存在模型不确定性或外界扰动的实际控制系统中,提升控制精度与鲁棒性; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,深入理解算法实现细节,同时可参考文中提及的相关技术方向拓展研究思路,注重理论分析与仿真验证相结合。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值