浅谈iOS|浅谈iOS 11.0中UITableView 都更改了什么( (二))

版本记录

版本号 时间
V1.0 2018.01.25
前言
2017年iOS版本更新到了11.0的系统,新机器比如iPhone X都是预装11.0的系统,而我们的UIKit框架中的UITableView类都做了哪些更改?接下来我们就看一下iOS11.0中UITableView类的改变,共22处新增,改动的还是很大的,下面我们就详细的看一下。感兴趣的可以看上面几篇。
1. 浅谈iOS 11.0中UITableView 都更改了什么? (一)
方法 - (void)performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion 允许多个插入/删除/重新加载/移动调用同时进行动画,可嵌套。
先看一下这两个参数:
  • updates:执行相关插入,删除,重新加载或移动操作的块。
  • completion:所有操作完成后执行完成处理程序块。 该块没有返回值,并采用以下参数:
    • finished:一个布尔值,指示动画是否成功完成。 如果动画因任何原因中断,则此参数的值为NO。
如果要在一个动画操作中对tableview进行多个更改,而不是多个单独的动画,请使用此方法。 使用updates参数中传递来的block指定您要执行的所有操作。
在批量操作中插入之前处理删除。 这意味着在批处理操作之前,相对于tableview状态的索引来处理删除的索引,并且在批操作中的所有删除之后相对于状态的索引来处理插入的索引。
属性 @property (nonatomic, readonly) BOOL hasUncommittedUpdates 一个布尔值,指示tableview是否包含放置占位符还是将其行作为处理放置的一部分进行重新排序,先看一下API。
// Returns YES if the table view is in the middle of reordering, is displaying a drop target gap, or has drop placeholders. If possible, avoid calling -reloadData while there are uncommitted updates to avoid interfering with user-initiated interactions that have not yet completed.如果tableview处于重新排序的中间位置,显示放置目标间隔drop target gap或放置占位符drop placeholders,则返回YES。 如果可能的话,避免在未提交更新时调用-reloadData,以避免干扰尚未完成的用户启动的交互@property (nonatomic, readonly) BOOL hasUncommittedUpdates API_AVAILABLE(ios(11.0), tvos(11.0));

当此属性的值为YES时,请避免对tableview进行任何重大更改。 具体来说,不要重新加载tableview的数据,因为这样做会删除所有占位符并从数据源重新创建行。
属性 @property (nonatomic) BOOL insetsContentViewsToSafeArea 默认值是YES。这可以保证:我们可以看清楚表格里边的内容,而不会被下边的指示条遮挡。其实他就是增加了自己contentView的size实现的。
看一下设置为YES的效果图
浅谈iOS|浅谈iOS 11.0中UITableView 都更改了什么( (二))
文章图片
上边安全区域 浅谈iOS|浅谈iOS 11.0中UITableView 都更改了什么( (二))
文章图片
下边安全区域 属性 @property (nonatomic) BOOL dragInteractionEnabled 一个布尔值,指示tableview是否支持应用程序之间的拖放,我们先看一下API。
// To enable intra-app drags on iPhone, set this to YES. // You can also force drags to be disabled for this table view by setting this to NO. // By default, this will return YES on iPad and NO on iPhone.要在iPhone上启用应用程序内拖动,将其设置为YES。您也可以通过将此tableview设置为NO禁用拖动。默认情况下,这将在iPad上返回YES,在iPhone上返回NO。@property (nonatomic) BOOL dragInteractionEnabled API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

在iPhone上将值更改为true可以将内容从tableview拖动到iPhone上的另一个app,并从其他app接收内容。
属性 @property (nonatomic, readonly) BOOL hasActiveDrag 一个布尔值,指示行是否从tableview中提起并且尚未被放下,先看一下API。
// YES if a drag session is currently active. A drag session begins after rows are "lifted" from the table view.如果drag session当前是active状态,在行 从tableview "lifted” 之后,拖动会话开始。@property (nonatomic, readonly) BOOL hasActiveDrag API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

属性 @property (nonatomic, readonly) BOOL hasActiveDrop 先看一下API
// YES if table view is currently tracking a drop session.如果tableview正在tracking一个drop session值就为YES。@property (nonatomic, readonly) BOOL hasActiveDrop API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

