The project code is available on GitHUb: github.com/yishuiliuni…

A little digression first. When we do daily work related to IOS UI, there is a component that is used very frequently – UITabelView. Therefore, we are required to understand every function interface of UITableView, every attribute is well known, only in this way when using UITableView, we can handle various requirements with ease. Otherwise, most of the time, the function is only realized, but the program efficiency and code maintainability are relatively poor. So for example, let’s say I want to display some text in the head of a tableView. The most verbose solution I’ve seen looks like this:

  1. Subclass a UIViewController
  2. Set the root View to a UIScrollView
  3. Append the header Label and TableView to the ScrollView
  4. Start tweaking the parameters in the ScrollView and TableView’s delegate call functions so that the Label slides along with the TableView

In fact, if you’re familiar with UITableView, you can do it in a few words

UILabel* label = [UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 40); Label. text = @" just a paragraph of text "; tableVIew.tableHeaderView = label;Copy the code

In fact, programming languages and libraries are essentially tools. You want to use these tools to fulfill the requirements of the product and the Leader. Of course, not only the implementation of the function, but also program efficiency, code quality. In particular, code quality is a nightmare if you don’t want to maintain your own code later, if you don’t want to break your code every time a new requirement comes in, and if you don’t want to bury the hatchet for others. Then you’d better pay more attention.

Code quality here does not simply refer to code comments, using Xcode provided by pragam or #warning to explain code. The Art of Writing Readable Code and other programming books say that truly high-quality code doesn’t need comments. A good code is logically and structurally clear. I see a lot of code that is difficult to maintain because of logical structure chaos and the abuse of design patterns. In many cases, it is because the code writers are not familiar with the tools they use (mainly objC and UIKit), so they write a lot of makeshift schemes to simply implement the functions. It looks good on the surface, but in fact the code is so strong that it’s all messed up inside. It’s going to be a pain to maintain.

At the same time, I’ve always felt that implementing TableView on my own is a rite of passage for IOS development. You can really get into some of the technical details of UIKit by implementing a UITableView, and get into some of the tools that IOS UI programming uses. That way, you don’t have to run out of money to write programs.

Back to the point. Let’s start implementing a TableView.

The foundation that UIKit gives us

Again, to do a good job, you must first sharpen your tools. So let’s take a look at the tools that UIKit gives us to implement a TableView (not subclassing a UITableView, of course).

Geometric layout frame

A translation of Core Animation Programming translates UIKit’s layout model into a geometric layout model, which is a good word. The original English was “Struts and Springs”. The literal translation is structure and springs. In fact, it is an absolute layout model, the core data of this layout model is the geometric properties of an object. So the translation into geometric layout model is more appropriate.

One of the core data structures in UIKit’s geometric layout model is CGRect, which determines the absolute position of a View (or Layer, we’ll just focus on the View here, without going into detail) in the coordinate system in the parent View.

Let’s take a look at the definition of CGRect:

/* Points. */
 
struct CGPoint {
  CGFloat x;
  CGFloat y;
};
typedef struct CGPoint CGPoint;
 
/* Sizes. */
 
struct CGSize {
  CGFloat width;
  CGFloat height;
};
typedef struct CGSize CGSize;
 
/* Rectangles. */
 
struct CGRect {
  CGPoint origin;
  CGSize size;
};
typedef struct CGRect CGRect;Copy the code

We found that a CGRect actually contains an origin (point) and a set of width and height information (size). In fact, a CGRect describes a rectangular block, like the red block in the picture below. Each of our views will be represented as a rectangular block in the coordinate system.

For example, we have a View with position {{10,10},{20,20}} :

UIView* aView = [UIView new];
aView.frame = CGRectMake(0, 0 , 100 ,100)Copy the code

The following figure is displayed in the coordinate system of its parent class:

We can see that the geometric position described by the frame information of the red View is actually its absolute position in the parent View’s coordinate system. It’s written all over there. So layouts like UIKit are also called pairs of layouts. If you’ve ever used Swing in jave or QT in c++, you might find this absolute layout model cumbersome and verbose. Without the concept of a layout manager, everything is absolute. But each has its advantages. Complex interfaces with layout managers such as QT are convenient, but on mobile devices such as the iPhone, where the screen is limited and the device’s performance is limited, an absolute layout model is appropriate. Apple in iOS 5 after the introduction of some relative Layout things (autolayout) just here is an article about its Auto Layout Performance on iOS. After reading it, you can see that auto layout does not perform well in complex interface situations. So a primitive absolute layout like UIKit has some performance advantages.

