Source code reading analysis

1, source code reading tools

xcode12.4

Xcode is an integrated development tool (IDE) that runs on the Mac OS X operating system and is developed by Apple Inc. Xcode is the fastest way to develop macOS and iOS applications. Xcode has a unified user interface design, coding, testing, debugging are all done in a simple window.

2. Project introduction

DoraemonKit is a functional platform that allows every App to quickly access some commonly used or unimplemented auxiliary development tools, test efficiency tools, visual AIDS, and perfect access to the Doraemon panel of some of the non-common auxiliary tools that you have implemented and are tightly coupled with the business. With dokit platform, the function can be extended, easy access, easy to expand.

Simple summary

1, DoraemonKit can quickly make your business test code can be unified management here, unified closure;

2, DoraemonKit built-in many common tools, avoid repeated implementation, one access, you will have a powerful tool set;

3. With DoKit platform, Mock interface, health check and file synchronization assistant make it easy for you to cooperate with others, greatly improving the efficiency of research and development.

3, UI control Color Picker analysis

DoKit is rich in features, and here I mainly analyze and read the source code of the UI component Color Picker.

Component functions:

Color Picker is a Color Picker that can be used to pick the Color of each point on the screen, and can view the hexadecimal Color value of the point, which is convenient for developers to directly test the Color value of each point in their own programs, instead of taking screenshots to Ps and other software for comparison. It’s a pretty useful test plug-in.

Source code analysis:
1. Source code directory analysis

Color Picker source code is mainly indirectory

The DoraemonColorPickPlugin. M file declares the plug-in window, which includes three Windows altogether

@implementation DoraemonColorPickPlugin

- (void)pluginDidLoad {
    [[DoraemonColorPickWindow shareInstance] show];
    [[DoraemonColorPickInfoWindow shareInstance] show];
    [[DoraemonHomeWindow shareInstance] hide];
}

@end
Copy the code

It also contains a hidden DoraemonHomeWindow, which is the main window class of DoKit.

2. Analysis of function implementation

This part mainly realizes the functions of two views, namely the magnifying glass view and the color information box below.

Color information box

There are four files in this section:

  • DoraemonColorPickInfoView.h
  • DoraemonColorPickInfoView.m
  • DoraemonColorPickInfoWindow.h
  • DoraemonColorPickInfoWindow.m

Realize the two main classes are respectively DoraemonColorPickInfoView classes and class DoraemonColorPickInfoWindow, defining the color information box UIView view and the types of UIWIndow to forward information to the view.

Including DoraemonColorPickInfoView class defines the component attributes of the information box,

@ interface DoraemonColorPickInfoView () / / colorView matching the color, the color @ property (nonatomic, strong) UIView * colorView; @property (nonatomic, strong) UILabel *colorValueLbl; @property (nonatomic, strong) UIButton *closeBtn; @endCopy the code

ColorView of UIView type corresponds to the color property in the color information box, colorValueLbl of UILabel type corresponds to the text box showing the hexadecimal value of the color, and closeBtn of UIButton type. It corresponds to the close button of the color information box.

In addition to defining these attributes, the class also defines initialization methods for these attributes.

For example, the initialization of colorView, set the size of the color box in the color information box and the initial color value

- (UIView *)colorView { if (! _colorView) { _colorView = [[UIView alloc] init]; _colorView.layer.borderWidth = 1.; _colorView. Layer. BorderColor = [UIColor doraemon_colorWithHex: 0 x999999 andAlpha: 0.2]. CGColor; } return _colorView; }Copy the code

There is also the initialization of colorValueLbl, setting the font size and font color

- (UILabel *)colorValueLbl { if (! _colorValueLbl) { _colorValueLbl = [[UILabel alloc] init]; _colorValueLbl.textColor = [UIColor doraemon_black_1]; _colorValueLbl.font = [UIFont systemFontOfSize:kDoraemonSizeFrom750_Landscape(28)]; } return _colorValueLbl; }Copy the code

The same goes for closeBtn initialization.

