diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index f0d49cc5c..6967fd679 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -504,14 +504,16 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations UIImage *avatarImage = (localProfileAvatarImage ?: [[[OWSContactAvatarBuilder alloc] initForLocalUserWithDiameter:kAvatarSize] buildDefaultImage]); OWSAssertDebug(avatarImage); - AvatarImageView *avatarView = [[AvatarImageView alloc] initWithImage:avatarImage]; - [avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize]; - [avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize]; - avatarView.userInteractionEnabled = YES; - [avatarView - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(settingsButtonPressed:)]]; - settingsButton = [[UIBarButtonItem alloc] initWithCustomView:avatarView]; + + UIButton *avatarButton = [AvatarImageButton buttonWithType:UIButtonTypeCustom]; + [avatarButton addTarget:self + action:@selector(settingsButtonPressed:) + forControlEvents:UIControlEventTouchUpInside]; + [avatarButton setImage:avatarImage forState:UIControlStateNormal]; + [avatarButton autoSetDimension:ALDimensionWidth toSize:kAvatarSize]; + [avatarButton autoSetDimension:ALDimensionHeight toSize:kAvatarSize]; + + settingsButton = [[UIBarButtonItem alloc] initWithCustomView:avatarButton]; } else { // iOS 9 and 10 have a bug around layout of custom views in UIBarButtonItem, // so we just use a simple icon. diff --git a/SignalMessaging/Views/AvatarImageView.swift b/SignalMessaging/Views/AvatarImageView.swift index 62dd476c8..d4d4eb248 100644 --- a/SignalMessaging/Views/AvatarImageView.swift +++ b/SignalMessaging/Views/AvatarImageView.swift @@ -178,3 +178,59 @@ public class ConversationAvatarImageView: AvatarImageView { self.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: diameter) } } + +@objc +public class AvatarImageButton: UIButton { + private let shadowLayer = CAShapeLayer() + + // MARK: - Button Overrides + + override public func layoutSubviews() { + super.layoutSubviews() + + layer.cornerRadius = frame.size.width / 2 + + // Inner shadow. + // This should usually not be visible; it is used to distinguish + // profile pics from the background if they are similar. + shadowLayer.frame = bounds + shadowLayer.masksToBounds = true + let shadowBounds = bounds + let shadowPath = UIBezierPath(ovalIn: shadowBounds) + // This can be any value large enough to cast a sufficiently large shadow. + let shadowInset: CGFloat = -3 + shadowPath.append(UIBezierPath(rect: shadowBounds.insetBy(dx: shadowInset, dy: shadowInset))) + // This can be any color since the fill should be clipped. + shadowLayer.fillColor = UIColor.black.cgColor + shadowLayer.path = shadowPath.cgPath + shadowLayer.fillRule = kCAFillRuleEvenOdd + shadowLayer.shadowColor = (Theme.isDarkThemeEnabled ? UIColor.white : UIColor.black).cgColor + shadowLayer.shadowRadius = 0.5 + shadowLayer.shadowOpacity = 0.15 + shadowLayer.shadowOffset = .zero + } + + override public func setImage(_ image: UIImage?, for state: UIControlState) { + ensureViewConfigured() + super.setImage(image, for: state) + } + + // MARK: Private + + var hasBeenConfigured = false + func ensureViewConfigured() { + guard !hasBeenConfigured else { + return + } + hasBeenConfigured = true + + autoPinToSquareAspectRatio() + + layer.minificationFilter = kCAFilterTrilinear + layer.magnificationFilter = kCAFilterTrilinear + layer.masksToBounds = true + layer.addSublayer(shadowLayer) + + contentMode = .scaleToFill + } +}