Back to the figure above, we can see that UIKit’s coordinate system is a two-dimensional plane coordinate system, starting at the upper left corner, extending horizontally on the x axis and vertically down on the y axis. The line of defense on the y axis might be a little bit different than the coordinate system that we learned when we were in school. This is probably because when we think about layout on the ios screen, we tend to do it from the top down, so the Y-axis goes down. Now that we know that UIKit’s coordinate system is a two-dimensional planar coordinate system, a lot of the geometry that we’ve learned in the past can be applied to this coordinate system. There’s a lot of stuff going on here, but it’s also a little bit of a foretaste, knowing that we’re going to be using a lot of geometry in some of our TableViews.

At the same time, you can View the whole UIKit View layout system as a recursive system, where a View is laid out in its parent View, and the parent View is laid out in its parent View, until it’s laid out on UIWindow. So this recursive layout builds up the interface that we see in our app.

Uiview-related functions

Some general functions

Let’s start with some UIView functions, and let’s focus on some of the ones that are related to layout. For the purpose of making a TableView, let’s familiarize ourselves with what we’re going to use, and let the rest of the reader take a look at the document.

1. Initialize function?- (id)initWithFrame:(CGRect)aRect

Objc builds an object using two steps, first allocating memory alloc and then init. This has the advantage of decoupling memory operations from initialization operations, allowing us to do the necessary operations on the object at initialization time. This is a good idea, and we can use this two-step idea for a lot of things. For example, laying out a UIView, we can split it into two parts, initialize the necessary child views and variables, and then lay it out when appropriate.

And the first step of this two-stage formula is:

- (id)initWithFrame:(CGRect)aRectCopy the code

This function is one that will be called no matter what initializer you use. For example, if you use [UIView new] or [[UIView alloc] init], you will call initWithFrame (some UIView subclasses have special cases, UITableViewCell, for example, suspected apple to have done something special to it), so if you have an initialization operation on a view variable, it’s quite appropriate to try to put it inside initWithFrame. This ensures that all variables are correctly initialized for future use. And what do we normally do inside initWithFrame?

  1. Add child View
  2. Initialize the property variable
  3. Some other common operations

So we typically see code like this

