iOS 练习项目 Landmarks (四):添加 AutoLayout 约束
- iOS 练习项目 Landmarks (四):添加 AutoLayout 约束
- 新增 topLabel
- 图片视图圆形裁切+阴影
- 使用 AutoLayout 为详情页的组件添加约束
- DetailViewControllerDelegate
- 为 PlaceCell 添加 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);// 创建并设置 pictureViewself.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 外套一层 shadowViewUIView *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]]];