枚举 UITableViewDropIntent 我们先看一下这个枚举值API
typedef NS_ENUM(NSInteger, UITableViewDropIntent) { // Table view will accept the drop, but the location is not yet known and will be determined later. // Will not open a gap. You may wish to provide some visual treatment to communicate this to the user. // tableview将接受drop,但位置尚不知道,稍后将确定。 不会打开一个gap。 您可能希望提供一些视觉处理以将其传达给用户。 UITableViewDropIntentUnspecified,// The drop will be placed in row(s) inserted at the destination index path. // Opens a gap at the specified location simulating the final dropped layout. // drop将放置在目标索引路径中插入的行中。 在指定的位置打开一个gap,模拟最终放置的布局。 UITableViewDropIntentInsertAtDestinationIndexPath,// The drop will be placed inside the row at the destination index path (e.g. the row is a container of other items). // Will not open a gap. Table view will highlight the row at the destination index path. // drop将被放置在目标索引路径的行内(例如,该行是其他项目的容器)。将不打开gap。 tableview将highlight目标索引路径处的行。 UITableViewDropIntentInsertIntoDestinationIndexPath,// The table view will automatically choose between .insertAtDestinationIndexPath and // .insertIntoDestinationIndexPath depending on the position of the drop. This should be used instead // of .insertIntoDestinationIndexPath when the item being dropped can either be placed inside the row // at the destination index path or inserted in a new row at the index path of the container row. // tableview将自动在.insertAtDestinationIndexPath和.insertIntoDestinationIndexPath之间根据drop的位置进行选择。 当被drop的项目可以放置在目标索引路径的行内或插入到容器行的索引路径的新行中时,应该使用这个枚举值而不是使用.insertIntoDestinationIndexPath。 UITableViewDropIntentAutomatic } API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

也可以这么理解
  • UITableViewDropIntentUnspecified
    • 未指定的drop proposal。
  • UITableViewDropIntentInsertAtDestinationIndexPath
    • 在指定的indePath中插入drop的内容
  • UITableViewDropIntentInsertIntoDestinationIndexPath
    • 在指定的indePath中合并drop的内容
  • UITableViewDropIntentAutomatic
    • 根据放置位置以适当的方式合并内容。
类 UITableViewDropProposal 这个是iOS 11.0新增加的类,下面先看一下API
UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos) @interface UITableViewDropProposal : UIDropProposal- (instancetype)initWithDropOperation:(UIDropOperation)operation intent:(UITableViewDropIntent)intent; // The default is UITableViewDropIntentUnspecified. @property (nonatomic, readonly) UITableViewDropIntent intent; @end

这里就一个实例化方法,UITableViewDropIntent枚举在上面已经进行了说明,下面我们看一下这个UIDropOperation枚举,在这个UIDropInteraction.h头文件中。
// //UIDropInteraction.h //UIKit // //Copyright ? 2017 Apple Inc. All rights reserved. //#import NS_ASSUME_NONNULL_BEGIN@protocol UIDragAnimating, UIDropInteractionDelegate, UIDropSession; @class UIDragItem, UITargetedDragPreview; UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos) @interface UIDropInteraction : NSObject - (instancetype)initWithDelegate:(id)delegate NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @property (nonatomic, nullable, readonly, weak) id delegate; /* Whether to allow this interaction to handle more than one drop session * at the same time. A well-behaved delegate can handle this correctly, * but many will find it challenging, so the default value is false. * If false, then after one session has entered the view, other sessions will * be ignored until the first session exits and the other session enters again. */ @property (nonatomic, assign) BOOL allowsSimultaneousDropSessions; @end/* The UIDropInteraction's delegate uses a UIDropOperation to tell the system * what operation it will perform if the user drops. UIDropInteraction代理使用一个UIDropOperation告诉系统,如果用户drop要执行什么样的操作。 */ typedef NS_ENUM(NSUInteger, UIDropOperation) { /* Operations that do NOT cause data transfer. 不会引起数据转移的操作 * * If the user drops here, the UIDropInteraction's -dropInteraction:performDrop: * method will NOT be called. 如果用户在这里drop,UIDropInteraction的-dropInteraction:performDrop:方法不会被调用 *//* The drag will be cancelled. drag将被取消。 */ UIDropOperationCancel= 0,/* The drag will be cancelled. * Use this operation to signal that this interaction would *normally* perform * a different operation, but that it explicitly forbids a drop at this specific * time and place. * This may cause a special symbol to be displayed on the drag image.drag将被取消。 使用这个操作说明交互将normally执行一个不同的操作,但是它显式的禁止在指定的时间和控件内进行drop。 在拖拽图像时,这可能引起要显示一个特殊的符号。 */ UIDropOperationForbidden = 1,/* Operations that cause data transfer. 引起数据移动的操作。 * * If the user drops here, the UIDropInteraction's -dropInteraction:performDrop: * method will be called. 如果用户在这里使用drop,UIDropInteraction的dropInteraction:performDrop:方法会被调用。 *//* The data represented by the drag item(s) will be copied. * This is the most common operation to use. 拖动的item代表的数据将会被copy,这个是使用时最普遍的操作。 */ UIDropOperationCopy= 2,/* The data represented by the drag item(s) will be moved. 拖动items代表的数据被移动。 * * You may use this only if UIDropSession's allowsMoveOperation is true. * Otherwise it will be treated as UIDropOperationCancel. 如果UIDropSession的allowsMoveOperation是true,你可能使用这个值,否则,可能被当做UIDropOperationCancel对待。 * * Note that the system does not give any special meaning to this operation. * The delegates of both the UIDragInteraction and UIDropInteraction must * cooperate to produce the correct result. For instance, the UIDropInteraction's * delegate might insert the data in the new location, and the UIDragInteraction's * delegate might remove the data from the old location. 注意系统并不会给这个操作任何特殊的意义,UIDragInteraction和UIDropInteraction代理必须合作 产生正确的结果,例如,UIDropInteraction的代理可以在新的行插入数据,UIDragInteraction的代理可以从 旧的区域移除数据。 */ UIDropOperationMove= 3, } API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos); UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos) @interface UIDropProposal : NSObject - (instancetype)initWithDropOperation:(UIDropOperation)operation NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; /* The operation that this interaction proposes to perform. */ @property (nonatomic, readonly) UIDropOperation operation; /* Set `precise` to true to specify that this drop interaction wants to handle the drop * in a precise way, e.g. dropping into text. The drag system may move the drag point * away from the touch in order to make it easier to hit a small target. */ @property (nonatomic, getter=isPrecise) BOOL precise; /* Signals that this drop interaction wants the preview to be shown * at its full original size, not scaled smaller, while this proposal is in effect. * For instance, you might set this to true if the items are being moved from some * other nearby view, and scaling them smaller would be distracting. * This only applies to drags that started in the same app. */ @property (nonatomic) BOOL prefersFullSizePreview; @endAPI_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos) @protocol UIDropInteractionDelegate @optional/* Return whether the delegate is interested in the given session. * If this method is not implemented, or if it returns true, then the * other delegate methods will be called, starting with `-dropInteraction:sessionDidEnter:`. * If it returns false, this interaction will ignore this session. (We may ask again * if something significant about the session changes, like the items in it.) * * Note that returning true does not mean that the delegate definitely *will* accept this drop, * just that it *might* want to accept the drop. * To accept it, you must also implement `-dropInteraction:sessionDidUpdate:` and return a * UIDropProposal with an appropriate operation. * * This method is commonly used to check whether the session contains any * items that this delegate can consume. Use `UIDropSession`'s convenience methods * `-hasItemsConformingToTypeIdentifiers:`, `-canLoadObjectsOfClass:`, etc. */ - (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id)session; /* Called when a drag enters the view. */ - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnter:(id)session; /* Called when the drag enters the interaction's view, or when when the drag * moves while inside the view, or if items are added to the drag while inside the view. * * You *must* implement this method to accept a drop. * Return a `UIDropProposal` that contains the operation that the delegate * would like to perform. (Note that you may use `UIDropOperationMove` only if * session.allowsMoveOperation is true.) * * You may also set other properties on the `UIDropProposal` to change the appearance * of the drag over this view. * * Use the session's `-locationInView:` to get a point to use for hit testing. */ - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id)session; /* Called when the drag has exited the interaction's view. */ - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidExit:(id)session; /* Called when the user drops onto this interaction's view. * The delegate should now request data from the session's * items' item providers. Data may be requested *only* during * the scope of this method. */ - (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id)session; /* Called when the user has dropped onto this interaction's view, * after `-dropInteraction:performDrop:` has been called, * and all resulting drop animations have completed. * * The delegate should cause the interaction's view to draw in its final post-drop state. */ - (void)dropInteraction:(UIDropInteraction *)interaction concludeDrop:(id)session; /* When the drag session ends, for any reason, this method will be called * for *every* interaction that ever received `-dropInteraction:sessionDidEnter:`, * `-dropInteraction:sessionDidUpdate:`, or `-dropInteraction:sessionDidExit:`. * * If you were keeping track of the session for any reason, you may now forget it. */ - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnd:(id)session; /* Drop animation. *//* Called when the drop happens, after `-dropInteraction:performDrop:`, once for each visible item. * Provide a preview to animate the item to where it belongs. * We provide `defaultPreview` which would leave the current preview where the item was dropped. * You may return: * - defaultPreview, to use it as-is * - nil, to fade and shrink the drag item in place * - [defaultPreview retargetedPreviewWithTarget:] to move the preview to a different target * - a UITargetedDragPreview that you create however you like */ - (nullable UITargetedDragPreview *)dropInteraction:(UIDropInteraction *)interaction previewForDroppingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultPreview; /* Called when the drop animation is about to start, once for each item, * whether it is visible or not. * Use the animator to animate your own changes alongside the system animation. */ - (void)dropInteraction:(UIDropInteraction *)interaction item:(UIDragItem *)item willAnimateDropWithAnimator:(id)animator; @endNS_ASSUME_NONNULL_END

