美文网首页
iOS零碎知识点<初级版>

iOS零碎知识点<初级版>

作者: nenhall | 来源:发表于2017-06-28 16:56 被阅读51次

    iOS零碎知识点<初级版>
    iOS零碎知识点<中阶版>
    iOS零碎知识点<高阶版>
    iOS零碎知识点<工具篇>

    优雅的隐藏tabbar

    很多APP都使用TabBarController套NavigationController的方法来作为应用的框架,那么隐藏TabBar就成了一个必要的功能,目前最简单的方法还是使用hidesBottomBarWhenPushed来实现,最简单的方法就是在要隐藏tab bar的Controller里写入下面的方法(可以写个类扩展来更方便)来覆默认值:

    - (BOOL)hidesBottomBarWhenPushed {
        return (self.navigationController.topViewController == self);
    }
    

    把UITableview在editing模式下的drag按钮去掉,换成自己的样式,并保留原生拖动排序的行为

    先找到了UITableViewCell的结构,并将拖动按钮替换:

    //打印出来的自定义的cell在editing模式下的结构

    (lldb) po self
    >
    (lldb) po self.subviews
    5 elements
    - [0] : ; layer = >
    - [1] : <_UITableViewCellSeparatorView: 0x7d087c40; frame = (15 55; 305 1); layer = >
    - [2] : <_UITableViewCellSeparatorView: 0x7b163240; frame = (15 55.5; 305 0.5); layer = >
    - [3] : >
    - [4] : >
    (lldb) po self.subviews.last
    Optional
    - Some : >
    (lldb) po self.subviews.last?.subviews
    Optional>
    Some : 1 elements
    - [0] : >
    (lldb)
    

    可以看到此时contentView左右都向内缩进了一定的距离,最后有一个view叫UITableViewCellReorderControl,就是它了,然后看它的subviews,竟然包含了一个UIImageView,果断替换之,代码如下:

     override func layoutSubviews() {
            super.layoutSubviews()
            setupReorderControl()
        }
        
        func setupReorderControl() {
            if (self.reorderControl != nil) {
                return;
            }
            
            for view in self.subviews {
                if view.description.containsString("UITableViewCellReorderControl") {
                    self.reorderControl = view
                }
            }
            
            if ((self.reorderControl) != nil)
            {
                let imageOfReorder = self.reorderControl?.subviews[0] as? UIImageView
                imageOfReorder?.removeFromSuperview()
            }
        }
    

    此时就完成了将拖动按钮隐藏的功能,但是注意,在这里如果想通过设置reorderControl的frame去改变它的位置是不成功的,我想可能它的布局使用autolayout,并没有深入的再去研究。

    改變導航欄返回按鈕,iOS11上失效:

    [[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage
                                                     forState:UIControlStateNormal
                                                   barMetrics:UIBarMetricsDefault];
    
    //自定义文字部分
    [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(NSIntegerMin, NSIntegerMin)  forBarMetrics:UIBarMetricsDefault];
    

    判定滚动手势是往上还是往下拖拽

    //scrollView已经有拖拽手势,直接拿到scrollView的拖拽手势
        UIPanGestureRecognizer *pan = scrollView.panGestureRecognizer;
        CGFloat velocity = [pan velocityInView:scrollView].y;
        CGFloat offsetY = _lastPointY - scrollView.contentOffset.y;
        
        if (velocity < -5) {
            //向上拖动,隐藏导航栏
           
        } else if (velocity > 5) {
            //向下拖动,显示导航栏
           
        } else if(velocity == 0){
            //停止拖拽
        }
    

    ios UITableview header不随着滚动

    //去掉UItableview headerview黏性(sticky)
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        CGFloat sectionHeaderHeight = 40;
        if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
            scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
        } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
            scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
        }
    } 
    

    UITableView的子视图会自动往上偏移64

    大概结构是这样的:tableview上添加了个subview,然后设置了tableview的内偏移为subview的高,还设置了tableview的tableFooterView;运行后发现tableview的contentoffset自动往上多偏移了64;

    self.automaticallyAdjustsScrollViewInsets = NO;
        centerTableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight - kNavgationHeight) style:UITableViewStyleGrouped];
        centerTableView.showsVerticalScrollIndicator = NO;
        centerTableView.tableFooterView = self.bottomView;
        [centerTableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
        centerTableView.contentInset = UIEdgeInsetsMake(_centerViewHeight, 0, 0, 0);
        [self.view addSubview:centerTableView];
    
        _userHeadView = [BKUserCenterView loadViewFromNib];
        _userHeadView.frame = CGRectMake(0, -_centerViewHeight, kScreenWidth, _centerViewHeight);
        [centerTableView addSubview:_userHeadView];
    

    导致原因:先设置了tableFooterView,再addSubview: 导致tableview的计算就会不准确,把设置tableFooterView 放到 addSubview: 后面就好了,更改后如下:

    .......上面其它代码不变......
    [centerTableView addSubview:_userHeadView];
    centerTableView.tableFooterView = self.bottomView;
    

    获取相片的位置等信息:

    NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
            ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
            __block NSMutableDictionary *imageMetadata_GPS = nil;
    
            __weak typeof(self)weakSelf = self;
            [library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
    
                 //获取时间
                 NSDate* pictureDate = [asset valueForProperty:ALAssetPropertyDate];
                 NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
                 formatter.dateFormat = @"yyyy:MM:dd HH:mm:ss";
                 formatter.timeZone = [NSTimeZone localTimeZone];
                 NSString * pictureTime = [formatter stringFromDate:pictureDate];
                 weakSelf.time.text = pictureTime;
    
                 //获取GPS
                 imageMetadata_GPS = [[NSMutableDictionary alloc] initWithDictionary:asset.defaultRepresentation.metadata];
    
                 NSDictionary *GPSDict=[imageMetadata_GPS objectForKey:(NSString*)kCGImagePropertyGPSDictionary];
    
                 if (GPSDict!=nil) {
    
                     CLLocation *loc=[GPSDict locationFromGPSDictionary];
    
                     weakSelf.weidu.text = [NSString stringWithFormat:@"%f", loc.coordinate.latitude];
                     weakSelf.jingdu.text = [NSString stringWithFormat:@"%f", loc.coordinate.longitude];
    
                     CLGeocoder *clGeoCoder = [[CLGeocoder alloc] init];
                     CLLocation *newLocation = [[CLLocation alloc] initWithLatitude:loc.coordinate.latitude longitude:loc.coordinate.longitude];
    
                     //反向地理编码的请求 -> 根据经纬度 获取 位置
                     [clGeoCoder reverseGeocodeLocation:newLocation completionHandler: ^(NSArray *placemarks,NSError *error) {
                         for (CLPlacemark *placeMark in placemarks)
                         {
                             NSDictionary *addressDic=placeMark.addressDictionary;
                             NSArray *location_Arr = [addressDic objectForKey:@"FormattedAddressLines"];//系统格式化后的位置
                             weakSelf.location.text = [location_Arr firstObject];
                         }
                     }];
                 }else{
                          //@"此照片没有GPS信息";
                          //@"此照片没有GPS信息";
                          //@"此照片没有拍摄位置";
                     }
                 }
                failureBlock:^(NSError *error) {
            }];
        }
    

    获取网络图片的宽高

    +(CGSize)getImageSizeWithURL:(id)imageURL
    {
        NSURL * url = nil;
        if ([imageURL isKindOfClass:[NSURL class]]) {
            url = imageURL;
        }
        if ([imageURL isKindOfClass:[NSString class]]) {
            url = [NSURL URLWithString:imageURL];
        }
        if (!url) {
            return CGSizeZero;
        }
        CGImageSourceRef imageSourceRef =     CGImageSourceCreateWithURL((CFURLRef)url, NULL);
        CGFloat width = 0, height = 0;
        if (imageSourceRef) {
            CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);
            if (imageProperties != NULL) {
                CFNumberRef widthNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
                if (widthNumberRef != NULL) {
                    CFNumberGetValue(widthNumberRef, kCFNumberFloat64Type, &width);
                }
                CFNumberRef heightNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
                if (heightNumberRef != NULL) {
                    CFNumberGetValue(heightNumberRef, kCFNumberFloat64Type, &height);
                }
                CFRelease(imageProperties);
            }
            CFRelease(imageSourceRef);
        }
        return CGSizeMake(width, height);
    }
    

    tableView下拉导航栏上头像变大:

    UIView *titleView = [[UIView alloc] init];
      self.navigationItem.titleView = titleView;
    
      self.headerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head.jpg"]];
      self.headerImageView.layer.cornerRadius = 35;
      self.headerImageView.layer.masksToBounds = YES;
      self.headerImageView.frame = CGRectMake(0, 0, 70, 70);
      self.headerImageView.center = CGPointMake(titleView.center.x, 0);
      [titleView addSubview:self.headerImageView];
    
    #pragma mark - UIScrollViewDelegate
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
      CGFloat offsetY = scrollView.contentOffset.y + scrollView.contentInset.top;
      
      CGFloat scale = 1.0;
      // 放大
      if (offsetY < 0) {
        // 允许下拉放大的最大距离为300
        // 1.5是放大的最大倍数,当达到最大时,大小为:1.5 * 70 = 105
        // 这个值可以自由调整
        scale = MIN(1.5, 1 - offsetY / 300);
      } else if (offsetY > 0) { // 缩小
        // 允许向上超过导航条缩小的最大距离为300
        // 为了防止缩小过度,给一个最小值为0.45,其中0.45 = 31.5 / 70.0,表示
        // 头像最小是31.5像素
        scale = MAX(0.45, 1 - offsetY / 300);
      }
      
      self.headerImageView.transform = CGAffineTransformMakeScale(scale, scale);
    
      // 保证缩放后y坐标不变
      CGRect frame = self.headerImageView.frame;
      frame.origin.y = -self.headerImageView.layer.cornerRadius / 2;
      self.headerImageView.frame = frame;
    }
    

    画一个渐变色的圆

    - (void)drawRect:(CGRect)rect {
    
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        
        CGContextAddArc(ctx, self.centerX, self.centerY, self.height / 2, 0, M_PI * 2, 0);
        
        CGContextSetLineWidth(ctx, 8);
        
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        
        //    颜色数组
        NSArray *colors = @[
                            (id)[UIColor colorWithRed:86 / 255.f green:216 / 255.f blue:252 / 255.f alpha:1].CGColor,
                            (id)[UIColor colorWithRed:248 / 255.f green:114 / 255.f blue:155 / 255.f alpha:1].CGColor
                            ];
        
        CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
        
        CGColorSpaceRelease(colorSpace);
        CGContextReplacePathWithStrokedPath(ctx);
        CGContextClip(ctx);
        
        CGPoint beginPoint = CGPointMake(0, 0);
        CGPoint endPoint = CGPointMake(rect.size.width, rect.size.height);
        CGContextDrawLinearGradient(ctx, gradientRef, beginPoint, endPoint, 0);
        
        CGGradientRelease(gradientRef);
        
        CGContextStrokePath(ctx);
    
    }
    

    去除掉首尾的空白字符和换行字符

     [address stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 
    //替换\n
      [address stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    

    ios打包ipa的四种实用方法(.app转.ipa)

    基本数据类型的取值范围:

    unsigned   int   0~4294967295   
    int   -2147483648~2147483647 
    unsigned long 0~4294967295
    long   -2147483648~2147483647
    long long的最大值:9223372036854775807
    long long的最小值:-9223372036854775808
    unsigned long long的最大值:1844674407370955161
    __int64的最大值:9223372036854775807
    __int64的最小值:-9223372036854775808
    unsigned __int64的最大值:18446744073709551615
    

    修改 UIAlertController、UIAlertAction文字颜色

    UIAlertController * alertContro = [[UIAlertController alloc] init];
           NSMutableAttributedString *hogan = [[NSMutableAttributedString alloc] initWithString:@"heihei"];
            [hogan addAttribute:NSFontAttributeName value:kMediumFontIsB4IOS9(16) range:NSMakeRange(0, [[hogan string] length])];
            [hogan addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [[hogan string] length])];
            [alertContro setValue:hogan forKey:@"attributedTitle"];
    
    // UIAlertAction
            alertAction setValue:kRedColor forKey:@"_titleTextColor"];
    

    Mac OS 10.12.3如何添加永久静态路由(亲测无效,不知道是我那里执行错了!)

    https://discussionschinese.apple.com/thread/102393?start=0&tstart=0

    判定是否有导入了某个头件

    #if __has_include(<xxxxx/xxxxx.h>)
    #import <xxxxx/xxxxx.h>
    #else
    #import "zzzzz.h"
    #endif
    

    动态库与静态库

    区別:
    静态库:链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝。
    动态库:链接时不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用(如系统的UIKit.framework等),节省内存。

    1. 如果静态库中有category类,则在使用静态库的项目配置中Other Linker Flags需要添加参数-ObjC或者-all_load。
      如果创建的framework类中使用了.tbd,则需要在实际项目中导入.tbd动态库。

    2. Mach-O格式,因为动态库也可以是以framework形式存在,所以需要设置,否则默认打出来的是动态库。将target->BuildSetting->Mach-o Type 设为Static Library(默认为Dynamic Library)

    3. 架構:
      模拟器:
      iPhone4s-iPnone5:i386
      iPhone5s-iPhone7 Plus:x86_64
      真机:
      iPhone3gs-iPhone4s:armv7
      iPhone5-iPhone5c:armv7s
      iPhone5s-iPhone7 Plus:arm64
      支持armv7的静态库可以在armv7s上正常运行。

    手机号判定:

    • 方法一:建议使用这个
        if (self.length != 11)
        {
            return NO;
        }
        /**
         * 手机号码:
         * 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[0, 1, 6, 7, 8], 18[0-9]
         * 移动号段: 134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
         * 联通号段: 130,131,132,145,155,156,170,171,175,176,185,186
         * 电信号段: 133,149,153,170,173,177,180,181,189
         */
        NSString *MOBILE = @"^1(3[0-9]|4[57]|5[0-35-9]|7[0135678]|8[0-9])\\d{8}$";
        /**
         * 中国移动:China Mobile
         * 134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
         */
        NSString *CM = @"^1(3[4-9]|4[7]|5[0-27-9]|7[08]|8[2-478])\\d{8}$";
        /**
         * 中国联通:China Unicom
         * 130,131,132,145,155,156,170,171,175,176,185,186
         */
        NSString *CU = @"^1(3[0-2]|4[5]|5[56]|7[0156]|8[56])\\d{8}$";
        /**
         * 中国电信:China Telecom
         * 133,149,153,170,173,177,180,181,189
         */
        NSString *CT = @"^1(3[3]|4[9]|53|7[037]|8[019])\\d{8}$";
        
        NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
        NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
        NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
        NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
        
        if (([regextestmobile evaluateWithObject:self] == YES)
            || ([regextestcm evaluateWithObject:self] == YES)
            || ([regextestct evaluateWithObject:self] == YES)
            || ([regextestcu evaluateWithObject:self] == YES))
        {
            return YES;
        }
        else
        {
            return NO;
        }
    }
    
    • 方法二:
    - (BOOL)isMobile{
        NSString *regexStr = @"^1[3,8]\\d{9}|14[5,7,9]\\d{8}|15[^4]\\d{8}|17[^2,4,9]\\d{8}$";
        NSError *error;
        NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:&error];
        if (error) return NO;
        NSInteger count = [regular numberOfMatchesInString:self options:NSMatchingReportCompletion range:NSMakeRange(0, self.length)];
        if (count > 0) {
            return YES;
        } else {
            return NO;
        }
    }
    

    适配ios11 @available属性oc工程无法在xcode9以下工程编译问题解决方案:

    # ifdef __IPHONE_11_0
        if (@available(iOS 11.0, *)) {
            self.leftContentView.contenView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        } else {
            self.automaticallyAdjustsScrollViewInsets = NO;
        }
    #else
        self.automaticallyAdjustsScrollViewInsets = NO;
    #endif
    

    获取WKWebview的内容高度

    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        [webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
           CGFloat documentHeight = [result doubleValue];
            wkWebViewHeight = documentHeight;
            CGRect webFrame = webView.frame;
            webFrame.size.height = wkWebViewHeight;
            webView.frame = webFrame;
            [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:3 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
        }];
    }
    

    判定是否可以导入某个头文件:

    //判定是否可以导入某头文件
    #if __has_include(<xxxxxxx/xxxxxx.h>)
      //do sth
    #import <xxxxxxx/aaaaaa.h>
    //判定是否可以导入某framework库的头文件
    #elif __has_include(<YYWebImage/YYImage.h>)
      //do sth
    #else
      //do sth
    #endif
    

    查看.a库是否支持bitcode:

    1. 首先需要判断 library 是否是 fat 的,可以用 lipo 命令:
      lipo -info ibDHxls.a
    
    2. 如果是 fat library(支持多构架),需要将某个 CPU 架构的 slice 提取出来:
      lipo -thin arm64  ibDHxls.a -output libd-arm64.a
    
    3. 接下来我们需要将这个 slice 里面的目标文件解压出来,可以用 ar 命令:
      ar -x libd-arm64.a
    
    4. 解压完后当前的目录下会有多个.o文件,你选择其中一个.o文件执行下面的命令就可以:
      otool -l unit.o | grep bitcode //我这里选择的是:`unit.o`
    
    5. 如果找到了,说明第三方库是支持 bitcode 的:
      `sectname __bitcode`
    
    

    禁止WKWebView缩放:

    1. 在HTML里边的mata标签加入user-scalable = no,若是原生js交互的话可采用注入js的方法更改meta。
    - (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
    {
        NSString *injectionJSString = @"var script = document.createElement('meta');"
        "script.name = 'viewport';"
        "script.content=\"width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\";"
        "document.getElementsByTagName('head')[0].appendChild(script);";
        [webView evaluateJavaScript:injectionJSString completionHandler:nil];
    
    }
    
    1. 第一种方法的缺点:你无法再手动控制缩放比例了,并且在双击、或者遇到文本输入的时候,可能还是会自动缩放,下面使用第二种方法完美控制(ios11之前,ios之后失效):
    • 设置代理
    self.wk_WebView.scrollView.delegate = self;
    
    • webview的控制类中,设立一个控制属性,并初始化设置为YES:
    self.allowZoom = YES;
    
    • 实现scrollView的代理:
    // wkwebview在加载网页之后,会先自动适应缩放一次,
    // 如果在这个代理方法中直接return nil,会导致无法按系统建议的缩放比例正确的显示,
    // 所以需要利用我们申明的那个bool值来控制
    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
        //让它只有在我们允许的时候,才能缩放
        if(self.allowZoom){
            return nil;
        }else{
            return self.wk_WebView.scrollView.subviews.firstObject;
        }
    }
    
    • 在网页加载完之后,(此时系统已经为我们缩放了网页),关闭缩放
    -(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
        self.allowZoom = NO;
    }
    
    • 需要时,手动控制:
    self.allowZoom = YES;
    [self.wk_WebView.scrollView setZoomScale:1.2 animated:NO];
    

    手机基本信息

    iOS 将时间NSDate转化为毫秒时间戳

    对于将NSDate类型转换为时间戳,有直接转10位数值的时间戳方法,但是没有精确到毫秒,这种数值在转化为 NSDate类型的时候,就会出点儿错,每一个时间的毫秒都是为000的;
    因为 [[NSDate date] timeIntervalSince1970] 虽然可以获取到后面的毫秒、微秒 ,但是在保存的时候省略掉了。如一个时间戳不省略的情况下为 1395399556.862046 ,省略掉后为一般所见 1395399556 。所以想取得毫秒时用获取到的时间戳 *1000 ,想取得微秒时 用取到的时间戳 * 1000 * 1000 。这样就解释了上面的10位数值的问题,当你取毫秒的时候,就会变成13位数值了。我想这样大家应该明白了吧!

    2个小函数,这2个函数呢,是互逆的:

    • 将时间戳转换为NSDate类型
    -(NSDate *)getDateTimeFromMilliSeconds:(long long) miliSeconds
    {
        NSTimeInterval tempMilli = miliSeconds;
        NSTimeInterval seconds = tempMilli/1000.0;//这里的.0一定要加上,不然除下来的数据会被截断导致时间不一致
        NSLog(@"传入的时间戳=%f",seconds);
        return [NSDate dateWithTimeIntervalSince1970:seconds];
    }
    
    • 将NSDate类型的时间转换为时间戳,从1970/1/1开始
    -(long long)getDateTimeTOMilliSeconds:(NSDate *)datetime
    {
        NSTimeInterval interval = [datetime timeIntervalSince1970];
        NSLog(@"转换的时间戳=%f",interval);
        long long totalMilliseconds = interval*1000 ;
        NSLog(@"totalMilliseconds=%llu",totalMilliseconds);
        return totalMilliseconds;
    }
    

    解决ios11上 从状态栏下拉或底部栏上滑,跟系统的下拉通知中心手势和上滑控制中心手势冲突

    https://blog.csdn.net/auccy/article/details/78978036

    相关文章

      网友评论

          本文标题:iOS零碎知识点<初级版>

          本文链接:https://www.haomeiwen.com/subject/ecgucxtx.html