- (UIButton *)closeBtn { if (! _closeBtn) { _closeBtn = [[UIButton alloc] init]; UIImage *closeImage = [UIImage doraemon_xcassetImageNamed:@"doraemon_close"]; #if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) if (@available(iOS 13.0, *)) { if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { closeImage = [UIImage doraemon_xcassetImageNamed:@"doraemon_close_dark"]; } } #endif [_closeBtn setBackgroundImage:closeImage forState:UIControlStateNormal]; [_closeBtn addTarget:self action:@selector(closeBtnClicked:) forControlEvents:UIControlEventTouchUpInside]; } return _closeBtn; }Copy the code

DoraemonColorPickInfoView class also implements the setCurrentColor method, set the current color to the color information in the message box and color information. This method can be fulfilled in a magnifying glass view DoraemonColorPickWindow pan of a class method is called, which is based on the color information of the center of the magnifying glass to DoraemonColorPickInfoView hexColor class, realized the color information in the message box display.

- (void)setCurrentColor:(NSString *)hexColor{
    self.colorView.backgroundColor = [UIColor doraemon_colorWithHexString:hexColor];
    self.colorValueLbl.text = hexColor;
}
Copy the code

Then in DoraemonColorPickInfoWindow. M defines two classes, Are respectively DoraemonColorPickInfoController UIViewController type and type of UIWindow DoraemonColorPickInfoWindow,

The UIWindow object does not provide its own visible content. All of the visible content of a window is provided by its root view controller, which can be configured in the application’s storyboard. The window’s job is to receive events from UIKit and forward any related events to the root view controller and related views.

In the class, defines some instance method is used to control DoraemonColorPickInfoView class attributes in the content, which show and hide respectively used to implement the view of the show and hide.

@interface DoraemonColorPickInfoWindow : UIWindow

+ (DoraemonColorPickInfoWindow *)shareInstance;

- (void)show;

- (void)hide;

- (void)setCurrentColor:(NSString *)hexColor;

@end
Copy the code

SetCurrentColor sets the color content in the current color information box.

- (void)setCurrentColor:(NSString *)hexColor {
    [self.pickInfoView setCurrentColor:hexColor];
}
Copy the code

Including pickInfoView is DoraemonColorPickInfoView type of attribute.

@interface DoraemonColorPickInfoWindow()"DoraemonColorPickInfoViewDelegate>

@property (nonatomic.strong) DoraemonColorPickInfoView *pickInfoView;

@end
Copy the code

There are also actions defined in this class, such as the close Color box action to close the color box view.

- (void)closeBtnClicked:(id)sender onColorPickInfoView:(DoraemonColorPickInfoView *)colorPickInfoView {
    [[NSNotificationCenter defaultCenter] postNotificationName:DoraemonClosePluginNotification object:nil userInfo:nil];
}
Copy the code
Magnifying glass view

This section consists of six files

  • DoraemonColorPickMagnifyLayer.h
  • DoraemonColorPickMagnifyLayer.m
  • DoraemonColorPickView.h
  • DoraemonColorPickView.m
  • DoraemonColorPickWindow.h
  • DoraemonColorPickWindow.m

This section mainly defines three classes

  • CALayer DoraemonColorPickMagnifyLayer type
  • DoraemonColorPickView of UIView type
  • DoraemonColorPickWindow of UIWindow type

The DoraemonColorPickMagnifyLayer defines a layer, the image information of the content of the layer was used to build a magnifying glass.

Layers are usually used to provide backup storage for views, but they can also be used to display content without a view. The main job of layers is to manage the visual content you provide, but layers themselves have visual properties that can be set, such as background colors, borders, and shadows. In addition to managing visual content, this layer maintains information about the geometry of its content (such as its location, size, and transformation), which is used to render that content on the screen. Modifying the properties of a layer is a way to animate the content or geometry of the layer.

Four methods are implemented in this class:

  • DrawInContext, call gridCirclePath to draw the magnifying glass area and drawGridInContext to draw the grid content information

    - (void)drawInContext:(CGContextRef)ctx {
        // Grid clipping for the internal magnifier
        CGContextAddPath(ctx, self.gridCirclePath);
        CGContextClip(ctx);
        / / draw grid
        [self drawGridInContext:ctx];
    }
    Copy the code
  • DrawGridInContext, used to draw the grid content information in the magnifying glass

    - (void)drawGridInContext:(CGContextRef)ctx {
        CGFloat gridSize = ceilf(kMagnifySize/kGridNum);
        
        // Due to anchor point modification, offset is required here
        CGPoint currentPoint = self.targetPoint;
        currentPoint.x -= kGridNum*kPixelSkip/2;
        currentPoint.y -= kGridNum*kPixelSkip/2;
        NSInteger i,j;
        
        // Draw a grid in the magnifying glass and fill it with the colors of the current and surrounding points
        for (j=0; j<kGridNum; j++) {
            for (i=0; i<kGridNum; i++) {
                CGRect gridRect = CGRectMake(gridSize*i-kMagnifySize/2, gridSize*j-kMagnifySize/2, gridSize, gridSize);
                UIColor *gridColor = [UIColor clearColor];
                if (self.pointColorBlock) {
                    NSString *pointColorHexString = self.pointColorBlock(currentPoint);
                    gridColor = [UIColor doraemon_colorWithHexString:pointColorHexString];
                }
                CGContextSetFillColorWithColor(ctx, gridColor.CGColor);
                CGContextFillRect(ctx, gridRect);
                // Find the next adjacent point horizontally
                currentPoint.x += kPixelSkip;
            }
            // When a line is drawn, return horizontally to the starting point and search vertically for the next pointcurrentPoint.x -= kGridNum*kPixelSkip; currentPoint.y += kPixelSkip; }}Copy the code
  • MagnifyImage, draw this layer returns the image content of this layer

    - (UIImage *)magnifyImage {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO.0);
        
        CGContextRef ctx = UIGraphicsGetCurrentContext(a);CGFloat size = kMagnifySize;
        CGContextTranslateCTM(ctx, size/2, size/2);
        
        // Draw the clipping area
        CGContextSaveGState(ctx);
        CGContextAddPath(ctx, self.gridCirclePath);
        CGContextClip(ctx);
        CGContextRestoreGState(ctx);
        
        // Draw the magnifying glass edge
        CGContextSetLineWidth(ctx, kRimThickness);
        CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
        CGContextAddPath(ctx, self.gridCirclePath);
        CGContextStrokePath(ctx);
        
        // Draw the content between the two edge lines
        CGContextSetLineWidth(ctx, kRimThickness- 1);
        CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
        CGContextAddPath(ctx, self.gridCirclePath);
        CGContextStrokePath(ctx);
        
        // Draw the selection area of the center
        CGFloat gridWidth = ceilf(kMagnifySize/kGridNum);
        CGFloat xyOffset = -(gridWidth+1) /2;
        CGRect selectedRect = CGRectMake(xyOffset, xyOffset, gridWidth, gridWidth);
        CGContextAddRect(ctx, selectedRect);
        
    #if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0)
        if (@available(iOS 13.0, *)) {
            UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
                if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
                    return [UIColor blackColor];
                }
                else {
                    return [UIColorwhiteColor]; }}];CGContextSetStrokeColorWithColor(ctx, dyColor.CGColor);
        } else {
    #endif
            CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
    #if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0)
        }
    #endif
        CGContextSetLineWidth(ctx, 1.0);
        CGContextStrokePath(ctx);
        
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext(a);UIGraphicsEndImageContext(a);return image;
    }
    Copy the code
  • GridCirclePath, used to set the current magnifying glass region size

    - (struct CGPath *)gridCirclePath {
        if (_gridCirclePath == NULL) {
            CGMutablePathRef circlePath = CGPathCreateMutable(a);const CGFloat radius = kMagnifySize/2;
            CGPathAddArc(circlePath, nil.0.0, radius-kRimThickness/2.0.2*M_PI, YES);
            _gridCirclePath = circlePath;
        }
        return _gridCirclePath;
    }
    
    Copy the code

