Add a floating “scroll to bottom” button in the corner of chat conversation when scrolling up. Also, instead of always scrolling to the bottom of the chat conversation when receiving a new message, a “unread message count” badge is added on the scroll down button to notify that new messages are available.

This commit is contained in:
QuentinArguillere 2022-01-28 12:07:08 +01:00
parent fb82e69db3
commit 2c8a87f8bc
7 changed files with 147 additions and 21 deletions

View file

@ -58,6 +58,8 @@
@property(nonatomic, strong) id<ChatConversationDelegate> chatRoomDelegate;
@property NSMutableDictionary<NSString *, UIImage *> *imagesInChatroom;
@property(nonatomic) NSTimer *ephemeralDisplayTimer;
@property (nullable, nonatomic) UIButton *floatingScrollButton;
@property (nullable, nonatomic) UILabel *scrollBadge;
- (void)addEventEntry:(LinphoneEventLog *)event;
- (void)scrollToBottom:(BOOL)animated;

View file

@ -53,6 +53,7 @@
[self stopEphemeralDisplayTimer];
[NSNotificationCenter.defaultCenter removeObserver:self];
[super viewWillDisappear:animated];
[_floatingScrollButton setHidden:TRUE];
}
#pragma mark -
@ -83,6 +84,7 @@
bool oneToOne = capabilities & LinphoneChatRoomCapabilitiesOneToOne;
bctbx_list_t *chatRoomEvents = linphone_chat_room_get_history_events(_chatRoom, 0);
int unread_count = 0;
bctbx_list_t *head = chatRoomEvents;
size_t listSize = bctbx_list_size(chatRoomEvents);
@ -95,6 +97,11 @@
chatRoomEvents = chatRoomEvents->next;
} else {
LinphoneChatMessage *chat = linphone_event_log_get_chat_message(event);
if (chat && !linphone_chat_message_is_read(chat)) {
if (unread_count == 0) {
// [eventList addObject:[NSString stringWithString:@"Ceci est un test wesh wesh"]];
}
}
// if auto_download is available and file transfer in progress, not add event now
if (!(autoDownload && chat && linphone_chat_message_is_file_transfer_in_progress(chat))) {
[totalEventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]];
@ -164,6 +171,8 @@
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(count - 1) inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
[ChatConversationView markAsRead:_chatRoom];
}
- (void)scrollToLastUnread:(BOOL)animated {
@ -411,7 +420,7 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
LinphoneChatMessage *msg = linphone_event_log_get_chat_message(event);
[VIEW(ChatConversationView) initiateReplyViewForMessage:msg];
[self scrollToBottom:TRUE];
}];
UISwipeActionsConfiguration *swipeActionConfig = [UISwipeActionsConfiguration configurationWithActions:@[replyAction]];
@ -509,4 +518,5 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f;
[self reloadData];
}
@end

View file