- (instancetype) initWithFrame:(CGRect)arect { self = [super WithFrame:arect]; if (! self) { return nil; } [self commitInit]; <#init data#> return self; } - (void) commomInit { <#common init data#> }Copy the code

In the beginning, separate some common initialization operations into a function commomInit and then do the above. The advantage of this is to centralize the initialization code together. If you are implementing a different type of initWithXXX, just call commonInit directly.

Don’t let the name withFrame fool you into thinking this function uses layout. In projects where the code logic is clear, you rarely see interface layout work in this function. Because UIKit gives you a special function called layoutSubViews to do that. What’s more, all the layout work done in this function is one-time code. Your layout doesn’t have any reuse. If the parent View changes size, the View will remain the same as before. It also makes initialization functions bloated, which makes maintenance difficult.

2,layoutSubviewsandsetNeedsLayout

It says something about initWithFrame. It warns you never to do interface layout init. Where should you do it?

layoutSubviewsCopy the code

This is the place, this is the function that Apple gives you to do interface layout.

Let’s take a look at the documentation:

The default implementation of this method does nothing on iOS 5.1 and earlier. Otherwise, the default implementation uses any constraints you have set to determine the size and position of any subviews. Subclasses can override this method as needed to perform more precise layout of their subviews.

You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.

You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.

Apple says this is for layout when you subclass a View. Well, we’d better do the layout in there.

How to layout

This is an interesting topic, because a lot of people think it’s easy, you know, absolute layout is just writing dead numbers, so you can just write CGRectMake(10,10,20,20). If you do, the following may be helpful.

First of all, try not to write dead numbers directly in the layout. It is safer to use constant or macro definitions, even if you define a temporary variable, so that the code will be more maintainable.

Secondly, who says that the framework of absolute layout cannot be written in a relative layout way? Apple provides a CGGeometry. H file that defines a number of functions for convenient geometric layout. For example CGRectGetMaxX is used to get the maximum X-coordinate of a View. What good is that, you might ask? Let’s look at this code:

    _imageView.frame = CGRectMake(0, 0, width, height);
    _textLabel.frame = CGRectMake(CGRectGetMaxX(_imageView.frame), 0, CGRectGetWidth(self.frame) - CGRectGetMaxX(_imageView.frame), CGRectGetHeight(self.frame));Copy the code

The layout of the textLabel below is determined by the size of the imageView. Isn’t that what some layout managers do? That’s the concept of relative layout. So we can definitely use UIKit’s geometry coordinate system to do some relative layout things, and it’s recommended to do so.

When to layout

This depends on the function, but one thing is for sure: do not call layoutSubviews directly. UIKit and Runtime are closely tied together, and Apple prevents the interface from rearranging too often, so it only does layout work when runloop is appropriate. For details, Google.

Usually when you need to rearrange, you call setNeedsLayout and say, “I need to rearrange.” That’s it, and the system will give you the layout at the appropriate time for the next runloop.


3. Touch event response related functions

We usually don’t just lay out the View on the screen. We also need to provide the ability for users to interact with these views. I’m going to go ahead and give you a great post by someone who wrote it: Touch events and gestures on IOS

4. Other functions

There are other functions that require less attention and can be read directly from the documentation. For example:

Functions to adjust UIView structure tree:

- addSubview: - insertSubview: atIndex: - insertSubview: belowSubview: - exchangeSubviewAtIndex: withSubviewAtIndex: - (void)removeFromSuperviewCopy the code

Automatic layout related properties:

   autoresizingMask  
   autoresizesSubviewsCopy the code

UIScrollView related functions

The parent class of UITableView is UIScrollView. Of course, if we want to implement a TableView we also need to inherit from UIScrollView, so we need to look at some of the properties and methods of UIScrollView. Understanding Scroll Views is a good article to explain UIScrollView. Understand UISCrollView

Realize the TABLEView

Ok, now that we’re done with the work above, we have a general idea of what basic tools UIKit provides us. Looks like we can get started. No, wait. It looks like we’re missing something. It seems to be something related to design patterns, such as share patterns, chain of responsibility patterns, etc. These are the things we’re using. Let’s talk about them. Readers can also keep a book of design patterns handy for emergencies.

The project-related code can be obtained from DZTableView.

Take a look at the effect:

Here’s what we’ve achieved:

  1. Basic TableView to Cell layout
  2. Add and delete cells
  3. Right – swipe to appear the Delete and Edit menu
  4. Drop down input and create a new cell

No more nonsense and start working!!

Explain the overall UI hierarchy

The following figure roughly illustrates the View structure tree of DZTableView.

The whole TableView is divided into two main parts :DZTableView and DZTableViewCell. This structure is similar to the structure of UITableView.

DZTableView is the main part of tableView, mainly responsible for the layout and rendering of the whole tableView. DZTableViewCell is the object that is laid out and rendered. DZTableView just implements the vertical layout of the tableView on the y axis, without grouping. And we usually see a lot of cool right swipe delete effects are extended on DZTableViewCell.

DZTableViewCell’s most basic class has three main levels:

  1. The selectedBackgroudView that is responsible for rendering the transition state
  2. The actionsView that is responsible for rendering and controlling the slide effect, and the object of the various functions on the actionsView is DZCellActionItem
  3. The contentVIew is responsible for rendering the Cell body content.

The main task of completing a TableView is to arrange cells reasonably on UISCrollView.

Subclass UIScrollView to layout cells

Explain why you inherit from UIScrollView to complete the TableView. And this is really related to what TableView does. TableView is a layout with an indefinite amount of content, so it needs to display an infinite amount of content on a finite screen (640*960). The class that does this is UIScrollView. So DZTableView inherits from UIScrollView.

@interface DZTableView : UIScrollViewCopy the code

And then we’ll look at how to lay it out. Analyze, a vertical TableView layout, basically one Cell after another in the vertical determine their frame can be laid out. So our main task is to locate the cell.

To determine the location of the cell we define a number of variables:

typedef map<int, float> DZCellYoffsetMap; typedef vector<float> DZCellHeightVector; . DZCellHeightVector _cellHeights; DZCellYoffsetMap _cellYOffsets;Copy the code

CellHeights stores the heights of all cells, while cellYOffsets store the Y-axis coordinates of each cell. Each cell is filled in landscape. The layout starts at the left of the View (x=0) and goes all the way to the right (width= the width of the View). So the absolute position of a cell is:

- (CGRect) _rectForCellAtRow:(int)rowIndex
{
    if (rowIndex < 0 || rowIndex >= _numberOfCells) {
        return CGRectZero;
    }
    float cellYoffSet = _cellYOffsets.at(rowIndex);
    float cellHeight  = _cellHeights.at(rowIndex);
    return CGRectMake(0, cellYoffSet - cellHeight, CGRectGetWidth(self.frame), cellHeight);
}Copy the code

The key temporary variables mentioned at the beginning were initialized in the reduceContentSize function

- (void) reduceContentSize
{
    _numberOfCells = [_dataSource numberOfRowsInDZTableView:self];
    _cellYOffsets = DZCellYoffsetMap();
    _cellHeights = DZCellHeightVector();
    float height = 0;
    for (int i = 0  ; i < _numberOfCells; i ++) {
        float cellHeight = (_dataSourceReponse.funcHeightRow? [_dataSource dzTableView:self cellHeightAtRow:i] : kDZTableViewDefaultHeight);
        _cellHeights.push_back(cellHeight);
        height += cellHeight;
        _cellYOffsets.insert(pair<int, float>(i, height));
    }
    if (height < CGRectGetHeight(self.frame)) {
        height = CGRectGetHeight(self.frame) + 2;
    }
    height += 10;
    CGSize size = CGSizeMake(CGRectGetWidth(self.frame), height);
 
    [self setContentSize:size];
    [self reloadPiceGradientColor];
}Copy the code

In this way, we can confirm the absolute position of each cell in the TableView, so that the layout of the normal situation, or the layout of adding or deleting cells, is relatively simple. Call _rectForCellAtRow directly to get the cell’s frame, and the layout is ready.

Reuse of the Cell

We should be familiar with this interface when using UITableView:

- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
 
//ios6
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPathCopy the code

Before using a Cell, we first check whether there is a reusable Cell in the tableView. If there is, we use the reusable Cell. Only when there is no Cell, we create a Cell. This is the share mode.

The sharing mode can be understood as: when the number of fine-grained objects is very large, the cost of running will be quite large. At this time, the sharing technology will be used to greatly reduce the cost of running. The outstanding performance is that the content effectively inhibits the occurrence of memory jitter and controls memory growth. Its English name is flyweight, let the weight fly. Ha ha. True to its name, a Cell is a reusable element in a TableView, and the number of cells that need to be laid out is often large. If you create a Cell object every time you use it, the content jitter of the system will be obvious, and the system will consume a lot of memory. Suddenly it occurs to me that the share mode just provides an overbearing name for object instance sharing.

An example UML diagram of a typical share pattern is as follows:

In DZTableView, the storage and sharing of Cell instances in the share mode are mainly completed in tableView.

 NSMutableSet*  _cacheCells;
 NSMutableDictionary* _visibleCellsMap;Copy the code

We define two containers for storing two different types of cells:

  1. _cacheCells Stores unused cells that can be reused
  2. _visibleCellsMap stores active cells as key-value pairs. Key is the sequential information of the cell, that is, the number of cells from top to bottom.

And we get a cell function as follows:

- (DZTableViewCell*) _cellForRow:(NSInteger)rowIndex { DZTableViewCell* cell = [_visibleCellsMap objectForKey:@(rowIndex)]; if (! cell) { cell = [_dataSource dzTableView:self cellAtRow:rowIndex]; DZCellActionItem* deleteItem = [DZCellActionItem buttonWithType:UIButtonTypeCustom]; deleteItem.backgroundColor = [UIColor redColor]; [deleteItem addTarget:self action:@selector(deleteCellOfItem:) forControlEvents:UIControlEventTouchUpInside]; [called deleteItem setTitle: @ "delete" forState: UIControlStateNormal]; deleteItem.edgeInset = UIEdgeInsetsMake(0, 10, 0, 260); DZCellActionItem* editItem = [DZCellActionItem buttonWithType:UIButtonTypeCustom]; editItem.edgeInset = UIEdgeInsetsMake(0, 80, 0, 180); editItem.backgroundColor = [UIColor greenColor]; [editItem setTitle: @ "edit" forState: UIControlStateNormal]; [editItem addTarget:self action:@selector(editCellOfItem:) forControlEvents:UIControlEventTouchUpInside]; cell.actionsView.items = @[deleteItem,editItem ]; } return cell; }Copy the code

Let’s illustrate the reuse of cells when laying them out in several cases.

A cell that is already on the interface

There is obviously no need to rebuild cells that are already in the interface, or even go to the data source to ask for them. Get the corresponding cell directly.

DZTableViewCell* cell = [_visibleCellsMap objectForKey:@(rowIndex)];Copy the code

There is no cell on the interface

For cells that are not in the interface, we need to go to the data:

cell = [_dataSource dzTableView:self cellAtRow:rowIndex];Copy the code

The data source processes the request in accordance with the rules of the share mode described above:

- (DZTableViewCell*) dzTableView:(DZTableView *)tableView cellAtRow:(NSInteger)row { static NSString* const cellIdentifiy = @"detifail"; DZTypeCell* cell = (DZTypeCell*)[tableView dequeueDZTalbeViewCellForIdentifiy:cellIdentifiy]; if (! cell) { cell = [[DZTypeCell alloc] initWithIdentifiy:cellIdentifiy]; } NSString* text = _timeTypes[row]; return cell; }Copy the code

See if there are any reusable cells in the tableView. If there are, use them. If there aren’t, create new ones. But how does a tableView know that it has a cell that can be reused?

DZTableView Is the cache of reusable cells

First let’s look at the function that gets the reused cell:

- (DZTableViewCell*) dequeueDZTalbeViewCellForIdentifiy:(NSString*)identifiy
{
    DZTableViewCell* cell = Nil;
    for (DZTableViewCell* each  in _cacheCells) {
        if ([each.identifiy isEqualToString:identifiy]) {
            cell = each;
            break;
        }
    }
    if (cell) {
        [_cacheCells removeObject:cell];
    }
    return cell;
}Copy the code

Obviously, _cacheCells checks to see if there are any identifiy-specific cells, and if there are, there are reusable cells. This is a direct retrieval process, so there must be a process of putting cells into it.

- (void) layoutNeedDisplayCells { ... [self cleanUnusedCellsWithDispalyRange:displayRange]; . } - (void) cleanUnusedCellsWithDispalyRange:(NSRange)range { NSDictionary* dic = [_visibleCellsMap copy]; NSArray* keys = dic.allKeys; for (NSNumber* rowIndex in keys) { int row = [rowIndex intValue]; if (! NSLocationInRange(row, range)) { DZTableViewCell* cell = [_visibleCellsMap objectForKey:rowIndex]; [_visibleCellsMap removeObjectForKey:rowIndex]; [self enqueueTableViewCell:cell]; }}}Copy the code

When we finish the cell layout, we go back to the interface to clean up the useless cells. At the same time, put these cells into reusable cell containers. Wait for the next time to use, reuse.

DZTableViewCell related

Of course, if only DZTableView unilaterally want to reuse the cell is not possible. We need to do some work with DZTableViewCell to make the share mode work. As we have seen in the above code, we added some attributes to DZTableViewCell:

//DZTableViewCell_private.h
@interface DZTableViewCell ()
@property (nonatomic, strong) NSString* identifiy;
@property (nonatomic, assign) NSInteger index;
@endCopy the code

Identifiy identifies the type of cell. This allows us to reuse the same type of cell. Since there may be many different types of cells on DZTableViewCell, it is not known whether the obtained cell can adapt to a specific type if it is reused without identification.

There is also index information, which is the sequence information of the cell, mainly used for locating the cell.

It is important to note that this definition is defined in a Catogory manner in the dzTableViewcell_private. h file, which is only referenced in dzTableView.mm, so as to avoid the above attributes exposed to the user, the user uses the wrong way caused by the problem. In other words, these are private variables. Must be protected.

We also define and implement a function:

- (void) prepareForReused; . - (void) prepareForReused { _index = NSNotFound; [self setIsSelected:NO]; }Copy the code

Since we want to reuse a Cell, we should clean the Cell before reuse, otherwise we will use it with old data, and you will not know whether the data of the Cell is correct or wrong.

Respond to and handle events

As mentioned earlier, a tableView should be interactive, and the main interaction is to be able to confirm which cell the user clicked on.

- (void) addTapTarget:(id)target selector:(SEL)selecotr { self.userInteractionEnabled = YES; UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:target action:selecotr]; tapGesture.numberOfTapsRequired = 1; tapGesture.numberOfTouchesRequired = 1; [self addGestureRecognizer:tapGesture]; }... [self addTapTarget:self selector:@selector(handleTapGestrue:)]; . - (void) handleTapGestrue:(UITapGestureRecognizer*)tapGestrue { CGPoint point = [tapGestrue locationInView:self]; NSArray* cells = _visibleCellsMap.allValues; for (DZTableViewCell* each in cells) { CGRect rect = each.frame; if (CGRectContainsPoint(rect, point)) { if ([_actionDelegate respondsToSelector:@selector(dzTableView:didTapAtRow:)]) { [_actionDelegate dzTableView:self didTapAtRow:each.index]; } each.isSelected = YES; _selectedIndex = each.index; } else { each.isSelected = NO; }}}Copy the code

We added a standalone event UITapGestureRecognizer to the TableView. And then we dealt with it accordingly. It mainly obtains the user click position, and then finds the cell on the click position. This confirms which cell the user clicked on and sends the message out.

Interface and data acquisition

Through the above elaboration we have DZTableView frame together, to achieve a TableView layout, as well as cell reuse. But there is a crucial question of where the data for the tableView layout information comes from, and what interface we should call outwards to the provider.

Apple seems to have done a pretty good job of that. And DZTableView to do is as far as possible to make the interface and Apple keep consistent, so for the user, there is not too much learning cost.

Data acquisition

@class DZTableView;
@class DZTableViewCell;
@class DZPullDownView;
@protocol DZTableViewSourceDelegate <NSObject>
- (NSInteger) numberOfRowsInDZTableView:(DZTableView*)tableView;
- (DZTableViewCell*) dzTableView:(DZTableView*)tableView cellAtRow:(NSInteger)row;
- (CGFloat) dzTableView:(DZTableView*)tableView cellHeightAtRow:(NSInteger)row;
@endCopy the code

Click and other event responses

@class DZTableView;
@class DZTableViewCell;
@protocol DZTableViewActionDelegate <NSObject>
 
- (void) dzTableView:(DZTableView*)tableView didTapAtRow:(NSInteger)row;
- (void) dzTableView:(DZTableView *)tableView deleteCellAtRow:(NSInteger)row;
- (void) dzTableView:(DZTableView *)tableView editCellDataAtRow:(NSInteger)row;
 
