Размер кадра ContentView изменяется в TableViewCell после вызова scrollViewDidEndDecelerating - PullRequest
0 голосов
/ 25 марта 2020

my contentView.frame.size и layoutMargins, например, layoutMargins.top меняются после быстрой прокрутки в UITableView вниз / вверх, поэтому вызывается метод scrollViewDidEndDecelerating.

Однако, когда я устанавливаю scrollView.bounces = false в моем UITableViewController, фрейм не изменяется:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        scrollView.bounces = false
}

Так как я делаю некоторые вычисления для моего макета TableViewCell, которые зависят от contentView.frame, изменение размера является проблемой для меня. Тем не менее, я не хочу устанавливать scrollView.bounces = false из-за UX.

Любые идеи о том, как справиться с этим?

Вот мои соответствующие классы:

BasePostsUIViewController

import AVKit
import UIKit

class BasePostsUIViewController<View: PostViewProtocol, TableViewCell: BasePostUITableViewCell>: UIViewController, UITableViewDataSource, UITableViewDelegate {
    var viewModel: PostsModelProtocol?
    var basePostsView: PostViewProtocol! { return view as? View }
    let tableCellIdentifier = "landingPostCell"

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    func setupPostTableView() {
        basePostsView.postsTableView.delegate = self
        basePostsView.postsTableView.dataSource = self
        basePostsView.postsTableView.register(TableViewCell.self, forCellReuseIdentifier: tableCellIdentifier)
    }

    func displayPosts(viewModel: PostsModelProtocol) {
        refreshPosts(viewModel: viewModel)
    }

    private func refreshPosts(viewModel: PostsModelProtocol) {
        self.viewModel = viewModel
        basePostsView.postsTableView.reloadData()
    }

    // MARK: Tableview functions

    func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
        if viewModel != nil {
            return viewModel!.posts.count
        }

        return 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let post = viewModel!.posts[indexPath.row]
        guard let cell = (tableView.dequeueReusableCell(withIdentifier: tableCellIdentifier) as? TableViewCell) else {
            return UITableViewCell()
        }

        cell.tableView = basePostsView.postsTableView
        cell.tableViewBounds = basePostsView.postsTableView.bounds
        cell.post = post
        cellCreated(cell: cell)

        return cell
    }

    func cellCreated(cell _: TableViewCell) {}


    func tableView(_: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt _: IndexPath) {
        guard let videoCell = cell as? TableViewCell else { return }
        videoCell.videoPlayerView.playerView.playerLayer.player!.pause()
        videoCell.videoPlayerView.playerView.playerLayer.player = nil
    }

   }

BasePostUITableViewCell

import AVKit
import FontAwesome_swift
import SnapKit
import SwiftDate
import UIKit

class BasePostUITableViewCell: UITableViewCell {
    lazy var profilePictureThumbnailImageView = UIUtils.createUIImageView()
    lazy var nameLabel = UIUtils.createUILabel()
    lazy var messageTextView = UIUtils.createUITextView()
    lazy var videoPlayerView = UIUtils.createVideoPlayerView()
    lazy var dateLabel = UIUtils.createUILabel()
    lazy var numberOfLikesButton = UIUtils.createUIButton()
    lazy var numberOfStarsButton = UIUtils.createUILabel()
    lazy var numberOfCommentsButton = UIUtils.createUIButton()
    let iconsCounterStackView = UIStackView()
    var tableView: UITableView?
    var tableViewBounds: CGRect?

    var post: Post? {
        didSet {
            didSetPost()
        }
    }

