Implement follow/unfollow/block in UserView (IOS-140)
This commit is contained in:
parent
52fb1eff1f
commit
e2a05cd747
|
@ -22,6 +22,7 @@
|
|||
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; };
|
||||
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||
27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; };
|
||||
2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */; };
|
||||
2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; };
|
||||
2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; };
|
||||
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */; };
|
||||
|
@ -613,6 +614,7 @@
|
|||
164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = "<group>"; };
|
||||
1D6D967E77A5357E2C6110D9 /* Pods-Mastodon.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - debug.xcconfig"; sourceTree = "<group>"; };
|
||||
27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+URL.swift"; sourceTree = "<group>"; };
|
||||
2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+UserView.swift"; sourceTree = "<group>"; };
|
||||
2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowedTagsViewModel+DiffableDataSource.swift"; sourceTree = "<group>"; };
|
||||
2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+IsNotEmpty.swift"; sourceTree = "<group>"; };
|
||||
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountHistory.swift; sourceTree = "<group>"; };
|
||||
|
@ -2390,6 +2392,7 @@
|
|||
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
|
||||
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
|
||||
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */,
|
||||
2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */,
|
||||
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
|
||||
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
|
||||
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
|
||||
|
@ -3598,6 +3601,7 @@
|
|||
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */,
|
||||
DB0FCB9C27980AB6006C02E2 /* HashtagTimelineViewController+DataSourceProvider.swift in Sources */,
|
||||
DB63F76F279A7D1100455B82 /* NotificationTableViewCell.swift in Sources */,
|
||||
2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */,
|
||||
DB0FCB8C2796BF8D006C02E2 /* SearchViewModel+Diffable.swift in Sources */,
|
||||
DBEFCD76282A143F00C0ABEA /* ReportStatusViewController.swift in Sources */,
|
||||
DBDFF1952805561700557A48 /* DiscoveryPostsViewModel+Diffable.swift in Sources */,
|
||||
|
|
|
@ -122,6 +122,7 @@ extension SearchResultSection {
|
|||
configuration: Configuration
|
||||
) {
|
||||
cell.configure(
|
||||
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
||||
tableView: tableView,
|
||||
viewModel: viewModel,
|
||||
delegate: configuration.userTableViewCellDelegate
|
||||
|
|
|
@ -42,6 +42,7 @@ extension UserSection {
|
|||
context.managedObjectContext.performAndWait {
|
||||
guard let user = record.object(in: context.managedObjectContext) else { return }
|
||||
configure(
|
||||
context: context,
|
||||
tableView: tableView,
|
||||
cell: cell,
|
||||
viewModel: .init(value: .user(user)),
|
||||
|
@ -66,6 +67,7 @@ extension UserSection {
|
|||
extension UserSection {
|
||||
|
||||
static func configure(
|
||||
context: AppContext,
|
||||
tableView: UITableView,
|
||||
cell: UserTableViewCell,
|
||||
viewModel: UserTableViewCell.ViewModel,
|
||||
|
@ -73,6 +75,7 @@ extension UserSection {
|
|||
) {
|
||||
|
||||
cell.configure(
|
||||
meUserID: context.authenticationService.mastodonAuthenticationBoxes.first?.userID,
|
||||
tableView: tableView,
|
||||
viewModel: viewModel,
|
||||
delegate: configuration.userTableViewCellDelegate
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import MastodonUI
|
||||
import CoreDataStack
|
||||
import MastodonCore
|
||||
import MastodonSDK
|
||||
|
||||
extension DataSourceFacade {
|
||||
static func responseToUserViewButtonAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
buttonState: UserView.ButtonState
|
||||
) async throws {
|
||||
switch buttonState {
|
||||
case .follow, .unfollow:
|
||||
try await DataSourceFacade.responseToUserFollowAction(
|
||||
dependency: dependency,
|
||||
user: user
|
||||
)
|
||||
case .blocked:
|
||||
try await DataSourceFacade.responseToUserBlockAction(
|
||||
dependency: dependency,
|
||||
user: user
|
||||
)
|
||||
case .none:
|
||||
break //no-op
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ import UIKit
|
|||
import Combine
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
import MastodonUI
|
||||
import CoreDataStack
|
||||
|
||||
final class FamiliarFollowersViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -91,4 +93,15 @@ extension FamiliarFollowersViewController: UITableViewDelegate, AutoGenerateTabl
|
|||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension FamiliarFollowersViewController: UserTableViewCellDelegate { }
|
||||
extension FamiliarFollowersViewController: UserTableViewCellDelegate {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||
Task {
|
||||
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||
dependency: self,
|
||||
user: user.asRecord,
|
||||
buttonState: state
|
||||
)
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import Combine
|
|||
import MastodonCore
|
||||
import MastodonUI
|
||||
import MastodonLocalization
|
||||
import CoreDataStack
|
||||
|
||||
final class FollowerListViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -118,4 +119,15 @@ extension FollowerListViewController: UITableViewDelegate, AutoGenerateTableView
|
|||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension FollowerListViewController: UserTableViewCellDelegate { }
|
||||
extension FollowerListViewController: UserTableViewCellDelegate {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||
Task {
|
||||
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||
dependency: self,
|
||||
user: user.asRecord,
|
||||
buttonState: state
|
||||
)
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import Combine
|
|||
import MastodonLocalization
|
||||
import MastodonCore
|
||||
import MastodonUI
|
||||
import CoreDataStack
|
||||
|
||||
final class FollowingListViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -116,4 +117,15 @@ extension FollowingListViewController: UITableViewDelegate, AutoGenerateTableVie
|
|||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension FollowingListViewController: UserTableViewCellDelegate { }
|
||||
extension FollowingListViewController: UserTableViewCellDelegate {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||
Task {
|
||||
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||
dependency: self,
|
||||
user: user.asRecord,
|
||||
buttonState: state
|
||||
)
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import GameplayKit
|
|||
import Combine
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
import MastodonUI
|
||||
import CoreDataStack
|
||||
|
||||
final class FavoritedByViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -107,4 +109,15 @@ extension FavoritedByViewController: UITableViewDelegate, AutoGenerateTableViewD
|
|||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension FavoritedByViewController: UserTableViewCellDelegate { }
|
||||
extension FavoritedByViewController: UserTableViewCellDelegate {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||
Task {
|
||||
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||
dependency: self,
|
||||
user: user.asRecord,
|
||||
buttonState: state
|
||||
)
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import GameplayKit
|
|||
import Combine
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
import MastodonUI
|
||||
import CoreDataStack
|
||||
|
||||
final class RebloggedByViewController: UIViewController, NeedsDependency {
|
||||
|
||||
|
@ -107,4 +109,15 @@ extension RebloggedByViewController: UITableViewDelegate, AutoGenerateTableViewD
|
|||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension RebloggedByViewController: UserTableViewCellDelegate { }
|
||||
extension RebloggedByViewController: UserTableViewCellDelegate {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser) {
|
||||
Task {
|
||||
try await DataSourceFacade.responseToUserViewButtonAction(
|
||||
dependency: self,
|
||||
user: user.asRecord,
|
||||
buttonState: state
|
||||
)
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ final class UserListViewModel {
|
|||
)
|
||||
// end init
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UserListViewModel {
|
||||
|
|
|
@ -22,6 +22,6 @@ extension SearchHistoryUserCollectionViewCell {
|
|||
func configure(
|
||||
viewModel: ViewModel
|
||||
) {
|
||||
userView.configure(user: viewModel.value)
|
||||
userView.configure(user: viewModel.value, delegate: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@ import MastodonCore
|
|||
import Meta
|
||||
|
||||
extension UserView {
|
||||
public func configure(user: MastodonUser) {
|
||||
public func configure(user: MastodonUser, delegate: UserViewDelegate?) {
|
||||
self.delegate = delegate
|
||||
viewModel.user = user
|
||||
|
||||
Publishers.CombineLatest(
|
||||
user.publisher(for: \.avatar),
|
||||
UserDefaults.shared.publisher(for: \.preferredStaticAvatar)
|
||||
|
|
|
@ -26,13 +26,25 @@ extension UserTableViewCell {
|
|||
extension UserTableViewCell {
|
||||
|
||||
func configure(
|
||||
meUserID: MastodonUser.ID?,
|
||||
tableView: UITableView,
|
||||
viewModel: ViewModel,
|
||||
delegate: UserTableViewCellDelegate?
|
||||
) {
|
||||
switch viewModel.value {
|
||||
case .user(let user):
|
||||
userView.configure(user: user)
|
||||
userView.configure(user: user, delegate: delegate)
|
||||
|
||||
if user.id == meUserID {
|
||||
userView.setButtonState(.none)
|
||||
} else if user.blockingBy.contains(where: { $0.id == meUserID }) {
|
||||
userView.setButtonState(.blocked)
|
||||
} else if user.followingBy.contains(where: { $0.id == meUserID }) {
|
||||
userView.setButtonState(.unfollow)
|
||||
} else {
|
||||
userView.setButtonState(.follow)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.delegate = delegate
|
||||
|
|
|
@ -13,7 +13,7 @@ import MastodonLocalization
|
|||
import MastodonUI
|
||||
import MastodonSDK
|
||||
|
||||
protocol UserTableViewCellDelegate: AnyObject { }
|
||||
protocol UserTableViewCellDelegate: UserViewDelegate, AnyObject { }
|
||||
|
||||
final class UserTableViewCell: UITableViewCell {
|
||||
|
||||
|
@ -21,7 +21,9 @@ final class UserTableViewCell: UITableViewCell {
|
|||
|
||||
let userView: UserView = {
|
||||
let view = UserView()
|
||||
// view.setButtonState(.follow)
|
||||
[UserView.ButtonState.follow, UserView.ButtonState.unfollow, UserView.ButtonState.blocked].randomElement().map {
|
||||
view.setButtonState($0)
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.150",
|
||||
"blue" : "0x30",
|
||||
"green" : "0x3B",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.800",
|
||||
"green" : "0.227",
|
||||
"red" : "0.337"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.150",
|
||||
"blue" : "0.988",
|
||||
"green" : "0.173",
|
||||
"red" : "0.337"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -57,6 +57,9 @@ public enum Asset {
|
|||
public static let inactive = ColorAsset(name: "Colors/Button/inactive")
|
||||
public static let tagFollow = ColorAsset(name: "Colors/Button/tagFollow")
|
||||
public static let tagUnfollow = ColorAsset(name: "Colors/Button/tagUnfollow")
|
||||
public static let userBlocked = ColorAsset(name: "Colors/Button/userBlocked")
|
||||
public static let userFollow = ColorAsset(name: "Colors/Button/userFollow")
|
||||
public static let userFollowing = ColorAsset(name: "Colors/Button/userFollowing")
|
||||
}
|
||||
public enum Icon {
|
||||
public static let plus = ColorAsset(name: "Colors/Icon/plus")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Created by MainasuK on 2022-1-19.
|
||||
//
|
||||
|
||||
import CoreDataStack
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
|
@ -26,6 +27,7 @@ extension UserView {
|
|||
@Published public var authorUsername: String?
|
||||
@Published public var authorFollowers: Int?
|
||||
@Published public var authorVerifiedLink: String?
|
||||
@Published public var user: MastodonUser?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,13 @@ import UIKit
|
|||
import Combine
|
||||
import MetaTextKit
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import os
|
||||
import CoreDataStack
|
||||
|
||||
public protocol UserViewDelegate: AnyObject {
|
||||
func userView(_ view: UserView, didTapButtonWith state: UserView.ButtonState, for user: MastodonUser)
|
||||
}
|
||||
|
||||
public final class UserView: UIView {
|
||||
|
||||
|
@ -17,6 +23,10 @@ public final class UserView: UIView {
|
|||
case none, follow, unfollow, blocked
|
||||
}
|
||||
|
||||
private var currentButtonState: ButtonState = .none
|
||||
|
||||
public weak var delegate: UserViewDelegate?
|
||||
|
||||
public var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
public private(set) lazy var viewModel: ViewModel = {
|
||||
|
@ -92,7 +102,6 @@ public final class UserView: UIView {
|
|||
private let followButton: UIButton = {
|
||||
let button = FollowButton()
|
||||
button.cornerRadius = 10
|
||||
button.setTitle("Follow", for: .normal)
|
||||
button.isHidden = true
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
|
@ -105,22 +114,7 @@ public final class UserView: UIView {
|
|||
|
||||
return button
|
||||
}()
|
||||
|
||||
public func setButtonState(_ state: ButtonState) {
|
||||
switch state {
|
||||
case .follow, .unfollow, .blocked:
|
||||
verifiedStackView.axis = .vertical
|
||||
verifiedStackView.alignment = .leading
|
||||
verifiedStackCenterSpacerView.isHidden = true
|
||||
followButton.isHidden = false
|
||||
case .none:
|
||||
verifiedStackView.axis = .horizontal
|
||||
verifiedStackView.alignment = .leading
|
||||
verifiedStackCenterSpacerView.isHidden = false
|
||||
followButton.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func prepareForReuse() {
|
||||
disposeBag.removeAll()
|
||||
|
||||
|
@ -128,6 +122,7 @@ public final class UserView: UIView {
|
|||
viewModel.authorAvatarImageURL = nil
|
||||
|
||||
avatarButton.avatarImageView.cancelTask()
|
||||
setButtonState(.none)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
|
@ -246,3 +241,51 @@ private final class FollowButton: RoundedEdgesButton {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension UserView {
|
||||
private func prepareButtonStateLayout(for state: ButtonState) {
|
||||
switch state {
|
||||
case .none:
|
||||
verifiedStackView.axis = .horizontal
|
||||
verifiedStackView.alignment = .leading
|
||||
verifiedStackCenterSpacerView.isHidden = false
|
||||
followButton.isHidden = true
|
||||
default:
|
||||
verifiedStackView.axis = .vertical
|
||||
verifiedStackView.alignment = .leading
|
||||
verifiedStackCenterSpacerView.isHidden = true
|
||||
followButton.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func didTapButton() {
|
||||
guard let user = viewModel.user else { return }
|
||||
delegate?.userView(self, didTapButtonWith: currentButtonState, for: user)
|
||||
}
|
||||
|
||||
func setButtonState(_ state: ButtonState) {
|
||||
currentButtonState = state
|
||||
prepareButtonStateLayout(for: state)
|
||||
|
||||
switch state {
|
||||
case .follow:
|
||||
followButton.setTitle(L10n.Common.Controls.Friendship.follow, for: .normal)
|
||||
followButton.setBackgroundColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
||||
followButton.setTitleColor(.white, for: .normal)
|
||||
case .unfollow:
|
||||
followButton.setTitle(L10n.Common.Controls.Friendship.following, for: .normal)
|
||||
followButton.setBackgroundColor(Asset.Colors.Button.userFollowing.color, for: .normal)
|
||||
followButton.setTitleColor(Asset.Colors.Button.userFollow.color, for: .normal)
|
||||
case .blocked:
|
||||
followButton.setTitle(L10n.Common.Controls.Friendship.blocked, for: .normal)
|
||||
followButton.setBackgroundColor(Asset.Colors.Button.userBlocked.color, for: .normal)
|
||||
followButton.setTitleColor(.systemRed, for: .normal)
|
||||
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
||||
followButton.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||
followButton.titleLabel?.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .boldSystemFont(ofSize: 15))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue