iOS 练习项目 Landmarks (四):添加 AutoLayout 约束

iOS 练习项目 Landmarks (四):添加 AutoLayout 约束

新增 topLabel

与参考视频对比,返回标签的右边还要有一个显示景点名的标题,在详情页新增一个居中的 UILabel 来显示,命名为 topLabel。

图片视图圆形裁切+阴影

要使得图片展示为一个圆形,先设置一个正方形的 pictureView,再设置它的 layer.cornerRadius 为边长的一半,masksToBounds 是 layer 的属性,含义是子视图是否裁剪图层边界,设置为 YES 后,imageView 超出半径的部分就被裁减掉,这样就剩下一个圆形。

在圆形外设置一个 4px 的白色 border,就完成了边界的设置。

然后设置边界外的阴影,通过设置 layer.shadowOffset、layer.shadowRedius、layer.shadowPath、layer.shadowColor、layer.shadowOpacity 等属性,但是没有效果,因为 masksToBounds = true 会把设置的阴影裁剪掉。一种添加阴影的方法是在 pictureView 外套一层 shadowView,shadowView 的 frame 大小和 pictureView 相同,在 shadowView 上设置阴影效果,再让 picture View 作为 shadowView 的子视图。

    CGRect pictureFrame = CGRectMake(0, 0, 250, 250);
		// 创建并设置 pictureView
    self.pictureView = [[UIImageView alloc] initWithFrame:pictureFrame];
    [self.pictureView setImage:[self.place picture]];
    self.pictureView.layer.cornerRadius = 125.0;
    self.pictureView.layer.borderWidth = 4.0;
    self.pictureView.layer.borderColor = [UIColor whiteColor].CGColor;
    // self.pictureView.contentMode = UIViewContentModeScaleAspectFit;
    self.pictureView.translatesAutoresizingMaskIntoConstraints = NO;
    // 在 pictureView 上直接设置阴影,会因为 masksToBounds = true 而被裁减掉
    self.pictureView.layer.masksToBounds = YES;
    // 在 pictureView 外套一层 shadowView
    UIView *shadowView = [[UIView alloc] initWithFrame:self.pictureView.frame];
    shadowView.layer.shadowColor = [UIColor grayColor].CGColor;
    shadowView.layer.shadowOffset = CGSizeMake(0, 0);
    shadowView.layer.shadowOpacity = 1;
    shadowView.layer.shadowRadius = 9.0;
    shadowView.layer.cornerRadius = 9.0;
    [shadowView addSubview:self.pictureView];
		...
    [self.view addSubview:shadowView];
  • clipToBounds 是 view 的属性,含义是子视图只展示父视图边界内的内容,边界外会被裁减掉,默认为 NO。
  • masksToBounds 是 layer 的属性,含义是子视图是否裁剪图层边界,默认为 NO。

使用 AutoLayout 为详情页的组件添加约束

UIView 有一个属性:translatesAutoresizingMaskIntoConstraints,字面意思是把 autoresizingMask 转换为 Constraints,实际意思是把 frame ,bouds,center 方式布局的视图自动转化为约束形式,此时该视图上约束已经足够,不需要手动去添加别的约束。

  • 用代码创建的所有view , translatesAutoresizingMaskIntoConstraints 默认是 YES
  • 用 IB 创建的所有 view ,translatesAutoresizingMaskIntoConstraints 默认是 NO(autoresize 布局为 YES , autolayout 布局为 NO)。

如果我们要给视图添加自己创建的约束,会和上述约束冲突,所以使用 AutoLayout 前需要将视图的 translatesAutoresizingMaskIntoConstraints 属性设置为 NO。