    func didSetPost() {
        if let post = post {
            profilePictureThumbnailImageView.setupProfilePictureThumbnail(profilePictureThumbnailUrl: post.author.profilePictureThumbnailUrl!)

            nameLabel.text = "\(post.author.firstName!) \(post.author.lastName!)"
            messageTextView.text = post.message

            videoPlayerView.playVideo(forVideoURL: URL(string: post.video.uri!)!)

            dateLabel.text = post.createdAt.toRelative(since: DateInRegion(), style: RelativeFormatter.defaultStyle())

            if post.hasUserLikedPost {
                UIUtils.setupIconButtonWithText(text: String(post.countLikes!))
            } else {
                UIUtils.setupIconButtonWithText(text: String(post.countLikes!))
            }

            UIUtils.setupIconLabelWithText(text: String(post.countLikes!))
            UIUtils.setupIconButtonWithText(text: String(post.countComments!))

            setupViews()
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        contentView.backgroundColor = .white
    }

    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setupViews() {
        iconsCounterStackView.axis = .horizontal
        iconsCounterStackView.distribution = .equalCentering
        iconsCounterStackView.addArrangedSubview(numberOfLikesButton)
        iconsCounterStackView.addArrangedSubview(numberOfStarsButton)
        iconsCounterStackView.addArrangedSubview(numberOfCommentsButton)

        contentView.addSubview(profilePictureThumbnailImageView)
        contentView.addSubview(nameLabel)
        contentView.addSubview(messageTextView)
        contentView.addSubview(videoPlayerView)
        contentView.addSubview(dateLabel)
        contentView.addSubview(iconsCounterStackView)

        profilePictureThumbnailImageView.snp.makeConstraints { (make) -> Void in
            make.top.leading.equalTo(contentView.layoutMarginsGuide)
            make.width.height.equalTo(tableViewBounds!.width * 0.15).priorityHigh()
        }

        nameLabel.snp.makeConstraints { (make) -> Void in
            make.top.equalTo(contentView.layoutMarginsGuide)
            make.leading.equalTo(profilePictureThumbnailImageView.snp.trailing).offset(layoutMargins.left).priorityMedium()
        }
        nameLabel.numberOfLines = 0

        dateLabel.snp.makeConstraints { (make) -> Void in
            make.leading.equalTo(profilePictureThumbnailImageView.snp.trailing).offset(layoutMargins.left).priorityMedium()
            make.bottom.equalTo(profilePictureThumbnailImageView.snp.bottom)
        }
        dateLabel.numberOfLines = 0

        messageTextView.isEditable = false
        messageTextView.snp.makeConstraints { (make) -> Void in
            make.top.equalTo(profilePictureThumbnailImageView.snp.bottom).offset(0.5 * layoutMargins.bottom).priorityMedium()
            make.leading.trailing.equalTo(contentView.layoutMarginsGuide)
            make.bottom.equalTo(videoPlayerView.snp.top)
        }

        iconsCounterStackView.snp.makeConstraints { (make) -> Void in
            make.top.equalTo(videoPlayerView.snp.bottom)
            make.leading.trailing.equalTo(contentView.layoutMarginsGuide)
            make.bottom.equalTo(contentView.layoutMarginsGuide)
        }

        setNeedsLayout()
        layoutIfNeeded()

        let profilePictureThumbnailImageViewHeight = profilePictureThumbnailImageView.frame.height
        let messageViewHeight = messageTextView.frame.height
        let iconsCounterStackViewHeight = iconsCounterStackView.frame.height
        let layoutMarginTopHeight = layoutMargins.top
        let layoutMarginBottomHeight = layoutMargins.bottom

        let calculatedOffset =
            profilePictureThumbnailImageViewHeight +
            messageViewHeight +
            2 * iconsCounterStackViewHeight +
            layoutMarginTopHeight +
            2 * layoutMarginBottomHeight +
            0.5 * layoutMarginBottomHeight +
            4.5 * layoutMarginBottomHeight

        videoPlayerView.snp.makeConstraints { (make) -> Void in
            make.leading.trailing.equalToSuperview()
            make.height.equalTo(tableViewBounds!.height - calculatedOffset).priorityMedium()
        }
    }
}

PostViewProtocol

import UIKit

protocol PostViewProtocol {
    var postsTableView: UITableView { get }
}

LandingViewController

import CoreLocation
import FacebookLogin
import GooglePlaces
import UIKit

protocol LandingDisplayLogic: class {
    func checkHasAnyFollowees()
    func checkLoginResult(viewModel: Landing.DoFacebookLogin.ViewModel)
    func displayPosts(viewModel: PostsModelProtocol)
    func routeToAddFollowees()
    func routeToMain()
    func readCurrentUser()
    func storeUserId(viewModel: Landing.StoreUserId.ViewModel)
}

class LandingViewController: BasePostsUIViewController<LandingView, LandingPostTableViewCell>, LandingDisplayLogic {
    var interactor: LandingBusinessLogic?
    var router: (NSObjectProtocol & LandingRoutingLogic & LandingDataPassing)?

