Add CallFlowLayout and remove NBMPeersFlowLayout

Signed-off-by: Marcel Müller <marcel-mueller@gmx.de>
This commit is contained in:
Marcel Müller 2023-01-05 11:57:20 +01:00
Родитель 3438da964d
Коммит c7ed92b0fd
7 изменённых файлов: 124 добавлений и 270 удалений

Просмотреть файл

@ -47,6 +47,7 @@
1F98DF9C28E7484700E05174 /* ReferenceDeckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F98DF9B28E7484700E05174 /* ReferenceDeckView.swift */; };
1F98DF9E28E7485000E05174 /* ReferenceDeckView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F98DF9D28E7485000E05174 /* ReferenceDeckView.xib */; };
1FA20C8A284001D80062B4F3 /* DebounceWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA20C89284001D80062B4F3 /* DebounceWebView.swift */; };
1FA732FC2966CBB7003D2103 /* CallFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA732FB2966CBB7003D2103 /* CallFlowLayout.swift */; };
1FB52E762842C75E00AC741B /* QRCodeLoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB52E752842C75E00AC741B /* QRCodeLoginController.swift */; };
1FB6678F28CE381300D29F8D /* SubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB6678E28CE381300D29F8D /* SubtitleTableViewCell.swift */; };
1FD9182928C55A73009092AB /* BGTaskHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD9182828C55A73009092AB /* BGTaskHelper.swift */; };
@ -195,7 +196,6 @@
2C78EFA11F828C41008AFA74 /* CallViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C78EF9F1F828C41008AFA74 /* CallViewController.xib */; };
2C78EFA51F86FF4A008AFA74 /* CallParticipantViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C78EFA31F86FF4A008AFA74 /* CallParticipantViewCell.m */; };
2C78EFA61F86FF4A008AFA74 /* CallParticipantViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C78EFA41F86FF4A008AFA74 /* CallParticipantViewCell.xib */; };
2C78EFA91F87EF39008AFA74 /* NBMPeersFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C78EFA81F87EF39008AFA74 /* NBMPeersFlowLayout.m */; };
2C7A1237200E0A5700864818 /* UserSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C7A1235200E0A5700864818 /* UserSettingsTableViewCell.xib */; };
2C7A12422017872600864818 /* AddParticipantsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C7A12402017872600864818 /* AddParticipantsTableViewController.m */; };
2C7A12432017872600864818 /* AddParticipantsTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C7A12412017872600864818 /* AddParticipantsTableViewController.xib */; };
@ -402,6 +402,7 @@
1F98DF9B28E7484700E05174 /* ReferenceDeckView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceDeckView.swift; sourceTree = "<group>"; };
1F98DF9D28E7485000E05174 /* ReferenceDeckView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReferenceDeckView.xib; sourceTree = "<group>"; };
1FA20C89284001D80062B4F3 /* DebounceWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebounceWebView.swift; sourceTree = "<group>"; };
1FA732FB2966CBB7003D2103 /* CallFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallFlowLayout.swift; sourceTree = "<group>"; };
1FB52E752842C75E00AC741B /* QRCodeLoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginController.swift; sourceTree = "<group>"; };
1FB6678E28CE381300D29F8D /* SubtitleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleTableViewCell.swift; sourceTree = "<group>"; };
1FD9182828C55A73009092AB /* BGTaskHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGTaskHelper.swift; sourceTree = "<group>"; };
@ -589,8 +590,6 @@
2C78EFA21F86FF4A008AFA74 /* CallParticipantViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallParticipantViewCell.h; sourceTree = "<group>"; };
2C78EFA31F86FF4A008AFA74 /* CallParticipantViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallParticipantViewCell.m; sourceTree = "<group>"; };
2C78EFA41F86FF4A008AFA74 /* CallParticipantViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CallParticipantViewCell.xib; sourceTree = "<group>"; };
2C78EFA71F87EF39008AFA74 /* NBMPeersFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBMPeersFlowLayout.h; sourceTree = "<group>"; };
2C78EFA81F87EF39008AFA74 /* NBMPeersFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBMPeersFlowLayout.m; sourceTree = "<group>"; };
2C7A1235200E0A5700864818 /* UserSettingsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UserSettingsTableViewCell.xib; sourceTree = "<group>"; };
2C7A123F2017872600864818 /* AddParticipantsTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AddParticipantsTableViewController.h; sourceTree = "<group>"; };
2C7A12402017872600864818 /* AddParticipantsTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AddParticipantsTableViewController.m; sourceTree = "<group>"; };
@ -1145,8 +1144,6 @@
2C78EF9D1F828C41008AFA74 /* CallViewController.h */,
2C78EF9E1F828C41008AFA74 /* CallViewController.m */,
2C78EF9F1F828C41008AFA74 /* CallViewController.xib */,
2C78EFA71F87EF39008AFA74 /* NBMPeersFlowLayout.h */,
2C78EFA81F87EF39008AFA74 /* NBMPeersFlowLayout.m */,
2C78EF9A1F826B22008AFA74 /* NCCallController.h */,
2C78EF9B1F826B22008AFA74 /* NCCallController.m */,
2C8CDD0421C2EDE8004E2997 /* AvatarBackgroundImageView.h */,
@ -1156,6 +1153,7 @@
2C4DE9F021F732B40096940D /* NCAudioController.h */,
2C4DE9F121F732B40096940D /* NCAudioController.m */,
2C4446FA265D5BEF00DF1DBC /* CallConstants.h */,
1FA732FB2966CBB7003D2103 /* CallFlowLayout.swift */,
);
name = Calls;
sourceTree = "<group>";
@ -1883,7 +1881,6 @@
2C0574851EDD9E8E00D9E7F2 /* AppDelegate.m in Sources */,
2C4987BD21E640E20060AC27 /* CallKitManager.m in Sources */,
2C4446F3265D51A600DF1DBC /* NCPushNotificationsUtils.m in Sources */,
2C78EFA91F87EF39008AFA74 /* NBMPeersFlowLayout.m in Sources */,
2C1ABDE5257F883400AEDFB6 /* ABContact.m in Sources */,
2CA1CCDB1F1F6FCA002FE6A2 /* RoomTableViewCell.m in Sources */,
2C5BFBEF288A947900E75118 /* PollVotingView.swift in Sources */,
@ -1984,6 +1981,7 @@
DA66582D27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift in Sources */,
1F5CDF642584E78900B0026E /* NCChatFileStatus.m in Sources */,
2CBF82C11FD5AE3F00636459 /* NCPushProxySessionManager.m in Sources */,
1FA732FC2966CBB7003D2103 /* CallFlowLayout.swift in Sources */,
2C78EF951F7E70EB008AFA74 /* NCPeerConnection.m in Sources */,
2C06BF6C20AEB0030031EB46 /* RoundedNumberView.m in Sources */,
2CEDA88926F10BB20044552B /* UserStatusMessageViewController.swift in Sources */,

