I am new in iOS.
I have a view controller in which I have added child view controller using addChildViewController in Parent Controller and it hangs the app.
addChildViewController has 2 webservice calling in the viewDidAppear method. It works fine when webservice calling complete. I want to know how to stop the app hangs problem when adding child view controller.
Below is the code how we add the child view controller:
self.stickerController = StickersViewController()
self.addChildViewController(self.stickerController)
self.addSubview(subView: self.stickerController.view, toView: self.view)
self.stickerController.view.isHidden = true
self.stickerController.didMove(toParentViewController: self)
StickersViewController view controller:
import Foundation
class StickersViewController: UIViewController,UICollectionViewDelegateFlowLayout, UICollectionViewDataSource,UISearchBarDelegate {
var yValue:CGFloat = 65
var navheight:CGFloat = 65
var searchView = UIView()
var searchField = UISearchBar()
let cellReuseIdentifier = "cell"
var tableView: UICollectionView!
var dataDictionary: NSMutableArray!
var imageCache = [String:UIImage]()
var titleHeight:CGFloat = 260
var pagenumber: String = "1"
var showAnimator: Bool = true
var isgetLastInsertId: Bool = false
var isRequestSend: Bool = false
var stopPagging : Bool = false
var callFromPagging: Bool = false
var noDataLabel = UILabel()
var tryAgainButton = UIButton()
var gallery_id:Int = 0
let cellReuseIdentifierEmoji = "cell"
var tableViewEmoji: UICollectionView!
var dataDictionaryEmoji: NSMutableArray!
var pagenumberEmoji: String = "1"
var isRequestSendEmoji: Bool = false
var stopPaggingEmoji : Bool = false
var noDataLabelEmoji = UILabel()
var callFromPaggingEmoji:Bool = false
var emojisCategory:UIScrollView!
var sepratorView:UIView!
var plusEmojiBtn = UILabel()
var userEmojiData:NSMutableArray!
var tabMenuHeight:CGFloat = 40
var isSearchBarHidden:Bool = false
var searchBarHeight:CGFloat = 40
var viewHeight:CGFloat = 0
var categoryTabMenuHeight:CGFloat = 0
override func viewDidLoad() {
if isSearchBarHidden == true{
yValue = 0
}else{
searchBarHeight = 80
}
if isSearchBarHidden == false{
viewHeight = self.view.frame.height
}
self.view.addSubview(searchView)
makeSearchView()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
layout.itemSize = CGSize(width: view.frame.width, height: view.frame.height)
self.tableView = UICollectionView(frame:CGRect(x: 0, y: yValue , width: appWidth,height: viewHeight - yValue - searchBarHeight), collectionViewLayout: layout)
tableView.delegate = self
tableView.dataSource = self
tableView.register(EmoticonsCategoryTableViewCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
tableView.backgroundColor = appBackgroundColor
self.tableView.alwaysBounceVertical = true
self.tableView.alpha = 1
self.tableView.tag = 1
self.view.addSubview(tableView)
//emoji table
let layoutEmoji: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layoutEmoji.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
layoutEmoji.itemSize = CGSize(width: view.frame.width, height: view.frame.height)
self.tableViewEmoji = UICollectionView(frame:CGRect(x: 0, y: yValue , width: appWidth,height: viewHeight - yValue - tabMenuHeight - searchBarHeight - categoryTabMenuHeight), collectionViewLayout: layoutEmoji)
self.tableViewEmoji.tag = 2
tableViewEmoji.delegate = self
tableViewEmoji.dataSource = self
tableViewEmoji.register(ContentFeelingTableViewCell.self, forCellWithReuseIdentifier: cellReuseIdentifier)
tableViewEmoji.backgroundColor = appBackgroundColor
self.tableViewEmoji.alwaysBounceVertical = true
tableViewEmoji.alpha = 0
self.view.addSubview(tableViewEmoji)
showAnimator = true;
paramsPost.removeAll(keepingCapacity: false)
emojisCategory = UIScrollView(frame: CGRect(x: 0, y: viewHeight - searchBarHeight, width: appWidth - 40, height: 40))
view.addSubview(emojisCategory)
emojisCategory.backgroundColor = appBackgroundColor
sepratorView = UIView(frame: CGRect(x: 0, y: emojisCategory.frame.origin.y, width: appWidth , height: 1/divideSepratorScaleValue))
sepratorView.backgroundColor = appSepratorColor
view.addSubview(sepratorView)
plusEmojiBtn.frame = CGRect(x: emojisCategory.frame.width, y: emojisCategory.frame.origin.y + 1/divideSepratorScaleValue, width: 40, height: 40)
plusEmojiBtn.backgroundColor = appforgroundcolor
plusEmojiBtn.textColor = navigationColor
plusEmojiBtn.font = UIFont(name: fontIcon, size: fontSizeHuge)
plusEmojiBtn.text = "\u{f067}"
plusEmojiBtn.textAlignment = .center
view.addSubview(plusEmojiBtn)
plusEmojiBtn.isUserInteractionEnabled = true
let tabbedPlus = UITapGestureRecognizer(target: self, action: #selector(self.plusTabbed))
plusEmojiBtn.addGestureRecognizer(tabbedPlus)
}
#objc func plusTabbed() {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "EmojiStoreButtonClicked"), object: nil)
let pVc = EmojiStickerStore()
pVc.modalTransitionStyle = UIModalTransitionStyle.coverVertical
let navigationController = UINavigationController(rootViewController: pVc)
self.present(navigationController, animated: true, completion: nil)
}
func getData(){
let width = appWidth
let height = appHeight
if(showAnimator == true){
//show activity indicator
if(callFromPagging == false){
animator.center = self.tableView.center
}else{
animator.center = CGPoint(x: width/2, y: (height - CGFloat(activityIndicatorBottomHeight)))
}
animator.hidesWhenStopped = true
animator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
animator.startAnimating()
self.view.addSubview(animator)
}
if Reachability.isConnectedToNetwork() {
isRequestSend = true
noDataLabel.isHidden = true
tryAgainButton.isHidden = true
let imageData = Data()
paramsPost.removeAll(keepingCapacity: false)
//remove previous stored data
if !(searchField.text?.isEmpty)!{
paramsPost["search"] = searchField.text!
}
sendRequest(paramsPost as Dictionary<String, AnyObject>, url: "stickers/stickers/limit/"+limitCount+"/page/"+pagenumber, method: "POST",image:imageData) { (succeeded) -> () in
DispatchQueue.main.async(execute: {
if(self.pagenumber == "1" && self.dataDictionary != nil){
//remove all stored data from global variable
self.dataDictionary.removeAllObjects()
}
animator.stopAnimating()
if self.isgetLastInsertId == true
{
//stop refresh spinner
refreshControl.endRefreshing()
}
if(succeeded["error_message"] != nil){
if let code = succeeded["error_message"] as? String{
if !code.isEmpty{
openMaintainanceMode(view: self,code: code)
return
}
}
//alert error
self.view.makeToast(succeeded["error_message"]! as! String, duration: 5, position: .bottom)
}else{
//success
if let responseResult = succeeded["result"] as? NSDictionary{
//pagging data
// self.pagenumber = "\(responseResult["next_page"]!)"
//if(responseResult["total_page"] as! Int == responseResult["current_page"] /as! Int){
// self.stopPagging = true
//}else{
// self.stopPagging = false
// }
self.stopPagging = true
if(self.dataDictionary != nil && self.dataDictionary.count > 0){
for data in responseResult["emotions"] as! NSMutableArray {
self.dataDictionary.add(data as AnyObject)
}
}else{
// store in global variable
if(responseResult["emotions"] != nil){
self.dataDictionary = responseResult["emotions"] as! NSMutableArray
self.noDataLabel.isHidden = true
self.tryAgainButton.isHidden = true
//reload table view
self.tableView.reloadData()
}
}
}
self.tableView.reloadData()
}
if self.dataDictionary == nil || self.dataDictionary.count == 0{
self.noData()
}
//reinitialize default variables
self.isRequestSend = false
self.isgetLastInsertId = false
self.callFromPagging = false
})
}
}else{
self.view.makeToast(String(format: NSLocalizedString("No Internet Connection", comment: "")), duration: 5, position: .bottom)
}
}
func makeScrollEmojiContent() {
for i in 0..<userEmojiData.count{
if let dic = userEmojiData[i] as? NSDictionary{
let title = dic["title"] as! String
let id = dic["gallery_id"] as! Int
let label = UILabel(frame: CGRect(x: CGFloat(i*40), y: 0, width: 40, height: 40))
label.tag = id
if title == "search"{
label.font = UIFont(name: fontIcon, size: fontSizeHuge)
label.text = "\u{f002}"
label.textColor = navigationColor
label.backgroundColor = titleLightColor
}else{
let icon = dic["icon"] as! String
let attachment: NSTextAttachment = NSTextAttachment()
if let img = self.imageCache[icon]{
attachment.image = img
}else{
if let url = NSURL(string: icon) {
if let data = NSData.init(contentsOf: url as URL) {
let imageI = UIImage(data: data as Data)!
attachment.image = imageI
self.imageCache[icon] = imageI
}
}
}
attachment.bounds = CGRect(x: 0, y: -3, width: fontSizeHuge, height: fontSizeHuge)
let attachmentString: NSAttributedString = NSAttributedString(attachment: attachment)
label.attributedText = attachmentString
}
label.isUserInteractionEnabled = true
let tabbedPlus = UITapGestureRecognizer(target: self, action: #selector(self.labelTabbed(_:)))
label.addGestureRecognizer(tabbedPlus)
label.textAlignment = .center
emojisCategory.addSubview(label)
}
}
if userEmojiData != nil{
emojisCategory.contentSize = CGSize(width: CGFloat(userEmojiData.count * 40), height: emojisCategory.frame.height)
}
}
#objc func labelTabbed(_ sender:UITapGestureRecognizer) {
let viewS = sender.view as! UILabel
let tag = viewS.tag
for obj in emojisCategory.subviews{
if obj.tag != tag{
obj.backgroundColor = UIColor.clear
}else{
obj.backgroundColor = titleLightColor
}
}
self.searchField.text = ""
if tag == 0{
self.tableView.alpha = 1
self.searchView.alpha = 1
self.tableViewEmoji.alpha = 0
self.tableViewEmoji.frame.origin.y = yValue
self.noDataLabelEmoji.isHidden = true
self.tableViewEmoji.frame.size.height = viewHeight - yValue - tabMenuHeight - searchBarHeight
}else{
self.tableView.alpha = 0
self.searchView.alpha = 0
gallery_id = tag
self.tableViewEmoji.frame.origin.y = navheight
self.tableViewEmoji.frame.size.height = viewHeight - yValue - tabMenuHeight - categoryTabMenuHeight
if dataDictionaryEmoji != nil && dataDictionaryEmoji.count > 0{
dataDictionaryEmoji.removeAllObjects()
}
tableViewEmoji.reloadData()
getEmojiFeelingData()
}
}
func getEmojiFeelingData(){
self.tableView.alpha = 0
self.tableViewEmoji.alpha = 1
let width = appWidth
let height = appHeight
if(showAnimator == true){
//show activity indicator
if(callFromPagging == false){
animator.center = self.view.center
}else{
animator.center = CGPoint(x: width/2, y: (height - CGFloat(activityIndicatorBottomHeight)))
}
animator.hidesWhenStopped = true
animator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
animator.startAnimating()
self.view.addSubview(animator)
}
if Reachability.isConnectedToNetwork() {
isRequestSendEmoji = true
let imageData = Data()
paramsPost.removeAll(keepingCapacity: false)
//remove previous stored data
if !(searchField.text?.isEmpty)!{
paramsPost["search"] = searchField.text!
}
paramsPost["gallery_id"] = "\(self.gallery_id)"
sendRequest(paramsPost as Dictionary<String, AnyObject>, url: "stickers/emoji-content/", method: "POST",image:imageData) { (succeeded) -> () in
DispatchQueue.main.async(execute: {
if(self.pagenumberEmoji == "1" && self.dataDictionaryEmoji != nil){
//remove all stored data from global variable
self.dataDictionaryEmoji.removeAllObjects()
}
animator.stopAnimating()
if(succeeded["error_message"] != nil){
if let code = succeeded["error_message"] as? String{
if !code.isEmpty{
openMaintainanceMode(view: self,code: code)
return
}
}
//alert error
self.view.makeToast(succeeded["error_message"]! as! String, duration: 5, position: .bottom)
}else{
//success
if let responseResult = succeeded["result"] as? NSDictionary{
self.stopPaggingEmoji = true
if(self.dataDictionaryEmoji != nil && self.dataDictionaryEmoji.count > 0){
for data in responseResult["emotions"] as! NSMutableArray {
self.dataDictionaryEmoji.add(data as AnyObject)
}
}else{
// store in global variable
if(responseResult["emotions"] != nil){
self.dataDictionaryEmoji = responseResult["emotions"] as! NSMutableArray
self.noDataLabelEmoji.isHidden = true
//reload table view
self.tableViewEmoji.reloadData()
}
}
}
self.tableViewEmoji.reloadData()
}
if self.dataDictionaryEmoji == nil || self.dataDictionaryEmoji.count == 0{
self.noDataEmoji()
}
//reinitialize default variables
self.isRequestSendEmoji = false
self.callFromPaggingEmoji = false
})
}
}else{
self.view.makeToast(String(format: NSLocalizedString("No Internet Connection", comment: "")), duration: 5, position: .bottom)
}
}
func scrollViewDidScroll(_ sender:UIScrollView) {
view.endEditing(true)
}
func noDataEmoji() {
self.noDataLabelEmoji = UILabel(frame: CGRect( x: 0, y: 0, width: appWidth, height: 30))
self.noDataLabelEmoji.font = UIFont(name: fontName, size: fontSizeLarge)
self.view.backgroundColor = appBackgroundColor
self.noDataLabelEmoji.textFontColor()
self.noDataLabelEmoji.textAlignment = .center
self.noDataLabelEmoji.text = NSLocalizedString("No stickers to show.", comment: "")
self.noDataLabelEmoji.center = self.tableViewEmoji.center
self.view.addSubview(self.noDataLabelEmoji)
}
func noData() {
self.noDataLabel = UILabel(frame: CGRect( x: 0, y: 0, width: appWidth, height: 30))
self.noDataLabel.font = UIFont(name: fontName, size: fontSizeLarge)
self.view.backgroundColor = appBackgroundColor
self.noDataLabel.textAlignment = .center
self.noDataLabel.textColor = noDataLabelTextColor
self.noDataLabel.text = NSLocalizedString("No category found with the given search.", comment: "")
self.noDataLabel.center = self.tableView.center
self.view.addSubview(self.noDataLabel)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView.tag == 2{
if(dataDictionaryEmoji != nil){
return dataDictionaryEmoji.count
}else{
return 0
}
}else{
if(dataDictionary != nil){
return dataDictionary.count
}else{
return 0
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
var width = appWidth - 20
if collectionView.tag == 1{
if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad)
{
// Ipad
width = (width - 5*2)/3
}
else
{
width = (width - 5*1)/2
}
return CGSize(width: width, height: 40)
}else{
if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad)
{
// Ipad
width = (width - 5*6)/6
}
else
{
width = (width - 5*4)/4
}
return CGSize(width: width, height: 60)
}
}
func makeSearchView() {
searchView.frame = CGRect(x: 0, y: yValue, width: appWidth, height: 50)
yValue = yValue + 50
searchField.frame = CGRect(x: 0, y: 0, width: appWidth, height: 50)
searchField.delegate = self
searchField.placeholder = NSLocalizedString("Search stickers", comment: "")
searchView.addSubview(searchField)
//change search text field attributes
for mainView in searchField.subviews {
for subView in mainView.subviews {
if let textField = subView as? UITextField {
textField.textColor = searchBarTextColor
textField.font = UIFont(name: fontName, size: fontSizeMedium)
}
}
}
}
func userEmojis() {
if Reachability.isConnectedToNetwork() {
let imageData = Data()
paramsPost.removeAll()
paramsPost["user_emojis"] = "1"
sendRequest(paramsPost as Dictionary<String, AnyObject>, url: "stickers/stickers/", method: "POST",image:imageData) { (succeeded) -> () in
DispatchQueue.main.async(execute: {
if(succeeded["error_message"] != nil){
if let code = succeeded["error_message"] as? String{
if !code.isEmpty{
openMaintainanceMode(view: self,code: code)
return
}
}
}else{
for obj in self.emojisCategory.subviews{
obj.removeFromSuperview()
}
//success
if let responseResult = succeeded["result"] as? NSDictionary{
if let userEmoji = responseResult["useremotions"] as? NSMutableArray{
self.emojisCategory.isHidden = false
self.sepratorView.isHidden = false
self.userEmojiData = userEmoji
self.makeScrollEmojiContent()
}else{
self.emojisCategory.isHidden = true
self.sepratorView.isHidden = true
}
}
}
})
}
}
}
override func viewDidAppear(_ animated: Bool) {
self.getData()
self.userEmojis()
}
func changeTableViewSize() {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "emojiSearchButtonTabbed"), object: nil)
}
}
Related
I’m using Spreadsheetview library in order to show case Jobber functionality.
There is one critical issues which is blocking project release. This is mentioned below:
Problem: Not able to display multi block content in custom cell.
Scenario: Suppose userA has task1 from 10:00 AM to 12:00 AM, userB has task2 from 10:00 AM to 11:00 AM, userC has task3 from 10:00 AM to 11:30 AM so these three task should be displayed in merged cell with one after another.
Refer below screenshot.
Code:
func spreadsheetView(_ spreadsheetView: SpreadsheetView, cellForItemAt indexPath: IndexPath) -> Cell? {
if self.jobDetails == nil {
return nil
}
. . .
//other cases handled like displaying time, date, visit person name which is not having any issue
. . .
else if case (1...(calenderData.count + 1), 2...(Constants.timeIntervals.count + 1)) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier: String(describing: ScheduleCell1.self), for: indexPath) as! ScheduleCell1
if(cell.firstBlockLabel.text != nil) || (cell.secondBlockLabel.text != nil) || (cell.thirdBlockLabel.text != nil) {
return nil
}
let visits = calenderData[indexPath.column - 1].calendarRows
for visit in visits {
let diff = findTimeDifference(firstTime: cellTime,secondTime: visit.startTime)
print("startTime: \(visit.startTime) endTime \(visit.endTime) title \(visit.title) totalBlocks \(visit.totalBlocks)")
if(diff >= -30 && diff <= 30 && diff != -1) {
switch visit.totalBlocks {
case 0,1:
cell.firstBlockLabel.isHidden = false
cell.secondBlockLabel.isHidden = true
cell.thirdBlockLabel.isHidden = true
cell.firstBlockLabel.text = "1 - case 1"
if visit.blockSerialNo == 1 {
if(visit.statusCode.caseInsensitiveCompare("completed") == .orderedSame){
cell.firstBlockLabel.attributedText = "\(visit.title)".strikeThrough()
} else {
cell.firstBlockLabel.text = "\(visit.title)"
}
cell.firstBlockLabel.backgroundColor = hexStringToUIColor(hex: visit.statusTagProp.background)
cell.firstBlockLabel.textColor = hexStringToUIColor(hex: visit.statusTagProp.text)
}
case 2:
cell.firstBlockLabel.isHidden = false
cell.secondBlockLabel.isHidden = false
cell.thirdBlockLabel.isHidden = true
cell.firstBlockLabel.text = "1 - case 2"
cell.secondBlockLabel.text = "2 - case 2"
if visit.blockSerialNo == 2 {
if(visit.statusCode.caseInsensitiveCompare("completed") == .orderedSame){
cell.secondBlockLabel.attributedText = "\(visit.title)".strikeThrough()
} else {
cell.secondBlockLabel.text = "\(visit.title)"
}
cell.secondBlockLabel.backgroundColor = hexStringToUIColor(hex: visit.statusTagProp.background)
cell.secondBlockLabel.textColor = hexStringToUIColor(hex: visit.statusTagProp.text)
}
case 3:
cell.firstBlockLabel.isHidden = false
cell.secondBlockLabel.isHidden = false
cell.thirdBlockLabel.isHidden = false
cell.firstBlockLabel.text = "1 - case 3"
cell.secondBlockLabel.text = "2 - case 3"
cell.thirdBlockLabel.text = "3 - case 3"
if visit.blockSerialNo == 3 {
if(visit.statusCode.caseInsensitiveCompare("completed") == .orderedSame){
cell.thirdBlockLabel.attributedText = "\(visit.title)".strikeThrough()
} else {
cell.thirdBlockLabel.text = "\(visit.title)"
}
cell.thirdBlockLabel.backgroundColor = hexStringToUIColor(hex: visit.statusTagProp.background)
cell.thirdBlockLabel.textColor = hexStringToUIColor(hex: visit.statusTagProp.text)
}
default:
break
}
break
}
}
return cell
}
return nil
}
class ScheduleCell1: Cell {
let firstBlockLabel = UILabel()
let secondBlockLabel = UILabel()
let thirdBlockLabel = UILabel()
let stackview = UIStackView()
let lineLabel = UILabel()
var lineYPosition: Int = 0
override var frame: CGRect {
didSet {
firstBlockLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
secondBlockLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
thirdBlockLabel.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
lineLabel.frame = bounds.insetBy(dx: 0, dy: 0)
lineLabel.frame = CGRect(x: 0, y: lineYPosition, width: 300, height: 1)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
lineLabel.frame = bounds
lineLabel.backgroundColor = .red
firstBlockLabel.textAlignment = .center
//firstBlockLabel.text = "firstBlockLabel"
firstBlockLabel.numberOfLines = 0
firstBlockLabel.lineBreakMode = .byTruncatingTail
firstBlockLabel.translatesAutoresizingMaskIntoConstraints = false
secondBlockLabel.textAlignment = .center
//secondBlockLabel.text = "secondBlockLabel"
secondBlockLabel.numberOfLines = 0
secondBlockLabel.lineBreakMode = .byTruncatingTail
secondBlockLabel.translatesAutoresizingMaskIntoConstraints = false
thirdBlockLabel.textAlignment = .center
//thirdBlockLabel.text = "thirdBlockLabel"
thirdBlockLabel.numberOfLines = 0
thirdBlockLabel.lineBreakMode = .byTruncatingTail
thirdBlockLabel.translatesAutoresizingMaskIntoConstraints = false
stackview.frame = bounds
stackview.axis = .horizontal
stackview.spacing = .leastNonzeroMagnitude
stackview.contentMode = .scaleToFill
stackview.translatesAutoresizingMaskIntoConstraints = false
stackview.alignment = .fill
stackview.distribution = .fill
stackview.distribution = .fillProportionally
stackview.addArrangedSubview(firstBlockLabel)
stackview.addArrangedSubview(secondBlockLabel)
stackview.addArrangedSubview(thirdBlockLabel)
firstBlockLabel.backgroundColor = .yellow
secondBlockLabel.backgroundColor = .purple
thirdBlockLabel.backgroundColor = .green
stackview.backgroundColor = .magenta
//contentView.backgroundColor = .magenta
contentView.addSubview(lineLabel)
contentView.bringSubviewToFront(lineLabel)
contentView.addSubview(stackview)
stackview.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
stackview.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
So I’ve added two property totalBlocks (determines how may blocks to be displayed) and BlockSrNo (determines which serial number of block). Logic for this is mentioned below:
func determineMultiBlocks() {
for data in calenderData {
let visits = data.calendarRows
for var i in 0..<visits.count {
let visit = visits[i]
for var j in (i+1)..<visits.count {
let nextVisit = visits[j]
let timeOverlapExists = CheckIfTimeExistBetweenTwoTimeInterval(withStartTime: visit.startTime.timeInSeconds, withEndTime: visit.endTime.timeInSeconds, withTimeToCheck: nextVisit.startTime.timeInSeconds)
if timeOverlapExists {
visit.totalBlocks = visit.totalBlocks + 1
nextVisit.totalBlocks = 0 //nextVisit.totalBlocks - 1
nextVisit.blockSerialNo = visit.totalBlocks
j = j + 1
}
}
break
}
Can help me where am I going wrong? If there is any other solution instead of using totalBlocks/blockSerialNo then let me know.
Appreciate all solutions!
I new to AsyncDisplayKit. So, I create a new app to learn AsyncDisplayKit animationTransition based on my real project code.
The show / hide animation works perfect, but i dont know why the parent node (ASDisplayNode) is not readjusting the layout when the children's hidden (Sorry if my english is bad)
i already tried to put setNeedsLayout() on transitionLayout measurementCompletion, but nothing change
import AsyncDisplayKit
class HomeView: ASDisplayNode {
let topWrapperNode: TopWrapperNode
let loginButtonNode: LoginButtonNode
override required init() {
self.topWrapperNode = TopWrapperNode()
self.loginButtonNode = LoginButtonNode()
super.init()
self.automaticallyManagesSubnodes = true
self.automaticallyRelayoutOnSafeAreaChanges = true
self.insetsLayoutMarginsFromSafeArea = true
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let verticalStackSpec = ASStackLayoutSpec.vertical()
verticalStackSpec.children = [
self.topWrapperNode,
self.loginButtonNode
]
verticalStackSpec.alignItems = .stretch
verticalStackSpec.justifyContent = .spaceBetween
let displayInset = ASInsetLayoutSpec(
insets: UIEdgeInsets(top: 0, left: 32, bottom: 16, right: 32),
child: verticalStackSpec
)
return ASInsetLayoutSpec(insets: safeAreaInsets, child: displayInset)
}
func keyboardShowUpdateLayout(keyboardHeight: CGFloat) {
// self.topWrapperNode.hideWelcomeLabelNode()
}
func keyboardHideUpdateLayout() {
// self.topWrapperNode.showWelcomeLabelNode()
}
}
// MARK - TopWrapperNode
class TopWrapperNode: ASDisplayNode {
let welcomeLabelNode: WelcomeLabelNode
// let textFieldNode: TextFieldNode
override required init() {
welcomeLabelNode = WelcomeLabelNode()
// textFieldNode = TextFieldNode()
super.init()
self.automaticallyManagesSubnodes = true
self.autoresizesSubviews = true
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let verticalStackSpec = ASStackLayoutSpec.vertical()
verticalStackSpec.children = [
self.logoImage,
self.welcomeLabelNode,
// self.textFieldNode,
]
verticalStackSpec.alignItems = .stretch
verticalStackSpec.justifyContent = .spaceBetween
self.backgroundColor = .yellow
let displayInset = ASInsetLayoutSpec(
insets: UIEdgeInsets(top: 24, left: 0, bottom: 0, right: 0),
child: verticalStackSpec
)
return ASInsetLayoutSpec(insets: safeAreaInsets, child: displayInset)
}
private let logoImage: ASImageNode = {
let imageNode = ASImageNode()
imageNode.image = UIImage(named: "logo")
imageNode.frame.size = CGSize(
width: CGFloat(SizeScaler().moderateScale(size: 98)),
height: CGFloat(SizeScaler().moderateScale(size: 48))
)
imageNode.contentMode = .scaleAspectFill
imageNode.style.alignSelf = .center
return imageNode
}()
func hideWelcomeLabelNode() {
self.welcomeLabelNode.setHide(visibility: true)
self.welcomeLabelNode.transitionLayout(withAnimation: true, shouldMeasureAsync: false)
}
func showWelcomeLabelNode() {
self.welcomeLabelNode.setHide(visibility: false)
self.welcomeLabelNode.transitionLayout(withAnimation: true, shouldMeasureAsync: false)
}
}
// MARK: - WelcomeLabel
class WelcomeLabelNode: ASDisplayNode {
var isHide: Bool = false
override required init() {
super.init()
self.automaticallyManagesSubnodes = true
self.autoresizesSubviews = true
self.shouldAnimateSizeChanges = true
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let verticalStackSpec = ASStackLayoutSpec.vertical()
verticalStackSpec.children = [self.welcomeTitleLabel, self.welcomeDescLabel]
verticalStackSpec.alignItems = .start
self.backgroundColor = .green
return ASInsetLayoutSpec(
insets: UIEdgeInsets(top: 0, left: 0, bottom: 40, right: 0),
child: verticalStackSpec
)
}
override func animateLayoutTransition(_ context: ASContextTransitioning) {
if (self.isHide) {
let initialTitle = context.initialFrame(for: self.welcomeTitleLabel)
let initialDesc = context.initialFrame(for: self.welcomeDescLabel)
self.welcomeTitleLabel.alpha = 1
self.welcomeTitleLabel.frame = initialTitle
self.welcomeDescLabel.alpha = 1
self.welcomeDescLabel.frame = initialDesc
var finalTitle = context.finalFrame(for: self.welcomeTitleLabel)
finalTitle.origin.y -= 50
var finalDesc = context.finalFrame(for: self.welcomeDescLabel)
finalDesc.origin.y -= 50
UIView.animate(withDuration: 0.4, animations: {
self.welcomeTitleLabel.alpha = 0
self.welcomeTitleLabel.frame = finalTitle
self.welcomeDescLabel.alpha = 0
self.welcomeDescLabel.frame = finalDesc
}, completion: { finished in
context.completeTransition(finished)
})
} else {
var finalTitle = context.finalFrame(for: self.welcomeTitleLabel)
finalTitle.origin.y -= 50
var finalDesc = context.finalFrame(for: self.welcomeDescLabel)
finalDesc.origin.y -= 50
self.welcomeTitleLabel.alpha = 0
self.welcomeTitleLabel.frame = finalTitle
self.welcomeDescLabel.alpha = 0
self.welcomeDescLabel.frame = finalDesc
let initialTitle = context.initialFrame(for: self.welcomeTitleLabel)
let initialDesc = context.initialFrame(for: self.welcomeDescLabel)
UIView.animate(withDuration: 0.4, animations: {
self.welcomeTitleLabel.alpha = 1
self.welcomeTitleLabel.frame = initialTitle
self.welcomeDescLabel.alpha = 1
self.welcomeDescLabel.frame = initialDesc
}, completion: { finished in
context.completeTransition(finished)
})
}
}
let welcomeTitleLabel: QlueWorkLabel = {
let label = QlueWorkLabel()
label.setFont34(text: "Selamat datang!", fontType: "medium")
label.textContainerInset = UIEdgeInsets(top: 32, left: 0, bottom: 8, right: 0)
label.style.flexGrow = 1
label.style.flexShrink = 1
label.backgroundColor = .cyan
return label
}()
let welcomeDescLabel: QlueWorkLabel = {
let label = QlueWorkLabel()
label.setFont16or20(
text: "Pantau pekerjaanmu lebih mudah dengan QlueWork",
fontType: "regular"
)
label.style.flexGrow = 1
label.style.flexShrink = 1
label.backgroundColor = .blue
return label
}()
func setHide(visibility: Bool) {
self.isHide = visibility
}
}
i expect the parent node readjusting layout when the children is hide / show like the flexBox should be.
Can anyone help me or tell me why did i do wrong?
after rendering complete, you cannot expect parentNode to adjust itself with changing its child dimension
but you can do work arround with ask parentNode to re-render itself
like this
DispatchQueue.main.async{
parentNode.transitionLayout(withAnimation: false,
shouldMeasureAsync: true,
measurementCompletion: nil)
}
make sure to run transitionLayout on main thread
Happy Texturing
I made the function updateItems() which create, from an array, many UIView's in a UIScrollView :
Here is the file where this function is :
class MainViewController: UIViewController {
#IBOutlet weak var body: UIScrollView!
#IBOutlet weak var edit: UIButton!
var _title: String = "Title"
var _isEditing: Bool = false
var firstItems: [UISectionView] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.topItem?.title = self._title
navigationController?.navigationItem.largeTitleDisplayMode = .automatic
body.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height + 100)
self.updateItems(self.firstItems)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func updateItems(_ s: [UISectionView]) {
let topMargin = 10
let rightMargin = 10
let leftMargin = 10
let space = 5
let heightItem = 60
var b = topMargin
for i in body.subviews {
i.removeFromSuperview()
}
for t in s {
if t.isHidden == true {
continue
}
if t.title != nil {
let f = UIFont(name: "Helvetica", size: 20)
let l = UILabel(frame: CGRect(x: rightMargin, y : b, width: Int(UIScreen.main.bounds.width) - (rightMargin + leftMargin), height: Int(f!.lineHeight)))
l.font = f
l.text = t.title
body.addSubview(l)
b = b + Int(f!.lineHeight) + space
}
for i in t.items{
body.addSubview(i.getView(frame: CGRect(x: rightMargin, y: b, width: Int(UIScreen.main.bounds.width) - (rightMargin + leftMargin), height: heightItem), view: self))
b = b + heightItem + space
}
}
}
}
TIPS : UISectionView is an object which contains an array of UIItemView
The object UIItemView looks like :
class UIItemView {
var icon: UIImage = UIImage();
var line1: rString = rString("")!;
var line2: rString = rString("")!;
var leftline: Any = String();
var background: String = "white"
var onItemTap: (_ sender: UITapGestureRecognizer?) -> () = {sender in }
var onItemLongPress: (_ sender: UILongPressGestureRecognizer?) -> () = {sender in }
var id: String
init?(_ id: String) {
self.id = id
}
public func getView(frame: CGRect, view: UIViewController) -> UIView {
let width = Int(frame.width)
let height = Int(frame.height)
let rightMargin = 20
let leftMargin = 10
let topMargin = 10
let bottomMargin = 10
let iconSide = height - (topMargin + bottomMargin)
let marginLine = leftMargin + iconSide + 10
let v = UIView(frame: frame)
//Background & shape
if self.background == "white" {
v.backgroundColor = UIColor.white;
} else if self.background == "blur" {
let bEV = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.extraLight))
bEV.frame = v.bounds
bEV.autoresizingMask = [.flexibleWidth, .flexibleHeight]
v.addSubview(bEV)
}
v.layer.cornerRadius = 10.0
//Icon
let i = UIImageView()
i.image = self.icon;
i.frame = CGRect(x: leftMargin, y: topMargin, width: iconSide, height: iconSide)
v.addSubview(i)
//First Line
let l1 = self.line1.getLabel()
l1.frame = CGRect(x: marginLine, y: topMargin, width: width - (marginLine + leftMargin), height: Int(self.line1.getFont().lineHeight))
v.addSubview(l1)
//Seconde Line
let l2 = self.line2.getLabel()
l2.frame = CGRect(x: marginLine, y: height - (bottomMargin + Int(self.line1.getFont().lineHeight)), width: width - (marginLine + leftMargin), height: Int(self.line1.getFont().lineHeight))
v.addSubview(l2)
//Left Line
if type(of: self.leftline) == type(of: SpinnerView()) {
let sv = (self.leftline as! SpinnerView)
sv.frame = CGRect(x: width - (rightMargin + iconSide), y: height/2 - iconSide/2, width: iconSide, height: iconSide)
v.addSubview(sv)
} else if type(of: self.leftline) == type(of: rString("")) {
let rS = (self.leftline as! rString)
if rS.text != "" {
rS.fontName = "HelveticaNeue-Bold"
rS.size = 15
rS.color = UIColor(red:0.01, green:0.48, blue:1.00, alpha:1.0)
let l3 = rS.getLabel()
l3.frame = CGRect(x: width - (rightMargin + Int(rS.getFont().lineWidth(rS.text)) + 15), y: height/2 - (Int(rS.getFont().lineHeight) + 10)/2, width: Int(rS.getFont().lineWidth(rS.text)) + 15, height: Int(rS.getFont().lineHeight) + 10)
l3.backgroundColor = UIColor(red:0.94, green:0.94, blue:0.97, alpha:1.0)
l3.layer.masksToBounds = true
l3.layer.borderWidth = 2
l3.layer.borderColor = UIColor(red:0.94, green:0.94, blue:0.97, alpha:1.0).cgColor
l3.layer.cornerRadius = rS.getFont().lineHeight/1.2
l3.textAlignment = .center
v.addSubview(l3)
}
}
//GestureRecognizer
v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.oIT(_:))))
v.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.oILP(_:))))
v.restorationIdentifier = self.id
return v;
}
#objc func oIT(_ sender: UITapGestureRecognizer) {
print("Tap")
self.onItemTap(sender)
}
#objc func oILP(_ sender: UILongPressGestureRecognizer) {
print("LongPress")
self.onItemLongPress(sender)
}
static func ==(lhs: UIItemView, rhs: UIItemView) -> Bool {
return lhs === rhs
}
}
TIPS : UIItemView contains the function getView() which returns a specific UIView
The problem :
Everything work properly, when I load the ViewController (where there is the UIScrollView) every UIView's are build like I want, and I can interact with the UIView by the UITapGestureRecognizer or the UILongPressGestureRecognizer (the function is called as expected)
BUT
When I call the function updateItems() a second time, without reload the ViewController, the items change as expected but the UITapGestureRecognizer and the UILongPressGestureRecognizer don't work any more.
I hope you can help me :D
If information are missing for you to understand the problem, please let me know ;)
I'm using Swiftpages. When app is opened it looks like first picture.
But app goes to background and opened different app on device, after open again my app it looks like second picture.
I updated to Swift 3, but I can't figure out the issue, I write about it on Github but no reply from them.
public class SwiftPages: UIView {
private lazy var token = 0
var containerVieww: UIView!
private var scrollView: UIScrollView!
private var topBar: UIView!
var animatedBar: UIView!
var viewControllerIDs = [String]()
private var buttonTitles = [String]()
private var buttonImages = [UIImage]()
private var pageViews = [UIViewController?]()
private var currentPage: Int = 0
// Container view position variables
private var xOrigin: CGFloat = 0
private var yOrigin: CGFloat = 64
private var distanceToBottom: CGFloat = 0
// Color variables
private var animatedBarColor = UIColor(red: 28/255, green: 95/255, blue: 185/255, alpha: 1)
private var topBarBackground = UIColor.white
private var buttonsTextColor = UIColor.gray
private var containerViewBackground = UIColor.white
// Item size variables
private var topBarHeight: CGFloat = 52
private var animatedBarHeight: CGFloat = 3
// Bar item variables
private var aeroEffectInTopBar = false //This gives the top bap a blurred effect, also overlayes the it over the VC's
private var buttonsWithImages = false
var barShadow = true
private var shadowView : UIView!
private var shadowViewGradient = CAGradientLayer()
private var buttonsTextFontAndSize = UIFont(name: "HelveticaNeue-Light", size: 20)!
private var blurView : UIVisualEffectView!
private var barButtons = [UIButton?]()
// MARK: - Positions Of The Container View API -
public func setOriginX (origin : CGFloat) { xOrigin = origin }
public func setOriginY (origin : CGFloat) { yOrigin = origin }
public func setDistanceToBottom (distance : CGFloat) { distanceToBottom = distance }
// MARK: - API's -
public func setAnimatedBarColor (color : UIColor) { animatedBarColor = color }
public func setTopBarBackground (color : UIColor) { topBarBackground = color }
public func setButtonsTextColor (color : UIColor) { buttonsTextColor = color }
public func setContainerViewBackground (color : UIColor) { containerViewBackground = color }
public func setTopBarHeight (pointSize : CGFloat) { topBarHeight = pointSize}
public func setAnimatedBarHeight (pointSize : CGFloat) { animatedBarHeight = pointSize}
public func setButtonsTextFontAndSize (fontAndSize : UIFont) { buttonsTextFontAndSize = fontAndSize}
public func enableAeroEffectInTopBar (boolValue : Bool) { aeroEffectInTopBar = boolValue}
public func enableButtonsWithImages (boolValue : Bool) { buttonsWithImages = boolValue}
public func enableBarShadow (boolValue : Bool) { barShadow = boolValue}
override public func draw(_ rect: CGRect) {
DispatchQueue.main.async {
let pagesContainerHeight = self.frame.height - self.yOrigin - self.distanceToBottom
let pagesContainerWidth = self.frame.width
// Set the notifications for an orientation change & BG mode
let defaultNotificationCenter = NotificationCenter.default
defaultNotificationCenter.addObserver(self, selector: #selector(SwiftPages.applicationWillEnterBackground), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
defaultNotificationCenter.addObserver(self, selector: #selector(SwiftPages.orientationWillChange), name: NSNotification.Name.UIApplicationWillChangeStatusBarOrientation, object: nil)
defaultNotificationCenter.addObserver(self, selector: #selector(SwiftPages.orientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
defaultNotificationCenter.addObserver(self, selector: #selector(SwiftPages.applicationWillEnterForeground), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
// Set the containerView, every item is constructed relative to this view
self.containerVieww = UIView(frame: CGRect(x: self.xOrigin, y: self.yOrigin, width: pagesContainerWidth, height: pagesContainerHeight))
self.containerVieww.backgroundColor = self.containerViewBackground
self.containerVieww.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.containerVieww)
//Add the constraints to the containerView.
if #available(iOS 9.0, *) {
let horizontalConstraint = self.containerVieww.centerXAnchor.constraint(equalTo: self.centerXAnchor)
let verticalConstraint = self.containerVieww.centerYAnchor.constraint(equalTo: self.centerYAnchor)
let widthConstraint = self.containerVieww.widthAnchor.constraint(equalTo: self.widthAnchor)
let heightConstraint = self.containerVieww.heightAnchor.constraint(equalTo: self.heightAnchor)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
// Set the scrollview
if self.aeroEffectInTopBar {
self.scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.containerVieww.frame.size.width, height: self.containerVieww.frame.size.height))
} else {
self.scrollView = UIScrollView(frame: CGRect(x: 0, y: self.topBarHeight, width: self.containerVieww.frame.size.width, height: self.containerVieww.frame.size.height - self.topBarHeight))
}
self.scrollView.isPagingEnabled = true
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
self.scrollView.delegate = self
self.scrollView.backgroundColor = UIColor.clear
self.scrollView.contentOffset = CGPoint(x: 0, y: 0)
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.isScrollEnabled = false
self.containerVieww.addSubview(self.scrollView)
// Add the constraints to the scrollview.
if #available(iOS 9.0, *) {
let leadingConstraint = self.scrollView.leadingAnchor.constraint(equalTo: self.containerVieww.leadingAnchor)
let trailingConstraint = self.scrollView.trailingAnchor.constraint(equalTo: self.containerVieww.trailingAnchor)
let topConstraint = self.scrollView.topAnchor.constraint(equalTo: self.containerVieww.topAnchor)
let bottomConstraint = self.scrollView.bottomAnchor.constraint(equalTo: self.containerVieww.bottomAnchor)
NSLayoutConstraint.activate([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
}
// Set the top bar
self.topBar = UIView(frame: CGRect(x: 0, y: 0, width: self.containerVieww.frame.size.width, height: self.topBarHeight))
self.topBar.backgroundColor = self.topBarBackground
if self.aeroEffectInTopBar {
// Create the blurred visual effect
// You can choose between ExtraLight, Light and Dark
self.topBar.backgroundColor = UIColor.clear
let blurEffect: UIBlurEffect = UIBlurEffect(style: .light)
self.blurView = UIVisualEffectView(effect: blurEffect)
self.blurView.frame = self.topBar.bounds
self.blurView.translatesAutoresizingMaskIntoConstraints = false
self.topBar.addSubview(self.blurView)
}
self.topBar.translatesAutoresizingMaskIntoConstraints = false
self.containerVieww.addSubview(self.topBar)
// Set the top bar buttons
// Check to see if the top bar will be created with images ot text
if self.buttonsWithImages {
var buttonsXPosition: CGFloat = 0
for (index, image) in self.buttonImages.enumerated() {
let frame = CGRect(x: buttonsXPosition, y: 0, width: self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count), height: self.topBarHeight)
let barButton = UIButton(frame: frame)
barButton.backgroundColor = UIColor.clear
barButton.imageView?.contentMode = .scaleAspectFit
barButton.setImage(image, for: .normal)
barButton.tag = index
barButton.addTarget(self, action: #selector(SwiftPages.barButtonAction), for: .touchUpInside)
self.topBar.addSubview(barButton)
self.barButtons.append(barButton)
buttonsXPosition += self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count)
}
} else {
var buttonsXPosition: CGFloat = 0
for (index, title) in self.buttonTitles.enumerated() {
let frame = CGRect(x: buttonsXPosition, y: 0, width: self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count), height: self.topBarHeight)
let barButton = UIButton(frame: frame)
barButton.backgroundColor = UIColor.clear
barButton.titleLabel!.font = self.buttonsTextFontAndSize
barButton.setTitle(title, for: .normal)
barButton.setTitleColor(self.buttonsTextColor, for: .normal)
barButton.tag = index
barButton.addTarget(self, action: #selector(SwiftPages.barButtonAction), for: .touchUpInside)
self.topBar.addSubview(barButton)
self.barButtons.append(barButton)
buttonsXPosition += self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count)
}
}
// Set up the animated UIView
self.animatedBar = UIView(frame: CGRect(x: 0, y: self.topBarHeight - self.animatedBarHeight + 1, width: (self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count)) * 0.8, height: self.animatedBarHeight))
self.animatedBar.center.x = self.containerVieww.frame.size.width / CGFloat(self.viewControllerIDs.count << 1)
self.animatedBar.backgroundColor = self.animatedBarColor
self.containerVieww.addSubview(self.animatedBar)
// Add the bar shadow (set to true or false with the barShadow var)
if self.barShadow {
self.shadowView = UIView(frame: CGRect(x: 0, y: self.topBarHeight, width: self.containerVieww.frame.size.width, height: 4))
self.shadowViewGradient.frame = self.shadowView.bounds
self.shadowViewGradient.colors = [UIColor(red: 150/255, green: 150/255, blue: 150/255, alpha: 0.28).cgColor, UIColor.clear.cgColor]
self.shadowView.layer.insertSublayer(self.shadowViewGradient, at: 0)
self.containerVieww.addSubview(self.shadowView)
}
let pageCount = self.viewControllerIDs.count
// Fill the array containing the VC instances with nil objects as placeholders
for _ in 0..<pageCount {
self.pageViews.append(nil)
}
// Defining the content size of the scrollview
let pagesScrollViewSize = self.scrollView.frame.size
self.scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(pageCount), height: pagesScrollViewSize.height)
// Load the pages to show initially
self.loadVisiblePages()
// Do the initial alignment of the subViews
self.alignSubviews()
}
}
// MARK: - Initialization Functions -
public func initializeWithVCIDsArrayAndButtonTitlesArray (VCIDsArray: [String], buttonTitlesArray: [String]) {
// Important - Titles Array must Have The Same Number Of Items As The viewControllerIDs Array
if VCIDsArray.count == buttonTitlesArray.count {
viewControllerIDs = VCIDsArray
buttonTitles = buttonTitlesArray
buttonsWithImages = false
} else {
print("Initilization failed, the VC ID array count does not match the button titles array count.")
}
}
public func initializeWithVCIDsArrayAndButtonImagesArray (VCIDsArray: [String], buttonImagesArray: [UIImage]) {
// Important - Images Array must Have The Same Number Of Items As The viewControllerIDs Array
if VCIDsArray.count == buttonImagesArray.count {
viewControllerIDs = VCIDsArray
buttonImages = buttonImagesArray
buttonsWithImages = true
} else {
print("Initilization failed, the VC ID array count does not match the button images array count.")
}
}
public func loadPage(page: Int) {
// If it's outside the range of what you have to display, then do nothing
guard page >= 0 && page < viewControllerIDs.count else { return }
// Do nothing if the view is already loaded.
guard pageViews[page] == nil else { return }
print("Loading Page \(page)")
// The pageView instance is nil, create the page
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
// Look for the VC by its identifier in the storyboard and add it to the scrollview
let newPageView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewControllerIDs[page])
newPageView.view.frame = frame
scrollView.addSubview(newPageView.view)
// Replace the nil in the pageViews array with the VC just created
pageViews[page] = newPageView
}
public func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth = scrollView.frame.size.width
let page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
// Work out which pages you want to load
let firstPage = page - 1
let lastPage = page + 1
// Load pages in our range
for index in firstPage...lastPage {
loadPage(page: index)
}
}
public func barButtonAction(sender: UIButton?) {
let index = sender!.tag
let pagesScrollViewSize = scrollView.frame.size
scrollView.setContentOffset(CGPoint(x: pagesScrollViewSize.width * CGFloat(index), y: 0), animated: true)
currentPage = index
}
// MARK: - Orientation Handling Functions -
public func alignSubviews() {
let pageCount = viewControllerIDs.count
// Setup the new frames
scrollView.contentSize = CGSize(width: CGFloat(pageCount) * scrollView.bounds.size.width, height: scrollView.bounds.size.height)
topBar.frame = CGRect(x: 0, y: 0, width: containerVieww.frame.size.width, height: topBarHeight)
blurView?.frame = topBar.bounds
animatedBar.frame.size = CGSize(width: (containerVieww.frame.size.width / (CGFloat)(viewControllerIDs.count)) * 0.8, height: animatedBarHeight)
if barShadow {
shadowView.frame.size = CGSize(width: containerVieww.frame.size.width, height: 4)
shadowViewGradient.frame = shadowView.bounds
}
// Set the new frame of the scrollview contents
for (index, controller) in pageViews.enumerated() {
controller?.view.frame = CGRect(x: CGFloat(index) * scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
}
// Set the new frame for the top bar buttons
var buttonsXPosition: CGFloat = 0
for button in barButtons {
button?.frame = CGRect(x: buttonsXPosition, y: 0, width: containerVieww.frame.size.width / CGFloat(viewControllerIDs.count), height: topBarHeight)
buttonsXPosition += containerVieww.frame.size.width / CGFloat(viewControllerIDs.count)
}
}
func applicationWillEnterBackground() {
//Save the current page
currentPage = Int(scrollView.contentOffset.x / scrollView.bounds.size.width)
print("Haydar")
}
func orientationWillChange() {
//Save the current page
currentPage = Int(scrollView.contentOffset.x / scrollView.bounds.size.width)
}
func orientationDidChange() {
//Update the view
alignSubviews()
scrollView.contentOffset = CGPoint(x: CGFloat(currentPage) * scrollView.frame.size.width, y: 0)
}
func applicationWillEnterForeground() {
alignSubviews()
scrollView.contentOffset = CGPoint(x: CGFloat(currentPage) * scrollView.frame.size.width, y: 0)
initializeWithVCIDsArrayAndButtonTitlesArray(VCIDsArray: buttonTitles, buttonTitlesArray: buttonTitles)
print("ForegroundHound")
}
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let previousPage : NSInteger = currentPage
let pageWidth : CGFloat = scrollView.frame.size.width
let fractionalPage = scrollView.contentOffset.x / pageWidth
let page : NSInteger = Int(round(fractionalPage))
if (previousPage != page) {
currentPage = page;
}
}
deinit {
NotificationCenter.default.removeObserver(self)
print("deinittta")
}
}
extension SwiftPages: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Load the pages that are now on screen
loadVisiblePages()
// The calculations for the animated bar's movements
// The offset addition is based on the width of the animated bar (button width times 0.8)
let offsetAddition = (containerVieww.frame.size.width / CGFloat(viewControllerIDs.count)) * 0.1
animatedBar.frame = CGRect(x: (offsetAddition + (scrollView.contentOffset.x / CGFloat(viewControllerIDs.count))), y: animatedBar.frame.origin.y, width: animatedBar.frame.size.width, height: animatedBar.frame.size.height)
}
}
I am having an issue with memory usage. Whenever I scroll or reload the data the memory usage keeps going up and never goes down. I have a feeling that for some reason the cells are not releasing any of the items displayed.
Any help would be greatly appreciated. I am quite sure I am doing something wrong, but I can't seem to find what.
Thank you in advance
Here is the code for creating my UICollectionView:
import Foundation
import UIKit
import CoreData
import Crashlytics
public class podCollectionView : UIViewController,UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout,UIPopoverPresentationControllerDelegate {
private var collectionView : UICollectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: UICollectionViewFlowLayout()) // Initialization
private var inmates : NSArray = [];
private var context : NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext;
private var configuration = Configuration.sharedInstance;
private var titleView = UILabel();
private var database : FMDatabase = (UIApplication.sharedApplication().delegate as! AppDelegate).database!;
public override func viewDidLoad() {
getInmates();
super.viewDidLoad();
self.collectionView = podMainView(x: "1", y: "2");
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "CellCell") // UICollectionViewCell
self.collectionView.backgroundColor = UIColor.clearColor();
self.collectionView.layer.cornerRadius = 5;
self.collectionView.layer.masksToBounds = true;
self.collectionView.delegate = self // delegate : UICollectionViewDelegate
self.collectionView.dataSource = self // datasource : UICollectionViewDataSource
self.view.addSubview(self.collectionView);
self.view = self.collectionView;
self.navigationItem.setHidesBackButton(true, animated:false);
var inmateRefresh: NSTimer!
gameTimer = NSTimer.scheduledTimerWithTimeInterval(120, target: self, selector: #selector(refreshDAta), userInfo: nil, repeats: true);
}
public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.inmates.count;
}
public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let retVal : UICollectionViewCell = self.collectionView.dequeueReusableCellWithReuseIdentifier("CellCell", forIndexPath: indexPath);
retVal.prepareForReuse();
retVal.addSubview(InmateSummary(inmate: self.inmates.objectAtIndex(indexPath.item) as! NSArray,parent: self));
return retVal;
}
private func getInmates(){
var totalInmates = 0;
var inmatesInArea = 0;
var inmatesOutOfArea = 0;
self.inmates = [];
do{
let tmpInmates : NSMutableArray = [];
let rs = try self.database.executeQuery("SELECT * FROM inmates WHERE podName='"+self.configuration.currentArea+"' ORDER BY suicideBlue DESC", values: nil)
while rs.next() {
let rowData : NSMutableArray = []
let colCount = Int(rs.columnCount()) as Int;
for i in 0 ..< colCount {
rowData.addObject(rs.objectForColumnName(rs.columnNameForIndex(Int32(i))));
}
if rowData[7] as! String == "" {
inmatesInArea = inmatesInArea + 1;
}else{
inmatesOutOfArea = inmatesOutOfArea + 1;
}
totalInmates = totalInmates + 1;
tmpInmates.addObject(rowData);
}
self.inmates = tmpInmates;
} catch {
CLSLogv("Error while loading inmates in main screen \(error)%#", getVaList(["three"]));
}
let barText = String(inmatesInArea) + "/" + String(inmatesOutOfArea) + " (" + String(totalInmates) + ")";
let buttonBack: UIButton = UIButton(type: UIButtonType.Custom) as UIButton
buttonBack.frame = CGRectMake(0, 0, 100, 40)
buttonBack.setTitle(barText, forState: .Normal);
let leftBarButtonItem: UIBarButtonItem = UIBarButtonItem(customView: buttonBack)
self.navigationItem.setLeftBarButtonItem(leftBarButtonItem, animated: false)
//self.database.close();
}
public func changePod(pod:String){
self.configuration.currentArea = pod;
self.titleView.text = pod + " ▼";
self.getInmates();
self.collectionView.reloadData();
}
public func refreshDAta(){
self.database.close();
self.database.open();
let inmateFetcer = InmateFetcher();
inmateFetcer.getUpdates();
self.getInmates();
//self.inmates = [];
self.collectionView.reloadData();
NSLog("DATA REFRESHED");
}
override public func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
}
And here is the code for the views within the cells:
import Foundation
import UIKit
public class InmateSummary : UIView, UIPopoverPresentationControllerDelegate{
public var inmate:NSArray;
private var screenSize : CGRect = UIScreen.mainScreen().bounds;
private var configuration = Configuration.sharedInstance;
private var parent : podCollectionView;
init(inmate:NSArray, parent:podCollectionView){
let cellHeight = screenSize.height * 0.295;
let cellWidth = screenSize.width * 0.295;
self.inmate = inmate;
self.parent = parent;
super.init(frame: CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight));
let aSelector : Selector = #selector(InmateSummary.inmateClicked(_:));
let tapGesture = UITapGestureRecognizer(target: self, action: aSelector);
tapGesture.numberOfTapsRequired = 1;
self.addGestureRecognizer(tapGesture);
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1);
let topView = UIView(frame: CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight*0.10));
topView.backgroundColor = UIColor.grayColor();
self.addSubview(topView);
let cellLab = UILabel(frame: CGRect(x: 0, y: 0, width: cellWidth, height: cellHeight*0.10));
cellLab.text = self.inmate[3] as? String;
cellLab.textColor = UIColor.whiteColor();
cellLab.font = UIFont.init(name:"Arial-BoldMT", size: 30);
cellLab.textAlignment = NSTextAlignment.Center;
self.addSubview(cellLab);
let sexLab = UILabel(frame: CGRect(x: cellWidth*0.75, y: 0, width: cellWidth*0.25, height: cellHeight*0.10));
sexLab.text = self.inmate[1] as? String
sexLab.textColor = UIColor.whiteColor();
sexLab.textAlignment = NSTextAlignment.Center;
self.addSubview(sexLab);
let greyView = UIView(frame: CGRect(x: 0, y: cellHeight*0.10, width: cellWidth, height: cellHeight*0.70));
greyView.backgroundColor = UIColor.grayColor();
self.addSubview(greyView);
if self.inmate[7] as? String == "" {
greyView.backgroundColor = UIColor.blackColor();
}
let eventLab = UILabel(frame: CGRect(x: cellWidth*0.75, y: 0, width: cellWidth*0.25, height: cellHeight*0.10));
eventLab.text = self.inmate[7] as? String;
eventLab.textColor = UIColor.whiteColor();
eventLab.textAlignment = NSTextAlignment.Center;
eventLab.adjustsFontSizeToFitWidth = true;
greyView.addSubview(eventLab);
let classLab = UILabel(frame: CGRect(x: 0, y: 0, width: cellWidth*0.25, height: cellHeight*0.10));
classLab.text = self.inmate[4] as? String;
classLab.textAlignment = NSTextAlignment.Center;
classLab.font = UIFont.init(name:"Arial-BoldMT", size: 20);
classLab.adjustsFontSizeToFitWidth = true;
if(classLab.text == "RISK"){
classLab.textColor = UIColor.yellowColor();
}else if(classLab.text == "7CLS"){
classLab.textColor = UIColor(red: 1.0, green:0.5, blue:0.0, alpha:1);
}else if(classLab.text == "8MAX"){
classLab.textColor = UIColor(red:1.0, green:0.0, blue:0.0, alpha:0.7);
}else{
classLab.textColor = UIColor.whiteColor();
}
greyView.addSubview(classLab);
var mugshot : UIImage = UIImage();
if (self.inmate[12] as? NSData) != nil {
mugshot = UIImage(data: (self.inmate[12] as? NSData)!)!;
}else{
NSLog("Inmate without mugshot: %#",self.inmate[2] as! String);
}
let mugshotView = UIImageView(frame: CGRect(x: cellWidth*0.16, y: cellHeight*0.09, width: cellWidth*0.66, height: cellHeight*0.5));
mugshotView.image = mugshot;
greyView.addSubview(mugshotView);
let bookingNumber = UILabel(frame: CGRect(x: cellWidth*0.25, y: cellHeight*0.545, width: cellWidth*0.50, height: cellHeight*0.15));
bookingNumber.text = self.inmate[2] as? String;
bookingNumber.textColor = UIColor.whiteColor();
bookingNumber.textAlignment = NSTextAlignment.Center;
bookingNumber.font = UIFont.init(name:"Arial-BoldMT", size: 20);
bookingNumber.adjustsFontSizeToFitWidth = true;
greyView.addSubview(bookingNumber);
let nameView = UIView(frame: CGRect(x: 0, y: cellHeight*0.75, width: cellWidth, height: cellHeight*0.25));
self.addSubview(nameView);
let maxRed = UIView(frame: CGRect(x: 0, y: 0, width: cellWidth*0.33, height: cellHeight*0.115));
maxRed.backgroundColor = UIColor.blackColor();
if (self.inmate[15] as! NSNumber == 1) {
maxRed.backgroundColor = UIColor.redColor();
}
nameView.addSubview(maxRed);
let suBlue = UIView(frame: CGRect(x: cellWidth*0.33, y: 0, width: cellWidth*0.33, height: cellHeight*0.115));
suBlue.backgroundColor = UIColor.blackColor();
if (self.inmate[18] as! NSNumber == 1) {
let aSelector : Selector = #selector(InmateSummary.suicideClicked(_:));
let tapGesture = UITapGestureRecognizer(target: self, action: aSelector);
tapGesture.numberOfTapsRequired = 1;
nameView.addGestureRecognizer(tapGesture);
suBlue.backgroundColor = UIColor.blueColor();
}
nameView.addSubview(suBlue);
let segGreen = UIView(frame: CGRect(x: cellWidth*0.66, y: 0, width: cellWidth*0.33, height: cellHeight*0.115));
segGreen.backgroundColor = UIColor.blackColor();
if (self.inmate[5] as! NSNumber == 1) {
segGreen.backgroundColor = UIColor.greenColor();
}
nameView.addSubview(segGreen);
let nameLab = UILabel(frame: CGRect(x: cellWidth*0.025, y: cellHeight*0.12, width: cellWidth*0.95, height: cellHeight*0.05));
nameLab.text = self.inmate[0] as? String;
nameLab.textAlignment = NSTextAlignment.Center;
nameLab.textColor = UIColor.whiteColor();
nameLab.font = UIFont.init(name:"Arial-BoldMT", size: 20);
nameLab.adjustsFontSizeToFitWidth = true;
nameView.addSubview(nameLab);
if(self.inmate[18] as! NSNumber == 1){
//NSLog("Suicide: %#",self.inmate);
let checkLab = UILabel(frame: CGRect(x: 0, y: cellHeight*0.17, width: cellWidth, height: cellHeight*0.07));
let formatter = NSDateFormatter()
formatter.dateFormat = "MM/dd HH:mm"
let tmpDate = NSDate(timeIntervalSince1970: (self.inmate[19] as! NSString).doubleValue)
let dateString = formatter.stringFromDate(tmpDate);
checkLab.text = (self.inmate[22] as? String)! + " - " + dateString;
checkLab.textAlignment = NSTextAlignment.Center;
checkLab.textColor = UIColor.whiteColor();
checkLab.font = UIFont.init(name:"Arial", size: 18);
checkLab.adjustsFontSizeToFitWidth = true;
nameView.addSubview(checkLab);
}
if self.inmate[1] as? String == "F" {
nameView.backgroundColor = UIColor.purpleColor();
}
self.layer.cornerRadius = 5;
self.layer.borderColor = UIColor.grayColor().CGColor;
self.layer.borderWidth = 1;
self.layer.masksToBounds = true;
}
func suicideClicked(sender:UITapGestureRecognizer!){
let podsChooser = ActivityChooser(parentView:self);
podsChooser.modalPresentationStyle = .Popover;
let popoverMenuViewController = podsChooser.popoverPresentationController
popoverMenuViewController!.permittedArrowDirections = .Any
popoverMenuViewController!.delegate = self
popoverMenuViewController!.sourceView = sender.view;
self.parent.presentViewController(
podsChooser,
animated: true,
completion: nil)
}
func inmateClicked (sender:InmateSummary){
NSLog("Inmate Clicked");
let vc = SingleInmateDisplay(inmate: inmate);
self.parent.navigationController!.pushViewController(vc,animated:false);
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}