High-quality graphical display plays a very important role in the interactive interface of an APP. High quality graphics make it more enjoyable for users. There are two main ways to create high quality graphics on iOS: OpenGL or using native Quarts, Core Animation, and UIKit. This article will cover the latter.

Quartz is the primary rendering approach that provides the ability to create display and parse path-based rendering, anti-aliasing, gradient rendering, graphic rendering, color, deformation, and PDF documents. UIKit is an encapsulation of Quartz’s line, image, and color manipulation. Core Animation provides support for modifying UIView properties in animations, as well as customizing animations.

This chapter will cover the rendering process in iOS apps and explain the principles used. Can help you learn a few tricks to optimize your own app rendering.

Important: Not all UIKit classes are thread-safe. Make sure you are on the main thread when you draw.

UIKit graphics system

In iOS, it doesn’t matter which technique is used (OpenGL, Quartz, UIKit, or Core Animation) to draw in UIView or subclass, it will be displayed on the screen. A view defines how it draws itself on the screen, or how it presents itself. Views provided by the system automatically define their own presentation. Custom views You must define how the view is presented. This chapter explains how to draw custom graphics using Quartz, Core Animation, and UIKit.

In addition, bitmaps and PDF graphics context can be drawn off-screen. . When you draw off-screen, the view life cycle doesn’t matter because you’re not drawing on the view.

View life cycle

The UIView subclass contains the most basic drawing model — updating itself as needed. The UIView class makes it easy and efficient to update its own drawings by batch updating drawings and updating itself when appropriate.

The drawRect: method is always requested by iOS when a view needs to be updated for the first time or when part of it needs to be updated.

Here are some actions that trigger view updates:

  • Move or delete views
  • By putting the view’shiddenProperty set toNO
  • The view that scrolls away needs to appear on the screen again
  • View is called explicitlysetNeedsDisplayorsetNeedsDisplayInRect:methods

The view system automatically triggers redrawing. For custom views, you must override the drawRect: method to perform all the drawing. Draw your own shape, text, image, gradient, or whatever you want to display in the method using the native draw API. When a view is first displayed, iOS passes a square area to represent the area the view is drawn on. To maximize performance, it is best to redraw only the affected areas when redrawing.

After calling the drawRect: method, the view marks itself as updated and waits for the next view update to be triggered. Static custom views need to handle view changes caused by scrolling or by other views. What does the second sentence mean? Didn’t quite understand)

If you want to change the view’s content, you must trigger the view to redraw its content. The update is triggered by calling the setNeedsDisplay or setNeedsDisplayInRect: method. Usage scenarios include updating the view multiple times per second, or new content appearing in the view based on user interactions.

Important: Do not explicitly call the drawRect: method. This method should only be reserved for system calls when iOS needs to be redrawn. Because the graphics context does not exist at any other time, the screen cannot be drawn. (Graphic context is explained in the next section.)

Drawing in coordinate system and iOS

When the App needs to draw a graph in iOS system, it must draw in a two-dimensional coordinate system. This seems simple enough, but in some drawing cases you need to deal with a different coordinate system.

IOS graphics need to rely on the graphics context to complete. In theory, a graphic context is used to describe where and how to draw, including information such as color drawing, cutting drawing areas, line thickness and style.

In addition, figure 1-1 shows that each graph context has a coordinate system. More precisely, there are three coordinate systems above and below each figure:

  • Draw the coordinate system. Plotting context coordinates drawn by using instructions.
  • View coordinate system. Fixed coordinate system relative to the view.
  • Device coordinate system. Physical screen pixel display coordinates.

Figure 1-1 Mapping coordinates, view coordinates, and hardware coordinates

Translator’s note: CTM is a concept in Quartz, which will be introduced below.

IOS’s drawing framework creates graphics contexts for drawing specific targets (screens, bitmaps, PDF content, and so on) that establish the initial drawing coordinate system for that destination. This initial coordinate system is called the default coordinate system and is mapped 1:1 to the view coordinate system. (Translator & Editor: Wang Yi)

Each view has its own Current Transformation Matrix (CTM), a digital proof that maps the current drawing coordinate system to the view coordinate system. App can modify the matrix to affect subsequent drawing operations.

IOS creates the graphics context based on the default coordinate system. In iOS, there are two main types:

  • The upper left origin coordinate system (ULO) is 0,0 from the upper left corner and positive from the right down. Both UIKit and Core Animation are based on ULO.
  • The lower left origin coordinate system (LLO) is 0,0 from the lower left and positive from the right up. Core Graphics is based on LLO.

Figure 1-2 shows the two coordinate systems

Figure 1-2 Default coordinate system in ios