Просмотреть файл

@ -0,0 +1,115 @@
//
// Copyright (c) 2022 Marcel Müller <marcel-mueller@gmx.de>
//
// Author Marcel Müller <marcel-mueller@gmx.de>
//
// GNU GPL version 3 or any later version
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Based on https://stackoverflow.com/a/41409642
import UIKit
@objcMembers
class CallFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
self.minimumInteritemSpacing = 10
self.minimumLineSpacing = 10
self.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func isPortrait() -> Bool {
guard let collectionView = collectionView else { return false }
return collectionView.bounds.size.width < collectionView.bounds.size.height
}
func numberOfColumns(for numberOfCells: Int) -> Int {
if isPortrait() {
if numberOfCells <= 2 {
return 1
} else if numberOfCells <= 6 {
return 2
}
return 3
}
if numberOfCells == 1 {
return 1
} else if numberOfCells <= 2 || numberOfCells == 4 {
return 2
} else if numberOfCells == 3 || numberOfCells == 5 {
return 3
}
return 4
}
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
let contentSize = collectionView.bounds.size
let numberOfCells = collectionView.numberOfItems(inSection: 0)
let numberOfColumns = numberOfColumns(for: numberOfCells)
let numberOfRows = (CGFloat(numberOfCells) / CGFloat(numberOfColumns)).rounded(.up)
// Calculate cell width
let sectionInsetWidth = sectionInset.left + sectionInset.right
let safeAreaInsetWidth = collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right
let marginsAndInsetsWidth = sectionInsetWidth + safeAreaInsetWidth + minimumInteritemSpacing * CGFloat(numberOfColumns - 1)
let itemWidth = ((contentSize.width - marginsAndInsetsWidth) / CGFloat(numberOfColumns)).rounded(.down)
// Calculate cell height
let sectionInsetHeight = sectionInset.top + sectionInset.bottom
let safeAreaInsetHeight = collectionView.safeAreaInsets.top + collectionView.safeAreaInsets.bottom
let marginsAndInsetsHeight = sectionInsetHeight + safeAreaInsetHeight + minimumLineSpacing * CGFloat(numberOfRows + 1)
var itemHeight = ((contentSize.height - marginsAndInsetsHeight) / CGFloat(numberOfRows)).rounded(.down)
// Enfore minimum cell height
if itemHeight < kCallParticipantCellMinHeight {
itemHeight = kCallParticipantCellMinHeight
}
itemSize = CGSize(width: itemWidth, height: itemHeight)
}
override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forBoundsChange: newBounds)
if let context = context as? UICollectionViewFlowLayoutInvalidationContext {
context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
}
return context
}
}