Using the above method you can define a magnifying glass layer to display on the DoraemonColorPickWindow.

The DoraemonColorPickView class defines two methods:

@interface DoraemonColorPickView : UIView

- (void)setCurrentImage:(UIImage *)image;

- (void)setCurrentColor:(NSString *)hexColor;

@end
Copy the code

But the class has been deprecated in DoraemonColorPickWindow, magnifying glass view by DoraemonColorPickMagnifyLayer defined and DoraemonColorPickWindow calls.

Next comes the most important DoraemonColorPickWindow class, which defines some basic methods and some important actions such as:

  • ColorAtPoint, this method achieves the current magnifying glass center point color information and returns

    - (NSString *)colorAtPoint:(CGPoint)point inImage:(UIImage *)image {
        // Cancel if point is outside image coordinates
        if(! image || !CGRectContainsPoint(CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), point)) {
            return nil;
        }
        
        // Create a 1x1 pixel byte array and bitmap context to draw the pixel into.
        // Reference: http://stackoverflow.com/questions/1042830/retrieving-a-pixel-alpha-value-for-a-uiimage
        NSInteger pointX = trunc(point.x);
        NSInteger pointY = trunc(point.y);
        CGImageRef cgImage = image.CGImage;
        NSUInteger width = image.size.width;
        NSUInteger height = image.size.height;
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(a);int bytesPerPixel = 4;
        int bytesPerRow = bytesPerPixel * 1;
        NSUInteger bitsPerComponent = 8;
        unsigned char pixelData[4] = { 0.0.0.0 };
        CGContextRef context = CGBitmapContextCreate(pixelData,
                                                     1.1,
                                                     bitsPerComponent,
                                                     bytesPerRow,
                                                     colorSpace,
                                                     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
        CGColorSpaceRelease(colorSpace);
        CGContextSetBlendMode(context, kCGBlendModeCopy);
        
        // Draw the pixel we are interested in onto the bitmap context
        CGContextTranslateCTM(context, -pointX, pointY-(CGFloat)height);
        CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
        CGContextRelease(context);
        
        NSString *hexColor = [NSString stringWithFormat:@"#%02x%02x%02x",pixelData[0],pixelData[1],pixelData[2]].return hexColor;
    }
    
    Copy the code
  • UpdateScreeShotImage, used to update the current screen snapshot and return

    - (void)updateScreeShotImage {
        UIGraphicsBeginImageContext([UIScreen mainScreen].bounds.size);
        [[DoraemonUtil getKeyWindow].layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext(a);UIGraphicsEndImageContext(a);self.screenShotImage = image;
    }
    
    Copy the code
  • Pan, used to track the magnifying glass as it slides with your finger, update the screen shot and reset the position of the magnifying glass control, while returning the color information of the center point of the current magnifying glass to the color information box.

    - (void)pan:(UIPanGestureRecognizer *)sender {
        if (sender.state == UIGestureRecognizerStateBegan) {
            // Update the screen snapshot when you start dragging
            [self updateScreeShotImage];
        }
        
        // Get the drag displacement
        CGPoint offsetPoint = [sender translationInView:sender.view];
        //2. Clear the drag displacement
        [sender setTranslation:CGPointZero inView:sender.view];
        //3. Reset the position of the control
        UIView *panView = sender.view;
        CGFloat newX = panView.doraemon_centerX+offsetPoint.x;
        CGFloat newY = panView.doraemon_centerY+offsetPoint.y;
        
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        
        CGPoint centerPoint = CGPointMake(newX, newY);
        panView.center = centerPoint;
        
        self.magnifyLayer.targetPoint = centerPoint;
        
        // update positions
        // self.magnifyLayer.position = centerPoint;
        
        // Make magnifyLayer sharp on screen
        CGRect magnifyFrame     = self.magnifyLayer.frame;
        magnifyFrame.origin     = CGPointMake(round(magnifyFrame.origin.x), round(magnifyFrame.origin.y));
        self.magnifyLayer.frame = magnifyFrame;
        [self.magnifyLayer setNeedsDisplay];
        
        [CATransaction commit];
        
        NSString *hexColor = [self colorAtPoint:centerPoint];
        [[DoraemonColorPickInfoWindow shareInstance] setCurrentColor:hexColor];
    }
    Copy the code

This allows you to update the view information in the magnifying glass and the color value information in the color extraction information box while the magnifying glass is constantly moving

4. Problem analysis and thinking

In DoraemonColorPickInfoWindow classes have a UIViewController type of DoraemonColorPickInfoController class,

@interface DoraemonColorPickInfoController: UIViewController

@end

@implementation DoraemonColorPickInfoController
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.view.window.frame = CGRectMake(kDoraemonSizeFrom750_Landscape(30), DoraemonScreenHeight - (size.height < size.width ? size.height : size.width) - kDoraemonSizeFrom750_Landscape(30), size.height, size.width);
    });
}
@end
Copy the code

