【问题标题】:iOS map view in table view cell wrong frame origin表格视图单元格中的iOS地图视图错误的框架原点
【发布时间】:2014-03-24 17:38:50
【问题描述】:

我需要在表格视图单元格中设置地图视图的位置。我只是在 cellForRow 方法中设置了框架。

一些地图视图放错了位置。当我向上滚动表格并向下滚动以使其重新出现(以重用表格单元格)时,它就被修复了。

注意

  1. y 位置错误但 x 位置正确。

  2. 我使用相同的方式(简单地设置框架)来设置图片位置,它们总是正确的。所以问题是地图视图本身而不是如何定位框架。

以下截图显示了 3 个地图视图,中间的 y 位置错误

编辑:

UI 部分相当复杂。在 cellForRowAtIndexPath 方法中,我将 MessageCell 出列并调用其 setupWithMessage:(Message *)message 方法,然后检查消息是否为位置类型:(位置类型消息具有可选的文本视图和地图视图)

        self.textView.hidden = NO;
        self.mapView.hidden = NO;

        self.textView.text = message.text;


        [Helper setupMapView:self.mapView posx:message.posx posy:message.posy];

        CGSize size1 = [UIHelper sizeWithText:message.text entity:message];
        CGSize size2 = [UIHelper sizeWithMapEntity:message];

        CGRect frame = [UIHelper adjustTextView:self.textView textViewSize:size1 extraView:self.mapView extraViewSize:size2 entity:message];








+ (CGRect)adjustTextView:(UITextView *)textView textViewSize:(CGSize)textViewSize extraView:(UIView *)extraView extraViewSize:(CGSize)extraViewSize entity:(id)entity {


    if (textView == nil || extraView == nil || CGSizeEqualToSize(textViewSize, CGSizeZero) || CGSizeEqualToSize(extraViewSize, CGSizeZero)) {
        CGSize targetSize;
        UIView *targetView;

        if (textView == nil || CGSizeEqualToSize(textViewSize, CGSizeZero)) {

            targetView = extraView;
            targetSize = extraViewSize;

        }
        else {
            targetView = textView;
            targetSize = textViewSize;
        }

        return [self adjustContentView:targetView size:targetSize entity:entity];

    }

    else {

        CGRect frame1 = [self adjustContentView:textView size:textViewSize entity:entity];
        CGRect frame2 = [self adjustContentView:extraView size:extraViewSize entity:entity];

        frame2.origin.y += frame1.size.height + OFFSET_BETWEEN_TEXTVIEW_EXTRAVIEW;
        extraView.frame = frame2;

        return CGRectMake(frame1.origin.x, frame1.origin.y, MAX(frame1.size.width, frame2.size.width), frame1.size.height + frame2.size.height + OFFSET_BETWEEN_TEXTVIEW_EXTRAVIEW);
    }

请注意,上面的“extraView”可以指代文本视图下方的任何视图,例如 UIImageView

+ (CGRect)adjustContentView:(UIView *)contentView size:(CGSize) size entity:(id)entity {

    float photoSideOffset = 60;
    float topOffset = 30;

    CGRect frame;
    frame.origin.y = topOffset;
    frame.size = size;

    if ([DBHelper isMyEntity:entity]) {
        frame.origin.x = 320 - size.width - photoSideOffset;
    }
    else {
        frame.origin.x = photoSideOffset;
    }
    contentView.frame = frame;


    if ([contentView isKindOfClass:[UITextView class]]) {
        [(UITextView *)contentView sizeToFit];
    }

    return contentView.frame;
}

编辑 2:我对照片消息使用相同的 UIHelper 方法,并且坐标是正确的:

编辑 3:size1 size2 和 frame 的值

【问题讨论】:

  • 你能把代码贴在你设置框架的地方吗?
  • 您使用的是哪种 UITableViewCell?通常对于这样的事情,我会在情节提要上创建一个自定义单元格,创建 UItableViewCell 的子类并将所有内容链接在一起。例如,我为我工作以插入 PickerViews。
  • 这是一个简单的视图复用问题。为了给你一个提示,请查看第一个单元格的可见部分和中间单元格的地图位置。如果您通过故事板或自定义视图使用它,请发布您的代码并告诉我们。
  • 您的自动布局和自动调整大小蒙版的使用可能也有一些事情要做。你用的是哪一个?
  • @VinnyCoyne 我发布了代码

标签: ios iphone cocoa-touch uikit


【解决方案1】:

方法一: alloc init UITableViewCell 在你的cellForRowAtIndexPath

简单的解决方法是每次cell = [[UITableViewCell alloc]init];alloc init (它每次都会创建新的单元格而不是缓存/或重用)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

        // Configure the cell...

       /*
        if (cell == nil) {

            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];

        }

       */
      // Try below code

      cell = [[UITableViewCell alloc]init];


        ------

        -------

        return cell;

    }