@ -631,7 +631,6 @@ static UICompositeViewDescription *compositeDescription = nil;
_composeIndicatorView.frame = newComposingFrame;
}
completion:^(BOOL finished) {
[_tableController scrollToBottom:TRUE];
_composeIndicatorView.hidden = !visible;
}];
}
@ -1348,10 +1347,18 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven
const LinphoneAddress *from = linphone_chat_message_get_from_address(chat);
if (!from)
return;
bool isDisplayingBottomOfTable = [view.tableController.tableView indexPathsForVisibleRows].lastObject.row == [view.tableController totalNumberOfItems] - 1;
[view.tableController addEventEntry:(LinphoneEventLog *)event_log];
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];
[view.tableController scrollToLastUnread:TRUE];
if (isDisplayingBottomOfTable) {
[view.tableController scrollToBottom:TRUE];
} else {
int unread_msg = linphone_chat_room_get_unread_messages_count(cr);
[[view.tableController scrollBadge] setText:[NSString stringWithFormat:@"%d", unread_msg]];
}
}
void on_chat_room_chat_message_sending(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) {

View file

@ -0,0 +1,99 @@
//
// FloatingScrollDownButton.swift
// linphone
//
// Created by QuentinArguillere on 27/01/2022.
//
import Foundation
import UIKit
public extension ChatConversationTableView {
private enum Constants {
static let trailingValue: CGFloat = 20.0
static let leadingValue: CGFloat = 85.0
static let buttonHeight: CGFloat = 40.0
static let buttonWidth: CGFloat = 40.0
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
createFloatingButton()
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let lastCellRowIndex = tableView.indexPathsForVisibleRows?.last?.row {
if( lastCellRowIndex != self.totalNumberOfItems() - 1) {
self.floatingScrollButton?.isHidden = false
self.scrollBadge?.isHidden = (self.scrollBadge?.text == nil)
} else {
self.floatingScrollButton?.isHidden = true
self.scrollBadge?.text = nil
}
}
}
private func createFloatingButton() {
self.floatingScrollButton = UIButton(type: .custom)
self.floatingScrollButton?.translatesAutoresizingMaskIntoConstraints = false
constrainFloatingButtonToWindow()
self.floatingScrollButton?.setImage(UIImage(named: "scroll_to_bottom_default"), for: .normal)
self.floatingScrollButton?.addTarget(self, action: #selector(scrollToBottomButtonAction(_:)), for: .touchUpInside)
self.floatingScrollButton?.isHidden = true;
addBadgeToButon(badge: nil)
}
private func constrainFloatingButtonToWindow() {
DispatchQueue.main.async {
guard let keyWindow = UIApplication.shared.keyWindow,
let floatingButton = self.floatingScrollButton else { return }
keyWindow.addSubview(floatingButton)
keyWindow.trailingAnchor.constraint(equalTo: floatingButton.trailingAnchor,
constant: Constants.trailingValue).isActive = true
keyWindow.bottomAnchor.constraint(equalTo: floatingButton.bottomAnchor,
constant: Constants.leadingValue).isActive = true
floatingButton.widthAnchor.constraint(equalToConstant:
Constants.buttonWidth).isActive = true
floatingButton.heightAnchor.constraint(equalToConstant:
Constants.buttonHeight).isActive = true
}
}
@IBAction private func scrollToBottomButtonAction(_ sender: Any) {
scroll(toBottom: true)
}
private func addBadgeToButon(badge: String?) {
self.scrollBadge = UILabel()
self.scrollBadge?.text = badge
self.scrollBadge?.textColor = UIColor.white
self.scrollBadge?.backgroundColor = UIColor.red
self.scrollBadge?.font = UIFont.systemFont(ofSize: 12.0)
self.scrollBadge?.sizeToFit()
self.scrollBadge?.textAlignment = .center
if let badgeSize = self.scrollBadge?.frame.size, let scrollButton = self.floatingScrollButton {
let height = max(18, Double(badgeSize.height) + 5.0)
let width = max(height, Double(badgeSize.width) + 10.0)
var vertical: Double?, horizontal: Double?
let badgeInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15)
vertical = Double(badgeInset.top) - Double(badgeInset.bottom)
horizontal = Double(badgeInset.left) - Double(badgeInset.right)
let x = (Double(scrollButton.bounds.size.width) - 10 + horizontal!)
let y = -(Double(badgeSize.height) / 2) - 10 + vertical!
self.scrollBadge?.frame = CGRect(x: x, y: y, width: width, height: height)
self.scrollBadge!.layer.cornerRadius = self.scrollBadge!.frame.height/2
self.scrollBadge!.layer.masksToBounds = true
scrollButton.addSubview(self.scrollBadge!)
}
}
}

View file

@ -8,4 +8,4 @@
#import "FastAddressBook.h"
#import "Log.h"
#import "AudioHelper.h"
#import "ChatConversationTableView.h"

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -629,6 +629,8 @@
63F1DF4F1BCE985F00EDED90 /* UICallConferenceCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF4D1BCE985F00EDED90 /* UICallConferenceCell.m */; };
63F1DF511BCE986A00EDED90 /* UICallConferenceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63F1DF531BCE986A00EDED90 /* UICallConferenceCell.xib */; };
63FB30351A680E73008CA393 /* UIRoundedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63FB30341A680E73008CA393 /* UIRoundedImageView.m */; };
669B140827A1821F0012220A /* scroll_to_bottom_default.png in Resources */ = {isa = PBXBuildFile; fileRef = 669B140727A1821F0012220A /* scroll_to_bottom_default.png */; };
669B140C27A29D140012220A /* FloatingScrollDownButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669B140B27A29D140012220A /* FloatingScrollDownButton.swift */; };
70E542F313E147E3002BA2C0 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70E542F213E147E3002BA2C0 /* OpenGLES.framework */; };
70E542F513E147EB002BA2C0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70E542F413E147EB002BA2C0 /* QuartzCore.framework */; };
8C1B67061E671826001EA2FE /* AudioHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C1B67051E671826001EA2FE /* AudioHelper.m */; };
@ -1619,6 +1621,8 @@
63FB30331A680E73008CA393 /* UIRoundedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIRoundedImageView.h; sourceTree = "<group>"; };
63FB30341A680E73008CA393 /* UIRoundedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIRoundedImageView.m; sourceTree = "<group>"; };
65CEDD144CABFAA70A29AF27 /* Pods_linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
669B140727A1821F0012220A /* scroll_to_bottom_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = scroll_to_bottom_default.png; sourceTree = "<group>"; };
669B140B27A29D140012220A /* FloatingScrollDownButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingScrollDownButton.swift; sourceTree = "<group>"; };
68D9EC27FCECD5DE2E19CD3C /* Pods-liblinphoneTester.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTester.debug.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester.debug.xcconfig"; sourceTree = "<group>"; };
6E1BC45342F5201DABD7FE55 /* Pods_latestCallsWidget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_latestCallsWidget.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6F30EA7BEA39DA427CE0754E /* Pods_msgNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_msgNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -2196,6 +2200,7 @@
C6DA657B261C950C0020CB43 /* VFSUtil.swift */,
614C087723D1A35F00217F80 /* ProviderDelegate.swift */,
614C087923D1A37400217F80 /* CallManager.swift */,
669B140B27A29D140012220A /* FloatingScrollDownButton.swift */,
6134812C2406CECC00695B41 /* ConfigManager.swift */,
6134812E2407B35200695B41 /* AppManager.swift */,
614C087623D1A35E00217F80 /* linphone-Bridging-Header.h */,
@ -2989,6 +2994,7 @@
633FED7D1D3CD5590014B822 /* routes_disabled@2x.png */,
633FED7E1D3CD5590014B822 /* routes_selected.png */,
633FED7F1D3CD5590014B822 /* routes_selected@2x.png */,
669B140727A1821F0012220A /* scroll_to_bottom_default.png */,
615A2812217F24D40060F920 /* security_1_indicator.png */,
615A2814217F24E00060F920 /* security_1_indicator@2x.png */,
615A281A217F6F9B0060F920 /* security_2_indicator.png */,
@ -3642,6 +3648,7 @@
633FEEE71D3CD55A0014B822 /* numpad_9_over~ipad.png in Resources */,
633FEEEA1D3CD55A0014B822 /* numpad_9~ipad@2x.png in Resources */,
633FEEC31D3CD55A0014B822 /* numpad_5_default.png in Resources */,
669B140827A1821F0012220A /* scroll_to_bottom_default.png in Resources */,
633FEE3B1D3CD5590014B822 /* contact_add_disabled@2x.png in Resources */,
633FEF011D3CD55A0014B822 /* options_default@2x.png in Resources */,
633FEEC01D3CD55A0014B822 /* numpad_4_over~ipad@2x.png in Resources */,
@ -4373,6 +4380,7 @@
D350F20E15A43BB100149E54 /* AssistantView.m in Sources */,
D3F795D615A582810077328B /* ChatConversationView.m in Sources */,
D32B6E2915A5BC440033019F /* ChatConversationTableView.m in Sources */,
669B140C27A29D140012220A /* FloatingScrollDownButton.swift in Sources */,
D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleTextCell.m in Sources */,
63D11C531C3D501200E8FCEE /* Log.m in Sources */,
D3128FE115AABC7E00A2147A /* ContactDetailsView.m in Sources */,
@ -5048,7 +5056,7 @@
IBC_MODULE = linphoneapp;
IBSC_MODULE = linphoneapp;
INFOPLIST_FILE = "linphone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
@ -5171,7 +5179,7 @@
IBC_MODULE = linphoneapp;
IBSC_MODULE = linphoneapp;
INFOPLIST_FILE = "linphone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
@ -5293,7 +5301,7 @@
IBC_MODULE = linphoneapp;
IBSC_MODULE = linphoneapp;
INFOPLIST_FILE = "linphone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
@ -5414,7 +5422,7 @@
IBC_MODULE = linphoneapp;
IBSC_MODULE = linphoneapp;
INFOPLIST_FILE = "linphone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
LINK_WITH_STANDARD_LIBRARIES = YES;
@ -5477,7 +5485,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
@ -5519,7 +5527,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
@ -5561,7 +5569,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
@ -5604,7 +5612,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = linphoneExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension;
@ -5703,7 +5711,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
@ -5755,7 +5763,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
@ -5807,7 +5815,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
@ -5859,7 +5867,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
@ -5915,7 +5923,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
@ -5967,7 +5975,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
@ -6019,7 +6027,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;
@ -6071,7 +6079,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = msgNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.6.0;
MTL_ENABLE_DEBUG_INFO = NO;