This class declares a method called viewWillTransitionToSize. Later, I looked up the official documentation and learned that the UIViewController type is the interface used to manage UIKit applications.

UIViewController manages a single root view, which itself can contain any number of child views. User interaction with this view hierarchy is handled by your view controller, which coordinates with other objects in your application as needed. Each application has at least one view controller whose content fills the main window. If your application contains more content than was last displayed on the screen, use multiple view controllers to manage different parts of that content.

The methods defined in this class viewWillTransitionToSize a UIViewControllerTransitionCoordinator type parameters, then through access to information found this to be called a ferry, coordinator of the agreement for coordinating the transitions between views, The transition coordinator is only implemented in transition animations.

conclusion

As I have not been exposed to the content related to ios development before, I am not familiar with many Objective-C syntax when reading the source code. As a result, I may have a lot of misunderstandings and mistakes in understanding. Later, I gradually understood the methods of type declaration and definition in Objective-C, as well as the definition of attributes and protocols, and felt that there were many similarities with C++. For example, objective-C class methods and instance methods implemented with + and – are distinguished by the static keyword in C++.

The IOS part of DoKit has a lot of functions that have been implemented, and the content is quite large and complicated, so it is difficult to read. However, it is also a good learning opportunity. I hope that I can improve my grammar foundation and development practice ability through continuous learning.