接下来就是添加约束了:

		/* 添加约束 */
    // topLabel 的上边缘距离 view 的上边缘有 50px,且居中显示
    [NSLayoutConstraint activateConstraints:@[
        [self.topLabel.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:50],
        [self.topLabel.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], // 设置视图位于 X 轴居中
        [self.topLabel.heightAnchor constraintEqualToConstant:50] // 设置视图的高度为 50 点
    ]];
    // mapView 的上边缘位于 topLabel 的下边缘
    NSLayoutConstraint *mapTopAttachToTopButtom = [NSLayoutConstraint constraintWithItem:self.mapView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLabel attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
    [self.view addConstraint:mapTopAttachToTopButtom];
    [NSLayoutConstraint activateConstraints:@[
        [self.mapView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], // 设置地图视图位于 X 轴居中
        // [self.mapView.topAnchor constraintEqualToAnchor:self.topLabel.bottomAnchor],
        // [self.mapView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        // [self.mapView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [self.mapView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
        [self.mapView.heightAnchor constraintEqualToConstant:290] // 设置地图视图的高度为 290px
    ]];
    // pictureView 的中心位于 mapView 的下边缘
    NSLayoutConstraint *pictureCenterYAttachToMapButtom = [NSLayoutConstraint constraintWithItem:self.pictureView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
    [self.view addConstraint:pictureCenterYAttachToMapButtom];
    // pictureView 的宽高为 250px,且 X 轴居中
    [NSLayoutConstraint activateConstraints:@[
        [self.pictureView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], // 设置图片视图位于 X 轴居中
        // [self.pictureView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor], // 设置图片视图位于 Y 轴居中
        [self.pictureView.widthAnchor constraintEqualToConstant:250], // 设置图片视图的宽度为 250px
        [self.pictureView.heightAnchor constraintEqualToConstant:250] // 设置图片视图的高度为 250px
    ]];
    // sightLabel 位于 pictureView 下方 30px 处,距离屏幕左边界 20px
    [NSLayoutConstraint activateConstraints:@[
        [self.sightLabel.topAnchor constraintEqualToAnchor:self.pictureView.bottomAnchor constant:30],
        [self.sightLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [self.sightLabel.heightAnchor constraintEqualToConstant:80]
    ]];
    // starButton 位于 sightLabel 右侧,距离 4px
    [NSLayoutConstraint activateConstraints:@[
        [self.starButton.leadingAnchor constraintEqualToAnchor:self.sightLabel.trailingAnchor constant:4],
        [self.starButton.topAnchor constraintEqualToAnchor:self.sightLabel.topAnchor],
        [self.starButton.bottomAnchor constraintEqualToAnchor:self.sightLabel.bottomAnchor],
        [self.starButton.widthAnchor constraintEqualToConstant:25],
        [self.starButton.heightAnchor constraintEqualToConstant:25]
    ]];
    // scenicAreaLabel 位于 sightLabel 下方,距离 15px
    [NSLayoutConstraint activateConstraints:@[
        [self.scenicAreaLabel.topAnchor constraintEqualToAnchor:self.sightLabel.bottomAnchor constant:15],
        [self.scenicAreaLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
        [self.scenicAreaLabel.heightAnchor constraintEqualToConstant:20]
    ]];
    // stateLabel 与 scenicAreaLabel 水平,其右边界距离屏幕右边界 20px
    [NSLayoutConstraint activateConstraints:@[
        [self.stateLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
        [self.stateLabel.topAnchor constraintEqualToAnchor:self.scenicAreaLabel.topAnchor],
        [self.stateLabel.bottomAnchor constraintEqualToAnchor:self.scenicAreaLabel.bottomAnchor],
        [self.stateLabel.heightAnchor constraintEqualToConstant:20]
    ]];

DetailViewControllerDelegate

详情页的 starButton 也要能修改数据源,所以在 DetailViewController 里声明一个 DetailViewControllerDelegate 协议。

@protocol DetailViewControllerDelegate <NSObject>

@optional

- (void)detailViewController:(DetailViewController *)detailViewController goBackWithFavorite:(BOOL)favorite atIndex:(NSInteger)index;

@end
  
@interface DetailViewController : UIViewController

@property (nonatomic, weak) id <DetailViewControllerDelegate> detailViewControllerDelegate;

...
  
@end

里面有一个可选方法,作用是将 favorite 从详情页传回主页,根据 index 修改数据源对应下标的 Place 对象的 favorite 属性。

在 ViewController 引入这个协议,在 tableView:didSelectRowAtIndexPath: 方法中,我们创建了 DetailViewController 对象,之后要设置代理,别忘了设置 index 属性:

// 设置代理,并且遵守 DetailViewControllerDelegate
detailViewController.detailViewControllerDelegate = self;
// 别忘了设置 index 属性
[detailViewController setIndex:indexPath.row];

并且实现协议的方法:

# pragma mark - DtailViewControllerDelegate Method

- (void)detailViewController:(DetailViewController *)detailViewController goBackWithFavorite:(BOOL)favorite atIndex:(NSInteger)index
{
    // 修改数据源对应的对象
    [self.places[index] setFavorite:favorite];
    // TableView 重新加载被修改了的那一行
    [placeTable reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
    // [self.placeTable reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}

为详情页的starButton的点击事件添加一个目标-动作对,点击按钮,将starButton.selected保存到favorite,再通知代理执行协议方法:

- (void)starButtonClicked:(UIButton *)sender
{
    sender.selected = !sender.selected;
    // [place setFavorite:sender.selected];
    self.favorite = sender.selected;
    // 首先判断代理人是否存在并且是否遵守协议并且实现了协议方法
    if (_detailViewControllerDelegate && [_detailViewControllerDelegate respondsToSelector:@selector(detailViewController:goBackWithFavorite:atIndex:)])
    {
        // 如果满足判断条件,则让代理执行协议方法,此处让代理人执行协议方法,在代理人那个控制器中的协议方法会被执行
        [_detailViewControllerDelegate detailViewController:self goBackWithFavorite:self.favorite atIndex:self.index];
    }
}

为 PlaceCell 添加 AutoLayout 约束

之前提到过:在自己实现的PlaceCell中,目前只有imageView的上面空出了10px,其实下面也要空出距离,实现imageView和contentView的上下都有空位的效果。现在通过 AutoLayout 约束,使得 imageView 的上下分别距离 contentView 5px,就可以实现这个效果,再设置 textLabel 和 starButton 位于 contentView 的 Y 轴居中。

    // 添加约束
    [NSLayoutConstraint activateConstraints:@[
        [self.imageView.widthAnchor constraintEqualToConstant:50],
        [self.imageView.heightAnchor constraintEqualToConstant:50],
        [self.imageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:20],
        [self.imageView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:5],
        [self.imageView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-5],
        
        [self.textLabel.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor],
        [self.textLabel.leadingAnchor constraintEqualToAnchor:self.imageView.trailingAnchor constant:10],
        
        [self.starButton.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor], // 设置图片视图位于 Y 轴居中
        [self.starButton.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-10],
        [self.starButton.widthAnchor constraintEqualToConstant:25],
        [self.starButton.heightAnchor constraintEqualToConstant:25]
    ]];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值