@endCopy the code

DZTableView member method

- (DZTableViewCell*) dequeueDZTalbeViewCellForIdentifiy:(NSString*)identifiy;
- (void) reloadData;
- (void) insertRowAt:(NSSet *)rowsSet withAnimation:(BOOL)animation;
- (void) removeRowAt:(NSInteger)row withAnimation:(BOOL)animation;
 
- (void) manuSelectedRowAt:(NSInteger)row;Copy the code

Extend functionality on DZTableViewCell

Select the state

This should be a basic function of all views, and on many UIView-based Spaces we see functions like setHeightlight or setSelected that give feedback to the user when the space is selected. DZTableViewCell is setSelected. There are two main things about the selected state, one is the selection time, and the other is how to represent the selected state.

Judgment of selected state

When the user touches the cell, it indicates that the cell is selected. When the user leaves the cell, it indicates that the cell is not selected. So we can check the selected state by overloading the response function for a bunch of UIView touch events.

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    [self setIsSelected:YES];
}
 
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self setIsSelected:NO];
}
 
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
    [self setIsSelected:NO];
}Copy the code

Display of selected states

Let’s go back to what we said at the beginning, our whole DZTableView UIView number hierarchy. At the bottom of a Cell is a selectedBackgroudView. And this is just to show you selected states. When the selected state of the Cell changes, we simply rearrange the selectedBackgroudView.

- (void) setIsSelected:(BOOL)isSelected { if (_isSelected ! = isSelected) { _isSelected = isSelected; [self setNeedsLayout]; } } - (void) layoutSubviews { .... if (_isSelected) { _selectedBackgroudView.frame = _contentView.bounds; _selectedBackgroudView.hidden = NO; [_contentView insertSubview:_selectedBackgroudView atIndex:0]; } else { _selectedBackgroudView.hidden = YES; }...Copy the code

Expandability of DZTableView

Since we want to implement a very generic component like UITableView, DZTableView is required to be a little more extensible. This includes:

  1. Property of the configurable type
  2. Functional expansibility, convenient subclass

In order to show this, I have made a right swipe to delete and a drop down to create a new cell. Because the main purpose of this article is to explain IOS UI programming by building a TableView of your own. So I’m not going to go into detail. If you look at the code, you’ll see.



3 comments

About the author:Time flies monohydrate

Personal home page
My article
2