    // MARK: Object lifecycle

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    // MARK: Setup

    private func setup() {
        let viewController = self
        let interactor = LandingInteractor()
        let presenter = LandingPresenter()
        let router = LandingRouter()
        viewController.interactor = interactor
        viewController.router = router
        interactor.presenter = presenter
        presenter.viewController = viewController
        router.viewController = viewController
        router.dataStore = interactor
    }

    // MARK: Routing

    func routeToAddFollowees() {
        router?.routeToAddFollowees()
    }

    func routeToMain() {
        router?.routeToMain()
    }

    // MARK: View lifecycle

    override func loadView() {
        view = LandingView(frame: CGRect(x: 0, y: navigationBarHeight, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        setupPostTableView()
        setupFacebookLoginButton()

        readIntroPosts()
    }

    // MARK: Handling of Intro Posts Objects

    func readIntroPosts() {
        let request = Landing.ReadIntroPosts.Request()
        interactor?.readIntroPosts(request: request)
    }

    // MARK: - Setup and Handling of Facebook Login

    fileprivate func setupFacebookLoginButton() {
        guard let landingView = (self.basePostsView as? LandingView) else {
            preconditionFailure("Expected basePostView to be of type LandingView")
        }
        landingView.facebookLoginButton.addTarget(self, action: #selector(facebookLogin), for: .touchUpInside)
    }

    @objc func facebookLogin() {
        let loginManager = LoginManager()
        loginManager.logOut()
        loginManager.logIn(permissions: [.publicProfile], viewController: self) { loginResult in
            switch loginResult {
            case let .failed(error):
                // TODO: What should happen here?
                log.error("Facebook Login failed: $\(error)")
            case .cancelled:
                // Nothing to do here
                print("User cancelled login.")
            case let .success(granted: grantedPermissions, declined: declinedPermissions, token: accessToken):
                print("Logged in!")
                self.doFacebookLogin(with: accessToken.tokenString)
            }
        }
    }

    func doFacebookLogin(with userAccessToken: String) {
        let request = Landing.DoFacebookLogin.Request(facebookLogin: FacebookLogin(userAccessToken: userAccessToken))
        interactor?.doFacebookLogin(request: request)
    }


}

LandingView

import Foundation
import UIKit

class LandingView: UIView, PostViewProtocol {
    let postsTableView = UITableView()
    let facebookLoginButton = FacebookButton()

    override init(frame: CGRect) {
        super.init(frame: frame)

        setupViews()
    }

    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    fileprivate func setupViews() {
        backgroundColor = .white

        setupFacebookLoginButton()
        setupPostTableView()
    }

    fileprivate func setupFacebookLoginButton() {
        addSubview(facebookLoginButton)

        facebookLoginButton.isUserInteractionEnabled = true
        facebookLoginButton.snp.makeConstraints { (make) -> Void in
            make.bottom.equalTo(layoutMargins)
            make.centerX.equalToSuperview()
        }
    }

    fileprivate func setupPostTableView() {
        postsTableView.rowHeight = UITableView.automaticDimension
        postsTableView.estimatedRowHeight = 500
        postsTableView.translatesAutoresizingMaskIntoConstraints = false

        addSubview(postsTableView)

        postsTableView.snp.makeConstraints { (make) -> Void in
            make.top.leading.trailing.equalToSuperview()
            make.bottom.equalTo(facebookLoginButton.snp.top).offset(-1 * layoutMargins.bottom)
        }
    }
}

LandingPostTableViewCell

class LandingPostTableViewCell: BasePostUITableViewCell {}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...