Просмотреть файл

@ -35,7 +35,6 @@
#import "CallKitManager.h"
#import "CallParticipantViewCell.h"
#import "NBMPeersFlowLayout.h"
#import "NCAPIController.h"
#import "NCAppBranding.h"
#import "NCAudioController.h"
@ -46,6 +45,8 @@
#import "NCSignalingMessage.h"
#import "NCUtils.h"
#import "NextcloudTalk-Swift.h"
typedef NS_ENUM(NSInteger, CallState) {
CallStateJoining,
CallStateWaitingParticipants,
@ -164,6 +165,7 @@ typedef NS_ENUM(NSInteger, CallState) {
[self.closeScreensharingButton.layer setCornerRadius:16.0f];
[self.collectionView.layer setCornerRadius:30.0f];
[self.collectionView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentAlways];
_airplayView = [[AVRoutePickerView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
_airplayView.tintColor = [UIColor whiteColor];
@ -1291,14 +1293,6 @@ typedef NS_ENUM(NSInteger, CallState) {
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGRect frame = [NBMPeersFlowLayout frameForWithNumberOfItems:_peersInCall.count
row:indexPath.row
contentSize:self.collectionView.frame.size];
return frame.size;
}
-(void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
CallParticipantViewCell *participantCell = (CallParticipantViewCell *)cell;
@ -1307,11 +1301,6 @@ typedef NS_ENUM(NSInteger, CallState) {
[self updateParticipantCell:participantCell withPeerConnection:peerConnection];
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return kCallViewParticipantLineSpacing;
}
#pragma mark - Call Controller delegate
- (void)callControllerDidJoinCall:(NCCallController *)callController

Просмотреть файл

@ -192,7 +192,7 @@
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="aUh-Z0-hO6">
<rect key="frame" x="0.0" y="84" width="834" height="1028"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" sectionInsetReference="safeArea" id="iSf-tu-VMU">
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" sectionInsetReference="safeArea" id="iSf-tu-VMU" customClass="CallFlowLayout" customModule="NextcloudTalk" customModuleProvider="target">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#import <UIKit/UIKit.h>
extern CGFloat const kCallViewParticipantLineSpacing;
@interface NBMPeersFlowLayout : UICollectionViewFlowLayout
+ (CGRect)frameForWithNumberOfItems:(NSUInteger)numberOfItems row:(NSUInteger)row contentSize:(CGSize)contentSize;
@end

Просмотреть файл

@ -1,223 +0,0 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#import "NBMPeersFlowLayout.h"
#import "CallParticipantViewCell.h"
CGFloat const kCallViewParticipantLineSpacing = 8.0;
@interface NBMPeersFlowLayout ()
@property (assign, nonatomic) BOOL isActvive;
// Containers for keeping track of changing items
@property (nonatomic, strong) NSMutableArray *insertedIndexPaths;
@property (nonatomic, strong) NSMutableArray *removedIndexPaths;
@property (nonatomic, strong) NSMutableArray *insertedSectionIndices;
@property (nonatomic, strong) NSMutableArray *removedSectionIndices;
// Caches for keeping current/previous attributes
@property (nonatomic, strong) NSMutableDictionary *currentCellAttributes;
@property (nonatomic, strong) NSMutableDictionary *currentSupplementaryAttributesByKind;
@property (nonatomic, strong) NSMutableDictionary *cachedCellAttributes;
@property (nonatomic, strong) NSMutableDictionary *cachedSupplementaryAttributesByKind;
@end
@implementation NBMPeersFlowLayout
- (CGSize)collectionViewContentSize {
return self.collectionView.frame.size;
}
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
}
- (void)prepareLayout {
// // Deep-copy attributes in current cache
// self.cachedCellAttributes = [[NSMutableDictionary alloc] initWithDictionary:self.currentCellAttributes copyItems:YES];
// self.cachedSupplementaryAttributesByKind = [NSMutableDictionary dictionary];
// [self.currentSupplementaryAttributesByKind enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableDictionary * attribByPath, BOOL *stop) {
// NSMutableDictionary * cachedAttribByPath = [[NSMutableDictionary alloc] initWithDictionary:attribByPath copyItems:YES];
// [self.cachedSupplementaryAttributesByKind setObject:cachedAttribByPath forKey:kind];
// }];
//
// self.minimumInteritemSpacing = 2;
// self.minimumLineSpacing = 2;
}
- (void)invalidateLayout {
[super invalidateLayout];
self.isActvive = NO;
}
+ (NSUInteger )columnsWithWithNumberOfItems:(NSUInteger )numbers isPortrait:(BOOL)isPortrait{
int countOfColumns = -1;
if (isPortrait) {
if (numbers <= 2 ){
countOfColumns = 1;
}
else if (numbers <= 6) {
countOfColumns = 2;
}
else {
countOfColumns = 3;
}
}
else {
if (numbers == 1 ) {
countOfColumns = 1;
} else if (numbers <= 2 || numbers == 4) {
countOfColumns = 2;
} else if (numbers == 3 || numbers == 5) {
countOfColumns = 3;
} else {
countOfColumns = 4;
}
}
return countOfColumns;
}
+ (CGRect)frameForWithNumberOfItems:(NSUInteger)numberOfItems row:(NSUInteger)row contentSize:(CGSize)contentSize {
BOOL isPortrait = contentSize.width < contentSize.height;
NSUInteger columns = [NBMPeersFlowLayout columnsWithWithNumberOfItems:numberOfItems
isPortrait:isPortrait];
// Only use a border if there's more than one participant, otherwise go fullscreen
// For X we take half the spacing, because both neighbour cells will have a border
NSUInteger borderX = numberOfItems == 1 ? 0 : kCallViewParticipantLineSpacing / 2;
NSUInteger borderY = numberOfItems == 1 ? 0 : kCallViewParticipantLineSpacing;
NSUInteger rows = ceil((float)numberOfItems / (float)columns);
CGFloat h = (contentSize.height - ((rows + 1) * borderY)) / rows;
CGFloat w = (contentSize.width - ((columns + 1) * borderX)) / columns ;
h = (h < kCallParticipantCellMinHeight) ? kCallParticipantCellMinHeight : h;
NSUInteger line = row == 0 ? 0 : row / columns;
NSUInteger _r = row % columns;
NSUInteger xOffset = w * _r;
NSUInteger yOffset = ceil(line == 0 ? 0 : h * line);
NSUInteger xBorderOffset = borderX * (_r + 1) ;
NSUInteger yBorderOffset = borderY * (line + 1);
NSUInteger mod = numberOfItems % columns;
NSUInteger centered = numberOfItems - mod;
if (row >= centered) {
CGFloat centerX = contentSize.width / 2;
xBorderOffset = centerX - mod * w/2;
}
CGRect result = CGRectMake(xOffset + xBorderOffset , yOffset + yBorderOffset, w , h);
return result;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSInteger items = [self.collectionView.dataSource collectionView:self.collectionView
numberOfItemsInSection:0];
[attributes enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attributes, NSUInteger idx, BOOL * stop) {
CGRect frame = [NBMPeersFlowLayout frameForWithNumberOfItems:items
row:idx
contentSize:self.collectionView.frame.size];
if (attributes.representedElementCategory == UICollectionElementCategoryCell)
{
attributes.frame = frame;
[self.currentCellAttributes setObject:attributes
forKey:attributes.indexPath];
}
}];
return attributes;
}
- (void)finalizeCollectionViewUpdates
{
[super finalizeCollectionViewUpdates];
self.insertedIndexPaths = nil;
self.removedIndexPaths = nil;
self.insertedSectionIndices = nil;
self.removedSectionIndices = nil;
}
// center last row elements by collectionView center
- (void)centerLastRowElements:(NSArray *)attributes {
CGFloat attributeWidth = ((UICollectionViewLayoutAttributes *)attributes[0]).frame.size.width;
CGFloat centerX = self.collectionView.center.x;
CGFloat offsetByX = centerX - attributes.count * attributeWidth/2; // shift by half sum of lowest row elements width
for (NSUInteger i = attributes.count; i != 0; i--) { // from left to right
// first element will be leftmost at display
UICollectionViewLayoutAttributes *currentAttribute = attributes[attributes.count - i];
currentAttribute.frame = CGRectMake(offsetByX,
currentAttribute.frame.origin.y,
currentAttribute.frame.size.width,
currentAttribute.frame.size.height);
offsetByX += attributeWidth;
}
}
/// arrange elements in row by full width
- (void)arrangeLastRowElements:(NSArray *)attributes width:(CGFloat)width {
for (UICollectionViewLayoutAttributes *attribute in attributes) {
attribute.size = CGSizeMake(width/attributes.count, attribute.size.height);
}
[self centerLastRowElements:attributes];
}
@end

Просмотреть файл

@ -24,6 +24,7 @@
#define NextcloudTalk_Bridging_Header_h
#import "AvatarHeaderView.h"
#import "CallParticipantViewCell.h"
#import "DetailedOptionsSelectorTableViewController.h"
#import "DirectoryTableViewCell.h"
#import "HeaderWithButton.h"