ios 如何在左侧对 UITableViewCell 进行重新排序控制?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11141947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I make reorder control of UITableViewCell in left side?
提问by Hoang Van Ha
I am doing a news reader app and I want to make it so that the user can choose show/hide news categories (such as top news, business, technology, sports, etc) and reorder them like the BBC news app in Android.
我正在做一个新闻阅读器应用程序,我想让它让用户可以选择显示/隐藏新闻类别(例如热门新闻、商业、技术、体育等)并像 Android 中的 BBC 新闻应用程序一样重新排序它们。
See the picture below:
见下图:
My question is:
我的问题是:
- How do I make reorder control in left side of cell? (Its default position is in the right side of the cell in edit mode)
- I have a checkbox custom control. How do I put it in right side of the cell?
- 如何在单元格左侧进行重新排序控制?(它的默认位置在编辑模式下单元格的右侧)
- 我有一个复选框自定义控件。我如何将它放在单元格的右侧?
回答by Luke Dubert
Answer [If you're not using any accessories (detail disclosure, &c)]
回答 [如果您没有使用任何配件(详细披露,&c)]
(0) I assume you have your tables set up, &c.
(0) 我假设你已经设置好了你的桌子,&c。
(1) This solution only works if your tables cells are always draggable. Add this in your viewDidLoad to your Table View Controller .m file.
(1) 此解决方案仅适用于您的表格单元格始终可拖动的情况。将此在您的 viewDidLoad 中添加到您的 Table View Controller .m 文件中。
- (void)viewDidLoad
{
[super viewDidLoad];
[self setEditing:YES];
}
(2) To make it so you can reorder your cells, add cell.showsReorderControl = YES;
to your tableView:cellForRowAtIndexPath:.
(2) 为了使您可以重新排列单元格,请添加cell.showsReorderControl = YES;
到您的 tableView:cellForRowAtIndexPath:。
(3) Ensure that you have the tableView:canMoveRowAtIndexPath: and tableView:moveRowAtIndexPath:toIndexPath: methods.
(3) 确保你有 tableView:canMoveRowAtIndexPath: 和 tableView:moveRowAtIndexPath:toIndexPath: 方法。
- (BOOL)tableView:(UITableView *)tableview canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
(4) Because you want the reorder control on the left, we have to take away the Delete circle usually there and the tableView:editingStyleForRowAtIndexPath: method does that.
(4) 因为你想要左边的重新排序控件,所以我们必须去掉通常在那里的 Delete 圆圈,而 tableView:editingStyleForRowAtIndexPath: 方法就是这样做的。
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
(5) Last step, where the magic happens - add the tableView:willDisplayCell:forRowAtIndexPath: method, search for the cell's subviews, narrow it down to the private UITableViewCellReorderControl, and finally override it.
(5) 最后一步,神奇的地方 - 添加 tableView:willDisplayCell:forRowAtIndexPath: 方法,搜索单元格的子视图,将其缩小到私有 UITableViewCellReorderControl,最后覆盖它。
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
for(UIView* view in cell.subviews)
{
if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"])
{
// Creates a new subview the size of the entire cell
UIView *movedReorderControl = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetMaxX(view.frame), CGRectGetMaxY(view.frame))];
// Adds the reorder control view to our new subview
[movedReorderControl addSubview:view];
// Adds our new subview to the cell
[cell addSubview:movedReorderControl];
// CGStuff to move it to the left
CGSize moveLeft = CGSizeMake(movedReorderControl.frame.size.width - view.frame.size.width, movedReorderControl.frame.size.height - view.frame.size.height);
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, -moveLeft.width, -moveLeft.height);
// Performs the transform
[movedReorderControl setTransform:transform];
}
}
}
I spent nearly ten hours trying to figure out a solution for when accessories and I couldn't do it. I need more experience and knowledge first. Simply doing the same thing to the reorder control on the disclosure button didn't work. So I hope this would works for now; and if I figure it out in the future I'll be sure to update this.
我花了将近十个小时试图找出配件时的解决方案,但我无法做到。我首先需要更多的经验和知识。简单地对披露按钮上的重新排序控件执行相同的操作是行不通的。所以我希望这能暂时奏效;如果我将来弄明白,我一定会更新这个。
Hi Hoang Van Ha, this is my first answer ever, and I only started learning Objective-C a month ago, so I'm still quite a noob.
I've been trying to do the same thing with my app and have been searching for hours how to do it. Apple's documentation isn't very much help when it comes to this. I found it quite frustrating when people would simply link to the documentation. I wanted to yell at them. I hope my answer helped you.
Lots of kudos to b2Cloudbecause I used some of their code from this articleand adapted it for my answers.
嗨,Hoang Van Ha,这是我的第一个答案,一个月前我才开始学习 Objective-C,所以我还是个菜鸟。
我一直在尝试用我的应用程序做同样的事情,并且已经搜索了几个小时如何去做。在这方面,Apple 的文档并没有多大帮助。当人们只是简单地链接到文档时,我发现这很令人沮丧。我想骂他们。希望我的回答对你有帮助。
回答by hpique
Instead of hunting for UITableViewCellReorderControl
, which is non-public API and might change in the future, I recommend using a UILongPressGestureRecognizer
over the UITableView
and implement your own reordering logic.
而不是寻找UITableViewCellReorderControl
,这是非公共 API,将来可能会发生变化,我建议使用UILongPressGestureRecognizer
over theUITableView
并实现您自己的重新排序逻辑。
I solved the general case (responding to a long press on any part of the cell) in HPReorderTableView
, a drop-in replacement of UITableView. It can be easily modified to respond to touches on specific parts of the cell, for example by implementing gestureRecognizer:shouldReceiveTouch:
of the reorderGestureRecognizer
delegate.
我在 中解决了一般情况(响应长按单元格的任何部分)HPReorderTableView
,这是 UITableView 的替代品。它可以通过实现被容易地修改以响应接触对细胞的特定部分,例如gestureRecognizer:shouldReceiveTouch:
所述的reorderGestureRecognizer
委托。
回答by Mohsen Ebrahimzadeh
Now there is a much easier solution for iOS 9 and later
现在有一个更简单的 iOS 9 及更高版本的解决方案
tableView.semanticContentAttribute = .forceRightToLeft
Edit:
编辑:
As Rolandmentioned you should notice that "if you align your cells' subviews via leading
and trailing
anchors instead of left
and right
, this setting will flip everything."
正如Roland提到的,您应该注意到“如果您通过leading
和trailing
锚点而不是left
和来对齐单元格的子视图right
,则此设置将翻转所有内容。”
回答by Max Tymchii
Luke Dubert, have cool answer for iOS 7 it should be changed a little bit to:
Luke Dubert,对 iOS 7 有很酷的回答,应该稍微改成:
UIView *cellSubview = cell.subviews[0];
for(UIView* view in cellSubview.subviews)
{
if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"])
{
回答by xarly
This solution didn't work for me in iOS7 so I have created a version for both, I hope this it usefull:
这个解决方案在 iOS7 中对我不起作用,所以我为两者都创建了一个版本,我希望它有用:
- (void)moveReorderControl:(UITableViewCell *)cell subviewCell:(UIView *)subviewCell
{
if([[[subviewCell class] description] isEqualToString:@"UITableViewCellReorderControl"]) {
static int TRANSLATION_REORDER_CONTROL_Y = -20;
//Code to move the reorder control, you change change it for your code, this works for me
UIView* resizedGripView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetMaxX(subviewCell.frame), CGRectGetMaxY(subviewCell.frame))];
[resizedGripView addSubview:subviewCell];
[cell addSubview:resizedGripView];
// Original transform
const CGAffineTransform transform = CGAffineTransformMakeTranslation(subviewCell.frame.size.width - cell.frame.size.width, TRANSLATION_REORDER_CONTROL_Y);
// Move custom view so the grip's top left aligns with the cell's top left
[resizedGripView setTransform:transform];
}
}
//This method is due to the move cells icons is on right by default, we need to move it.
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView.tag == MEETING_BLOCK_TABLE_TAG) {
for(UIView* subviewCell in cell.subviews)
{
if ([ROCFunctions isIOS7]) {
if([[[subviewCell class] description] isEqualToString:@"UITableViewCellScrollView"]) {
for(UIView* subSubviewCell in subviewCell.subviews) {
[self moveReorderControl:cell subviewCell:subSubviewCell];
}
}
}
else{
[self moveReorderControl:cell subviewCell:subviewCell];
}
}
}
}
回答by Troy
Inspired by Luke's solution, I've found a reliable and straight forward way to make this happen:
受卢克解决方案的启发,我找到了一种可靠且直接的方法来实现这一目标:
@implementation CustomTableViewCell
-(void) layoutSubviews {
[super layoutSubviews];
// set the frames of the other subviews here if necessary, for example:
// self.textLabel.frame = CGRectMake(80, 0, self.textLabel.frame.size.width, self.textLabel.frame.size.height);
for (UIView* view in self.subviews) {
if ([[[view class] description] containsString:@"UITableViewCellReorderControl"]) {
[view setFrame:CGRectMake(20, 0, view.frame.size.width, view.frame.size.height)];
}
}
}
@end
Simply create a custom UITableViewCell, override its subview and modify the position of each view will work nicely in all known scenarios.
只需创建一个自定义 UITableViewCell,覆盖其子视图并修改每个视图的位置即可在所有已知场景中很好地工作。
Performing the UITableViewReorderControl customization under willDisplayCellAtIndexPath doesn't work 100%, the first reorder button won't position correctly occasionally.
在 willDisplayCellAtIndexPath 下执行 UITableViewReorderControl 自定义不能 100% 工作,第一个重新排序按钮偶尔不会正确定位。
回答by meow2x
I tested on iOS 12 using swift 4.2, and instead of using view.self.description == "UITableViewCellReorderControl"
, use view.self.description.contains("UITableViewCellReorderControl")
我使用 swift 4.2 在 iOS 12 上进行了测试,而不是使用view.self.description == "UITableViewCellReorderControl"
,而是使用view.self.description.contains("UITableViewCellReorderControl")
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
for view in cell.subviews {
if view.self.description.contains("UITableViewCellReorderControl") {
let movedReorderControl = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.maxX, height: view.frame.maxY))
movedReorderControl.addSubview(view)
cell.addSubview(movedReorderControl)
let moveLeft = CGSize(width: movedReorderControl.frame.size.width - view.frame.size.width, height: movedReorderControl.frame.size.height - view.frame.size.height)
var transform: CGAffineTransform = .identity
transform = transform.translatedBy(x: -moveLeft.width, y: -moveLeft.height)
movedReorderControl.transform = transform
}
}
}
回答by jishiyu
Thanks everyone above.Here is my solution. have tested on iOS7 and iOS9
谢谢上面的每一个人。这是我的解决方案。已在 iOS7 和 iOS9 上测试
@implementation StoryPreviewCell
-(void)layoutSubviews{
[super layoutSubviews];
//subviews contains first level and second level views.
NSMutableArray *subviews = [NSMutableArray array];
[subviews addObjectsFromArray:self.subviews];
for (NSInteger i=0; i<self.subviews.count; i++) {
UIView *firstLevelView = self.subviews[i];
if(firstLevelView.subviews.count) [subviews addObjectsFromArray:firstLevelView.subviews];
}
for(UIView* view in subviews) {
if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"]) {
UIView *reorderControl = view;
CGFloat leftMoveDistance = 100;//can customize
CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -leftMoveDistance, 0);
reorderControl.transform = transform;
}
}
}
@end
回答by xleon
For those using Xamarin, here it is my solution based on @luke-dubert answer, but taking care of removing the additional added views when reusing:
对于那些使用 Xamarin 的人,这是我基于 @luke-dubert 答案的解决方案,但在重用时要注意删除额外添加的视图:
On TableViewSource:
在 TableViewSource 上:
public override void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
(cell as GraphBarLineCell)?.SetEditionMode(tableView.Editing);
}
On cell:
在单元格上:
private UIView _reorderControlContainer;
public void SetEditionMode(bool editing)
{
_title.Frame = editing ? TitleFrameForEditing : TitleFrame;
if (editing)
{
var reorderControl = Subviews.FirstOrDefault(x => x.Class.Name.Equals("UITableViewCellReorderControl"));
if (reorderControl != null)
{
_reorderControlContainer?.RemoveFromSuperview();
// Creates a new subview the size of the entire cell
_reorderControlContainer = new UIView(new CGRect(0, 0, reorderControl.Frame.GetMaxX(), reorderControl.Frame.GetMaxY()));
// Adds the reorder control view to our new subview
_reorderControlContainer.AddSubview(reorderControl);
// Adds our new subview to the cell
AddSubview(_reorderControlContainer);
// CGStuff to move it to the left
var moveLeft = new CGSize(_reorderControlContainer.Frame.Size.Width - reorderControl.Frame.Size.Width,
_reorderControlContainer.Frame.Size.Height - reorderControl.Frame.Size.Height);
var transform = CGAffineTransform.MakeIdentity();
transform = CGAffineTransform.Translate(transform, -moveLeft.Width, -moveLeft.Height);
_reorderControlContainer.Transform = transform;
// Align the icon with the title
var icon = reorderControl.Subviews[0];
icon.Frame = new CGRect(10, 25, icon.Frame.Width, icon.Frame.Height);
}
}
}
回答by karstux
I've found a far easier solution that requires no absolute manual positioning of anything, and even works with animated transitions from/to editing mode. Should work for both iOS6 and 7.
我找到了一个更简单的解决方案,它不需要对任何东西进行绝对的手动定位,甚至可以使用从/到编辑模式的动画过渡。应该适用于 iOS6 和 7。
Define this category on UIView somewhere:
在 UIView 某处定义此类别:
@interface UIView (ClassSearch)
- (instancetype)subviewOfClassMatching:(NSString*)partialName;
@end
@implementation UIView (ClassSearch)
-(instancetype)subviewOfClassMatching:(NSString *)partialName
{
if ([[[self class] description] rangeOfString:partialName options:NSCaseInsensitiveSearch].location != NSNotFound) {
return self;
}
for (UIView* v in self.subviews) {
id match = [v subviewOfClassMatching:partialName];
if (match) {
return match;
}
}
return nil;
}
@end
Then, in your UITableViewCell subclass, override willTransitionToState like so:
然后,在您的 UITableViewCell 子类中,像这样覆盖 willTransitionToState:
-(void)willTransitionToState:(UITableViewCellStateMask)state
{
[super willTransitionToState:state];
if (state == UITableViewCellStateShowingEditControlMask) {
UIView* reorderControl = [self subviewOfClassMatching:@"ReorderControl"];
UIView* reorderContainer = [[UIView alloc] initWithFrame:self.bounds];
CGAffineTransform t = CGAffineTransformMakeScale(-1, 1);
reorderContainer.transform = t;
[self addSubview:reorderContainer];
[reorderContainer addSubview:reorderControl];
}
}
Note that the cell needs to be configured to show only the reorder control, otherwise more work needs to be done.
请注意,单元格需要配置为仅显示重新排序控件,否则需要做更多的工作。