方法 2: removeFromSuperView

  for (UIView *subview in self.view.subviews) {

        if ([subview isKindOfClass:[UIButton class]]) { // set kind as per your own requirement

            [subview removeFromSuperview];
        }


    }

【讨论】:

  • 使用不可重复使用的单元格不是合适的方法,因为它是一个聊天应用程序,因此可能有很多单元格,每个单元格可能包含富媒体,在分配和释放时很慢
  • @OMGPOP - 你用仪器测量过性能吗 - 我已经在我的应用程序中测试过 - 我没有性能问题 --- 即使它仍然存在,那么方法 2 需要在该上下文中应用
  • 不工作。如果我不重复使用单元格,所有地图坐标都会错位。
【解决方案2】:

我提出的解决方法并不是真正的“解决方案”而是“替代方案”

您可以从地图视图创建快照,而不是将 mkmapview 放到单元格中。然后您可以为地图单元格创建图像单元格。我的建议是当用户单击图像单元格并推送新的“MapViewController”模式时触发一个动作。

        self.textView.hidden = NO;
        self.thumbnailButton.hidden = NO;

        self.textView.text = message.text;


        [Helper setupMapSnapshotThumbnailButton:self.thumbnailButton withPosx:message.posx posy:message.posy size:MESSAGE_SIZE_MAP];

        CGSize size1 = [UIHelper sizeWithText:message.text entity:message];
        CGSize size2 = MESSAGE_SIZE_MAP;

        CGRect frame = [UIHelper adjustTextView:self.textView textViewSize:size1 extraView:self.thumbnailButton extraViewSize:size2 entity:message];

        [self setupCommonOutletsWithContentFrame:frame];

您可以使用创建图像

MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.scale = [UIScreen mainScreen].scale;
options.region = [self private_mapRegionWithPosx:posx posy:posy];
options.size = size;

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
    [thumbnailButton setBackgroundImage:snapshot.image forState:UIControlStateNormal];
}];

顺便说一句,快照代码仅适用于 ios 7。如果您需要 ios6 或更早版本的支持,则需要进行一些 cg 编码