Tip: MacOS uses LLO by default. Drawing via AppKit and CoreGraphics is based on this coordinate system, and AppKit provides conversion support for the upper left origin coordinate system.

Point and pixel

There is a difference between the coordinate system specified in iOS and the underlying device that draws pixels. When using native drawing columns such as Quartz, UIKit and Core Animation, the western and attempted coordinate systems are all logical coordinate systems, and the coordinate system values represent points and do not have a one-to-one relationship with the pixels on the device.

The system will automatically map to the pixel of the device based on the point coordinate value of the view, but not necessarily one-to-one mapping, which is important.

A point does not necessarily map to a physical pixel.

The main purpose of using pixels instead of dots is to make the view look the right size on the device and not make the view look too small because the screen has more pixels. The specific number of pixels corresponding to a point is determined by the system according to the current device hardware. On a retina screen, for example, a line is drawn to correspond to the width of the line in pixels. This mapping allows the size of the display to be roughly the same on a normal retina screen as it is on a higher-resolution screen.

Tip: A dot corresponds to 1/72 inch when rendering and printing a PDF in Core Graphics.

In iOS, UIScreen, UIView, UIImage, and CALayer all provide properties that describe the mapping ratio between pixels and points. For example, the contentScaleFactor property of UIKit’s View. On non-retinal screens, the value of this property is 1.0. On the Retina screen, it’s 2.0. Other values may also appear in the future. (it was 1.0 until iOS4).

Because of automatic mapping, you don’t need to care about pixels when drawing a view. Only when downloading high-resolution images for display on the retina screen, we need to pay attention to the scale of image rendering, so as to avoid the problem that high-score images are rendered larger by low-score ones.

In iOS, when you draw something on the screen, the graphics subsystem uses a technique called anti-aliasing to approximate a high-resolution image on a low-resolution screen. Let me give you an example. Draw a black vertical line on a white background. If the line falls on the pixel, it will appear as a series of black pixels as shown on the left. If it falls exactly on two pixels, a gray pixel will appear and draw two squares to the right of figure 1-3 below.

Figure 1-3

The point coordinates of the integer value will fall between the two pixels. For example, if you draw a line (1,1) through (1,10) one pixel wide, you’ll get a gray one. If you draw a line two pixels wide, you get a solid black line, because two pixels fill exactly two pixels. In general, lines with an odd number of physical pixel widths will appear shallower compared to the width of physical pixels with even widths if they are not positioned so that they completely cover the pixels.

The scale property is just to show how many pixels are mapped to a point.

On a non-retina screen, the scale is always 1.0, one point per pixel. To avoid anti-aliasing, when you draw a single dot, you need to offset it by 0.5 points if it takes up an odd integer width, not if it takes up an even integer width.

FIG. 1-4 Display of a one-point-width line on non-retinal and retinal screens

With a retina scale of 2.0, a single line won’t trigger anti-aliasing because it fills up to two pixels. If you want to draw a one-pixel line, you need to use the width of 0.5 points and offset it by 0.25 points.

Controlling pixel drawing directly on scale is not the best experience. A line one pixel wide may look fine on a non-retinal screen, but it will look too thin on a retinal screen. It depends on how you draw it.

Getting the image Context

The image context can be obtained in the drawRect: method and drawn immediately. UIView provides the context for drawing the image context.

If you want to draw outside of the view (for example, to capture a series of drawing operations in a PDF or bitmap file), or if you need to call a core graphics function that requires a context object, you must take additional steps to get a graphics context object. The following chapter explains why.

For more information on modifying image context state and creating custom content, see Quartz 2D Programming Guide. [CGContext Reference], [CGBitmapContext Reference], [CGPDFContext Reference]

Draw on the screen

To draw on the screen, you need to get the image context in the drawRect: method. (The first argument in this series of methods is a CGContextRef object.) By calling the UIGraphicsGetCurrtnContext method, the drawRect: method to obtain a graphics context. (Multiple fetches will give you the same result.)

In UIKit’s View, drawing using the Core Graphics family of methods is based on ULO coordinates. Alternatively, flip the CTM to draw using LLO coordinates. For details, please read [Flipping the Default Coordinate System]

UIGraphicsGetCurrentContext function always returns the current context. For example, the context retrieved after the PDF is created is the PDF context. You must use this method to get context whenever you draw using the Core Graphics family of functions.

Tip: Print-related functions are placed in the UIPrintPageRender class. Similar to drawRect:, UIKit provides print-related implementations. And the default is based on ULO coordinates.

Draw bitmaps and PDF

UIKit provides context and a set of functions for drawing bitmaps and PDFS. Both creation methods require a separate function call to create its corresponding context. Draw through the context, and close the context when the drawing is complete.