从上面可以看见,这个是新增的,说明和tableview相关的东西改动还是很大的,这里就对UIDropOperation枚举进行了翻译。
协议 UITableViewDropCoordinator 用于协调您与tableview的自定义拖放相关操作的接口,下面我们看一下这个协议的API。
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos) @protocol UITableViewDropCoordinator // Ordered list of items available for this drop. @property (nonatomic, readonly) NSArray> *items; // The last hit-tested index path known during the drop session. // When the drop is at the end of a section, this index path will be for a row that does not yet exist (equal // to the number of rows in that section), where an inserted row would append to the end of the section. // This index path may be nil in some circumstances (e.g. when dragging over empty space where there are no cells), // and if it is nil, the proposal's intent will always be UITableViewDropIntentUnspecified. @property (nonatomic, readonly, nullable) NSIndexPath *destinationIndexPath; // The current drop proposal at the time of the drop. @property (nonatomic, readonly) UITableViewDropProposal *proposal; // The drop session. @property (nonatomic, readonly) id session; // Animate the dragItem to an automatically inserted placeholder row. // Once the dragItem data is available, you can exchange the temporary placeholder cell with the final cell using the placeholder context // method -commitInsertionWithDataSourceUpdates: - (id)dropItem:(UIDragItem *)dragItem toPlaceholder:(UITableViewDropPlaceholder *)placeholder; // Animate the dragItem to a row that you inserted at this index path. // You must call -performBatchUpdates:completion: to update your data source and insert a new row into the table view prior to calling this method. // If desired, use the drop delegate method -tableView:dropPreviewParametersForRowAtIndexPath: to provide preview parameters. - (id)dropItem:(UIDragItem *)dragItem toRowAtIndexPath:(NSIndexPath *)indexPath; // Animate the dragItem to a rect inside an existing row. // The rect is in the coordinate space of the cell at this index path. // The item will be animated with an aspect fit scale transform to fit inside the rect. Use a rect with zero size to shrink the item to a single point. - (id)dropItem:(UIDragItem *)dragItem intoRowAtIndexPath:(NSIndexPath *)indexPath rect:(CGRect)rect; // Animate the dragItem to a location specified by the UIDragPreviewTarget. // The -[UITableViewDropItem previewSize] may be helpful to compute an appropriate transform. - (id)dropItem:(UIDragItem *)dragItem toTarget:(UIDragPreviewTarget *)target; @end

你不要自己创建这个类的实例。 在tableview中发生拖放时,UIKit将创建此类的一个实例,并将其传递给您的 tableView:performDropWithCoordinator:方法。 使用该对象让tableview知道将item drop到某个位置如何进行动画。
Topics 1. Getting the Dragged Items
  • items
    • 要被drag的item,Required
2. Getting the Drop Location
  • destinationIndexPath
    • 在指定的indexPath插入item到tableview,Required
3. Animating Rows to Their Destination
  • dropItem:toRowAtIndexPath:
    • 将item添加的指定tableview的indexPath进行动画,Required
  • dropItem:intoRowAtIndexPath:rect:
    • Required
  • dropItem:toTarget:
    • 将item动画到视图层次结构中的任意位置,Required
4. Getting the Session Information
  • session
    • 包含有关事务信息的drop会话,Required
  • proposal
    • 关于如何合并dropped items的建议,Required
5. Instance Methods
  • dropItem:toPlaceholder:
    • Required
参考文章 1. Get Your Apps Ready for iPhone X
2. iOS 11安全区域对UIScrollview和UITableView的影响
后记
【浅谈iOS|浅谈iOS 11.0中UITableView 都更改了什么( (二))】本篇已结束,后面更精彩~~~
浅谈iOS|浅谈iOS 11.0中UITableView 都更改了什么( (二))
文章图片

    推荐阅读