【讨论】:

    【解决方案3】:

    代码示例

    MultiMap.h

    #import <UIKit/UIKit.h>
    #import <MapKit/MapKit.h>
    
    @interface MultiMap : UIViewController
    <UITableViewDelegate,UITableViewDataSource,MKMapViewDelegate>
    {
        MKMapView *mapView;
    }
    @property (retain, nonatomic) IBOutlet UITableView *mainTableView;
    
    @end
    

    MultiMap.m

    #import "MultiMap.h"
    
    @interface MultiMap ()
    
    @end
    
    @implementation MultiMap
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // Custom initialization
        }
        return self;
    }
    
    - (void)viewDidAppear:(BOOL)animated{
        [super viewDidAppear:YES];
        self.title=@"Multi Map";
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
    }
    
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
        return nil;
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 200;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return 4;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"multi";
        UITableViewCell *cell=nil;
    
        cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }
        mapView=[[MKMapView alloc]initWithFrame:CGRectMake(25, 25, 275, 150)];
        mapView.delegate=self;
        [mapView setBackgroundColor:[UIColor whiteColor]];
        [mapView setTintColor:[UIColor whiteColor]];
    
        [cell addSubview:mapView];
    
        return cell;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    }
    
    - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
    {
    
    }
    
    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
    {
        MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@""];
    
        annotationView.canShowCallout = YES;
    
        return annotationView;
    }
    
    - (void)dealloc {
        [_mainTableView release];
        [super dealloc];
    }
    @end
    

    你能得到帮助吗..

    【讨论】:

    • 我不这么认为。 @OMGPOP
    • 您只需创建一个地图视图并在 cellForRow 中设置框架。我也在 cellForRow 中调用 setupWithMessage。
    • 谢谢,但你需要先检查一下.. bcz 我在地图上没有遇到任何问题.. @OMGPOP
    【解决方案4】:

    请确保您的 textView 框架为 0。对于 mapView 未在准确位置显示的情况,您可以尝试关注。

    + (CGRect)adjustTextView:(UITextView *)textView textViewSize:(CGSize)textViewSize extraView:(UIView *)extraView extraViewSize:(CGSize)extraViewSize entity:(id)entity {
    
    
        if (textView == nil || extraView == nil || CGSizeEqualToSize(textViewSize, CGSizeZero) || CGSizeEqualToSize(extraViewSize, CGSizeZero)) {
            CGSize targetSize;
            UIView *targetView;
    
            if (textView == nil || CGSizeEqualToSize(textViewSize, CGSizeZero)) {
    
                targetView = extraView;
                targetSize = extraViewSize;
    
            }
            else {
                targetView = textView;
                targetSize = textViewSize;
            }
    
            return [self adjustContentView:targetView size:targetSize entity:entity];
    
        }
    
        else {
    
            CGRect frame1 = [self adjustContentView:textView size:textViewSize entity:entity];
            CGRect frame2 = [self adjustContentView:extraView size:extraViewSize entity:entity];
    
            frame2.origin.y += frame1.size.height + OFFSET_BETWEEN_TEXTVIEW_EXTRAVIEW;
            extraView.frame = frame2;
            [[extraView superview] setNeedsLayout]; //Addition
            return CGRectMake(frame1.origin.x, frame1.origin.y, MAX(frame1.size.width, frame2.size.width), frame1.size.height + frame2.size.height + OFFSET_BETWEEN_TEXTVIEW_EXTRAVIEW);
        }
    

    更新:或者您可以更新以下内容

    + (CGRect)adjustContentView:(UIView *)contentView size:(CGSize) size entity:(id)entity {
    
    float photoSideOffset = 60;
    float topOffset = 30;
    
    CGRect frame;
    frame.size = size;
    
    if ([DBHelper isMyEntity:entity]) {
        frame.origin.x = 320 - size.width - photoSideOffset;
    }
    else
    {
        frame.origin.x = photoSideOffset;
    }
    
    for (id view in [[contentView superview] subviews])
    {
        if (view != contentView)
        {
            if ([view respondsToSelector:@selector(frame)])
            {
                topOffset = (([view frame].origin.y + [view frame].size.height)<topOffset)?([view frame].origin.y + [view frame].size.height):topOffset;
            }
        }
    }
    if ((int)topOffset != 30)
    {
        topOffset += OFFSET_BETWEEN_TEXTVIEW_EXTRAVIEW;
    }
    frame.origin.y = topOffset;
    contentView.frame = frame;
    
    
    if ([contentView isKindOfClass:[UITextView class]]) {
        [(UITextView *)contentView sizeToFit];
    }
    
    return contentView.frame;
    }
    

    还要确保使用- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

    相应地更新单元格的高度

    【讨论】:

    • 两者都不起作用。请注意,图像消息(正确)是按钮图像。有帮助吗?
    • 你能把你从CGRect frame1 = [self adjustContentView:textView size:textViewSize entity:entity]; CGRect frame2 = [self adjustContentView:extraView size:extraViewSize entity:entity];得到的帧贴出来
    • 我在问题中添加了调试器截图
    • 对于所有索引路径,框架保持不变对吗?
    • 是的,所有单元格都有相同的框架(包括正确的和不正确的)
    猜你喜欢
    • 2012-12-03
    • 1970-01-01
    • 2016-09-13
    • 2015-09-30
    • 2015-03-07
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多