Both contexts are also based on ULO coordinates. Core Graphics provides a series of methods for rendering in bitmap context and in PDF context. The context is obtained from a direct call to a function from Core Graphics and drawn in an LLO coordinate system.

** Tip: ** In iOS, it is still recommended to use UIKit’s related functions to get context for drawing. If you have to use the relevant methods in Core Graphics to draw, you need to be compatible with coordinate system differences. (Translator: So the context-dependent methods in UIKit are actually Core Graphics methods that transform coordinate systems.)

For details, see Creating a Draw bitmap and Creating a PDF

Color and gamut

Although Quartz supports full gamut on iOS; But almost all apps only use RGB color gamut. After all, iOS is designed to draw and render on the screen, and RGB is the best fit.

UIColor objects provide a series of convenient ways to create colors from RGB/HSB and grayscale values, without worrying about gamut, which is automatically determined by UIColor objects.

You can also use the Core Graphics framework of CGContextSetRGBStrokeColor and CGContextSetRGBFillColor function set colors to create a club. Although Core Graphics provides functions to specify gamut and create custom gamut, it is not recommended for use in code. Translator: Why not? Why?) It is recommended to always use RGB color gamut.

Draw using Quartz and UIKit

We call graphics in iOS collectively Quartz. The Core Graphics framework is the heart of Quartz and does most of the drawing. The framework provides data types and functions to support the following capabilities:

  • Graphic context
  • The path
  • Pictures and bitmaps
  • Transparent layer
  • Color and gamut
  • Gradients and Shadows
  • The font
  • PDF

UIKit provides a set of classes for graphical manipulation based on Quartz. The purpose is not to replace Core Graphics, but rather to provide drawing support for other UIKit classes:

  • UIImage/UIColor/UIFont/UIScreen/UIBezierPath
  • Function to generate a JPEG or PNG image object
  • Function to get the bitmap context
  • Function to get the PDF context
  • Function to draw a rectangle and crop a drawing area
  • Function to get the current graph context

UIKit Framework Reference for more information, and Core Graphics Reference.

Configuring the Graphics Context

Before calling the drawRect: method, the view object has created a graphics context and set it to the current context. It only exists during a drawRect: method call. Could be obtained by UIGraphicsGetCurrentContext function called a reference of the graphics context. The CGContextRef method returns a reference to an object of type CGContextRef that passes the Core Graphics function to modify the state of the current graph. Table 1-1 lists the main methods. To view the full CGContext Reference, go to CGContext Reference. The following table also lists UIKit alternatives.

Table 1-1 Core Graphics methods for modifying Graphics state

state The function name UIKit alternatives
Current transformation matrix (CTM) CGContextRotateCTM/CGContextScaleCTM/CGContextTranslateCTM/CGContextConcatCTM None
Clipping area CGContextClipToRect UIRectClip function
Line: Width, join, cap, dash, miter limit CGContextSetLineWidth/CGContextSetLineJoin/CGContextSetLineCap/CGContextSetLineDash/CGContextSetMiterLimit None
Accuracy of curve estimation CGContextSetFlatness None
Anti-aliasing setting CGContextSetAllowsAntialiasing None
Color: Fill and stroke settings CGContextSetRGBFillColor/CGContextSetRGBStrokeColor UIColor class
Alpha global value (transparency) CGContextSetAlpha None
Rendering intent CGContextSetRenderingIntent None
Color space: Fill and stroke settings CGContextSetFillColorSpace/CGContextSetStrokeColorSpace UIColor class
Text: Font, font size, character spacing, text drawing mode CGContextSetFont/CGContextSetFontSize/CGContextSetCharacterSpacing UIFont class
Blend mode CGContextSetBlendMode The UIImage class and various drawing functions let you specify which blend mode to use.

The context holds the loading of the graph as a stack. The stack is empty when the context is created by Quartz. The current graph state is pushed onto the stack by calling the CGContextSaveGState function. Subsequent changes in graph state affect subsequent drawing operations, but do not affect those that are already on the stack. When the modification is complete, it can be popped from the stack by calling the CGContextRestoreGState function. This push and pop operation replaces the operation of undoing each state individually. It’s the only way to get back to where we were before.

Refer to Graphics Context and Quartz 2D for more information

Draw the path

A path is a vector shape consisting of a series of lines and Bezier curves. UIKit contains functions such as UIRectFrame and UIRectFill to draw simple paths (like rectangles). Core Graphics also provides convenient functions for drawing simple paths (such as rectangles and ellipses).

For more complex paths, you need to draw them yourself using the UIBezierPath class, or use functions to manipulate CGPathRef provided by Core Graphics. Although paths can be drawn out of context, ultimately context is used underneath, but encapsulated.

End — — — — — —

Original: iOS Drawing Concepts

read

CTM