From 55fc032f3644a20295145aedd90868c86241be2b Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 16 Jun 2019 19:02:18 -0400 Subject: [PATCH] Extract attachment layout to it's own view Use AutoLayout + stack views instead of manual frames, fixes attachments sometimes not using the whole width --- Tusker.xcodeproj/project.pbxproj | 14 ++- .../{ => Attachments}/AttachmentView.swift | 19 ++-- .../AttachmentsContainerView.swift | 88 +++++++++++++++++++ .../ConversationMainStatusTableViewCell.swift | 43 ++------- .../ConversationMainStatusTableViewCell.xib | 8 +- Tusker/Views/Status/StatusTableViewCell.swift | 43 +-------- Tusker/Views/Status/StatusTableViewCell.xib | 6 +- 7 files changed, 125 insertions(+), 96 deletions(-) rename Tusker/Views/{ => Attachments}/AttachmentView.swift (90%) create mode 100644 Tusker/Views/Attachments/AttachmentsContainerView.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 076b193a..37c8d79d 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -160,6 +160,7 @@ D6C693F92162E4DB007D6A6D /* StatusContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693F82162E4DB007D6A6D /* StatusContentLabel.swift */; }; D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */; }; D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */; }; + D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C7D27C22B6EBF800071952 /* AttachmentsContainerView.swift */; }; D6C94D872139E62700CB5196 /* LargeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D862139E62700CB5196 /* LargeImageViewController.swift */; }; D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C94D882139E6EC00CB5196 /* AttachmentView.swift */; }; D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */; }; @@ -391,6 +392,7 @@ D6C693F82162E4DB007D6A6D /* StatusContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentLabel.swift; sourceTree = ""; }; D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = ""; }; D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = ""; }; + D6C7D27C22B6EBF800071952 /* AttachmentsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsContainerView.swift; sourceTree = ""; }; D6C94D862139E62700CB5196 /* LargeImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageViewController.swift; sourceTree = ""; }; D6C94D882139E6EC00CB5196 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = ""; }; D6D4DDCC212518A000E1C4BB /* Tusker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tusker.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -918,13 +920,13 @@ D6D58DF822074B74009C8DD9 /* LinkLabel.swift */, 04496BD621625361001F1B23 /* ContentLabel.swift */, D6C693F82162E4DB007D6A6D /* StatusContentLabel.swift */, - D6C94D882139E6EC00CB5196 /* AttachmentView.swift */, D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */, 04ED00B021481ED800567C53 /* SteppedProgressView.swift */, D67C57A721E2649B00C3118B /* Account Detail */, D67C57B021E28F9400C3118B /* Compose Status Reply */, D60C07E221E817560057FAA8 /* Compose Media */, D641C78A213DD926004B4513 /* Status */, + D6C7D27B22B6EBE200071952 /* Attachments */, D641C78B213DD92F004B4513 /* Profile Header */, D641C78C213DD937004B4513 /* Notifications */, D6C693CB2161256B007D6A6D /* Silent Action Permissions */, @@ -954,6 +956,15 @@ path = Utilities; sourceTree = ""; }; + D6C7D27B22B6EBE200071952 /* Attachments */ = { + isa = PBXGroup; + children = ( + D6C94D882139E6EC00CB5196 /* AttachmentView.swift */, + D6C7D27C22B6EBF800071952 /* AttachmentsContainerView.swift */, + ); + path = Attachments; + sourceTree = ""; + }; D6D4DDC3212518A000E1C4BB = { isa = PBXGroup; children = ( @@ -1401,6 +1412,7 @@ D6285B5121EA6E6E00FE4B39 /* AdvancedTableViewController.swift in Sources */, 0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */, D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */, + D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */, D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */, 0411610022B442870030A9B7 /* AttachmentViewController.swift in Sources */, D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */, diff --git a/Tusker/Views/AttachmentView.swift b/Tusker/Views/Attachments/AttachmentView.swift similarity index 90% rename from Tusker/Views/AttachmentView.swift rename to Tusker/Views/Attachments/AttachmentView.swift index f884112c..415ad79b 100644 --- a/Tusker/Views/AttachmentView.swift +++ b/Tusker/Views/Attachments/AttachmentView.swift @@ -22,23 +22,20 @@ class AttachmentView: UIImageView, GIFAnimatable { var gifData: Data? public lazy var animator: Animator? = Animator(withDelegate: self) + + init(attachment: Attachment) { + super.init(image: nil) + commonInit() + + self.attachment = attachment + loadImage() + } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonInit() } - override init(frame: CGRect) { - super.init(frame: frame) - commonInit() - } - - convenience init(frame: CGRect, attachment: Attachment) { - self.init(frame: frame) - self.attachment = attachment - loadImage() - } - func commonInit() { contentMode = .scaleAspectFill layer.masksToBounds = true diff --git a/Tusker/Views/Attachments/AttachmentsContainerView.swift b/Tusker/Views/Attachments/AttachmentsContainerView.swift new file mode 100644 index 00000000..d74cdbb1 --- /dev/null +++ b/Tusker/Views/Attachments/AttachmentsContainerView.swift @@ -0,0 +1,88 @@ +// +// AttachmentsContainerView.swift +// Tusker +// +// Created by Shadowfacts on 6/16/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +class AttachmentsContainerView: UIView { + + var delegate: AttachmentViewDelegate? + + override func awakeFromNib() { + super.awakeFromNib() + + self.isUserInteractionEnabled = true + } + + func updateUI(status: Status) { + let attachments = status.attachments.filter { $0.kind == .image } + + if attachments.count > 0 { + self.isHidden = false + + let mainView: UIView + switch attachments.count { + case 1: + mainView = createAttachmentView(attachments[0]) + case 2: + mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ + createAttachmentView(attachments[0]), + createAttachmentView(attachments[1]) + ]) + case 3: + mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ + createAttachmentView(attachments[0]), + createAttachmentsStack(axis: .vertical, arrangedSubviews: [ + createAttachmentView(attachments[1]), + createAttachmentView(attachments[2]) + ]) + ]) + case 4: + mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ + createAttachmentsStack(axis: .vertical, arrangedSubviews: [ + createAttachmentView(attachments[0]), + createAttachmentView(attachments[2]) + ]), + createAttachmentsStack(axis: .vertical, arrangedSubviews: [ + createAttachmentView(attachments[1]), + createAttachmentView(attachments[3]) + ]) + ]) + default: + fatalError("Too many attachments") + } + + addSubview(mainView) + NSLayoutConstraint.activate([ + mainView.leadingAnchor.constraint(equalTo: leadingAnchor), + mainView.trailingAnchor.constraint(equalTo: trailingAnchor), + mainView.topAnchor.constraint(equalTo: topAnchor), + mainView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + } else { + self.isHidden = true + subviews.forEach { $0.removeFromSuperview() } + } + } + + private func createAttachmentView(_ attachment: Attachment) -> AttachmentView { + let attachmentView = AttachmentView(attachment: attachment) + attachmentView.delegate = delegate + attachmentView.translatesAutoresizingMaskIntoConstraints = false + return attachmentView + } + + private func createAttachmentsStack(axis: NSLayoutConstraint.Axis, arrangedSubviews: [UIView]) -> UIStackView { + let stack = UIStackView(arrangedSubviews: arrangedSubviews) + stack.axis = axis + stack.spacing = 8 + stack.translatesAutoresizingMaskIntoConstraints = false + return stack + } + +} diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift index 6d96dded..fad59ddf 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift @@ -22,7 +22,7 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive @IBOutlet weak var contentLabel: StatusContentLabel! @IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var timestampLabel: UILabel! - @IBOutlet weak var attachmentsView: UIView! + @IBOutlet weak var attachmentsView: AttachmentsContainerView! @IBOutlet weak var favoriteButton: UIButton! @IBOutlet weak var reblogButton: UIButton! @@ -49,12 +49,10 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive override func awakeFromNib() { displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) - displayNameLabel.isUserInteractionEnabled = true usernameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) - usernameLabel.isUserInteractionEnabled = true avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed))) - avatarImageView.isUserInteractionEnabled = true avatarImageView.layer.masksToBounds = true + attachmentsView.delegate = self attachmentsView.layer.cornerRadius = 5 attachmentsView.layer.masksToBounds = true } @@ -93,33 +91,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive } updateTimestamp() - let attachments = status.attachments.filter({ $0.kind == .image }) - if attachments.count > 0 { - attachmentsView.isHidden = false - let width = attachmentsView.bounds.width - let height: CGFloat = 200 - switch attachments.count { - case 1: - addAttachmentView(frame: CGRect(x: 0, y: 0, width: width, height: height), attachment: attachments[0]) - case 2: - addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height), attachment: attachments[0]) - addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height), attachment: attachments[1]) - case 3: - addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height), attachment: attachments[0]) - addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[1]) - addAttachmentView(frame: CGRect(x: width / 2 + 4, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[2]) - case 4: - addAttachmentView(frame: CGRect(x: 0, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[0]) - addAttachmentView(frame: CGRect(x: 0, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[1]) - addAttachmentView(frame: CGRect(x: width / 2 + 4, y: 0, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[2]) - addAttachmentView(frame: CGRect(x: width / 2 + 4, y: height / 2 + 4, width: width / 2 - 4, height: height / 2 - 4), attachment: attachments[3]) - default: - fatalError("Too many attachments") - } - } else { - attachmentsView.isHidden = true - } - + attachmentsView.updateUI(status: status) + let realStatus = status.reblog ?? status favorited = realStatus.favourited ?? false reblogged = realStatus.reblogged ?? false @@ -149,13 +122,7 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive updateTimestampWorkItem = nil } } - - func addAttachmentView(frame: CGRect, attachment: Attachment) { - let attachmentView = AttachmentView(frame: frame, attachment: attachment) - attachmentView.delegate = self - attachmentsView.addSubview(attachmentView) - } - + override func prepareForReuse() { if let url = avatarURL { ImageCache.avatars.cancel(url) diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.xib b/Tusker/Views/Status/ConversationMainStatusTableViewCell.xib index 0cb84363..0adbb423 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.xib +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.xib @@ -20,20 +20,20 @@ - + - -