I'm pushing to a navigation controller and the large title I have set within the shootSummary VC text attributes change without warning -- they lose their original formatting. So I've tried putting them as I've done here after the push, but that hasn't worked either. Here's my code:
guard let shootSummaryVC = storyboard?.instantiateViewController(withIdentifier: "ShootPVC") as? ShootPVC else { return }
guard let indexPath = tableView.indexPathForSelectedRow else { return }
guard let shootID = shoots?[indexPath.row].id else { return }
shootSummaryVC.id = shootID
shootSummaryVC.initialPage = 3
DispatchQueue.main.async { [self] in
navigationController?.pushViewController(shootSummaryVC, animated: true)
navigationController?.hidesNavigationBarHairline = true
navigationController?.navigationBar.topItem?.backBarButtonItem =
UIBarButtonItem(title: "Track Payments", style: .plain, target: nil, action: nil)
let p = NSMutableParagraphStyle()
p.firstLineHeadIndent = 10
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font: currentTheme.titleFontLarge, NSAttributedString.Key.foregroundColor: currentTheme.navigationBarTextColor, NSAttributedString.Key.paragraphStyle: p]
}
The largeTitleTextAttributes in my attempted solution above don't affect the title. I tried setting them within the VC being pushed to (shootSummaryVC) but that hasn't worked either. Any ideas?
Related
I am using FlagPhoneno Pod to search or select country codes with flags, by using below codes. But when comes to edit part I get full phone no to the FPNtextFeild ex- +971568573570 and by default i set country code as .AE
I want to detect and automatically change the country flag when any other country phone no fetch from json let recivedPhoneNo = self.json["phone_number"].string ?? ""
For the moment the flag is changing when select only. please show me an example method with code from same pod "FlagPhoneno" module.
I am using following code.
func setupPhoneTF(){
self.phoneTFView.layer.borderColor = UIColor.appColor.customGray.cgColor
self.phoneTFView.layer.cornerRadius = 5
self.phoneTFView.layer.borderWidth = 1
phoneTF.displayMode = .list // .picker by default
listController.setup(repository: phoneTF.countryRepository)
listController.didSelect = { [weak self] country in
self?.phoneTF.setFlag(countryCode: country.code)
}
phoneTF.delegate = self
phoneTF.font = UIFont.systemFont(ofSize: 14)
// Custom the size/edgeInsets of the flag button
phoneTF.flagButtonSize = CGSize(width: 35, height: 35)
phoneTF.flagButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
let items = [
UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.save, target: self, action: nil),
UIBarButtonItem(title: "Item 1", style: .plain, target: self, action: nil),
UIBarButtonItem(title: "Item 2", style: .plain, target: self, action: nil)
]
phoneTF.textFieldInputAccessoryView = getCustomTextFieldInputAccessoryView(with: items)
phoneTF.hasPhoneNumberExample = true
phoneTF.placeholder = "Phone Number"
phoneTF.setFlag(countryCode: .AE)
}
extension AddNewAddressVC: FPNTextFieldDelegate {
func fpnDidValidatePhoneNumber(textField: FPNTextField, isValid: Bool) {
textField.rightViewMode = .whileEditing
textField.rightView = UIImageView(image: isValid ? imageLiteral(resourceName: "success") : imageLiteral(resourceName: "error"))
print(
isValid,
textField.getFormattedPhoneNumber(format: .E164) ?? "E164: nil",
textField.getFormattedPhoneNumber(format: .International) ?? "International: nil",
textField.getFormattedPhoneNumber(format: .National) ?? "National: nil",
textField.getFormattedPhoneNumber(format: .RFC3966) ?? "RFC3966: nil",
textField.getRawPhoneNumber() ?? "Raw: nil"
)
}
func fpnDidSelectCountry(name: String, dialCode: String, code: String) {
print(name, dialCode, code)
self.selectedDialCod = dialCode
}
func fpnDisplayCountryList() {
let navigationViewController = UINavigationController(rootViewController: listController)
listController.title = "Countries"
listController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(dismissCountries))
self.present(navigationViewController, animated: true, completion: nil)
}
}
// You can change the chosen flag then set the phone number
phoneNumberTextField.setFlag(for: .FR)
phoneNumberTextField.set(phoneNumber: "0600000001")
// Or directly set the phone number with country code, which will update automatically the flag image
phoneNumberTextField.set(phoneNumber: "+33600000001")
FlagPhoneNumber pod have an issue by which flag get disappear after upload and if you reinstall the ipa from testflight the flag will be gone.
I have an alert box that takes user input and starts a download task. After the user clicks "Ok," I want the screen to show a UIView that I added an ActivityIndicator to while the download occurs. The download occurs successfully and the function correctly opens up the next controller, however, the custom view nor activity indicator are ever displayed. Here's my code:
private func getKeyFromAlert() {
let alert = UIAlertController(title: "Enter Key", message: "Enter your text key below. If you want to scan it instead, click \"Scan Key.\" ", preferredStyle: .alert)
alert.addTextField { (textField) in
let attribString = NSAttributedString(string: "Enter your app code here")
textField.attributedPlaceholder = attribString
}
let scanAction = UIAlertAction(title: "Scan Key", style: .default) { _ in
self.openSettingsForApp()
}
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
let textField = alert.textFields![0]
if let text = textField.text {
let encoded = text.toBase64()
let status = APIKeychain.storeToken(encoded)
if !status {
self.displayAlertForBadKeychain(useCamera: false)
} else {
self.addLoader()
self.dismissSetup()
}
} else {
let _ = APIKeychain.storeToken("")
self.addLoader()
self.dismissSetup()
}
}
alert.addAction(scanAction)
alert.addAction(okAction)
show(alert, sender: nil)
}
The function in question that I use to display the UIView is addLoader()
The code for addLoader is:
private func addLoader() {
let frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
let loaderView = UIView(frame: frame)
loaderView.alpha = 1.0
loaderView.backgroundColor = UIColor.white
let activitySpinner: UIActivityIndicatorView = UIActivityIndicatorView(frame: loaderView.frame)
activitySpinner.center = loaderView.center
activitySpinner.hidesWhenStopped = false
activitySpinner.style = .whiteLarge
activitySpinner.startAnimating()
loaderView.addSubview(activitySpinner)
self.view.addSubview(loaderView)
self.view.layoutIfNeeded()
}
I've tried several iterations of setNeedsDisplay and setNeedsLayout without luck. I've also tried explicitly declaring this in a DispatchQueue.main.async without any affect as well.
EDIT
I added the code below for dismissSetup()
private func dismissSetup() {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
DispatchQueue.global(qos: .background).async {
if self.updateDatabase {
//This will start the download process
let _ = WebDataFetcher(dataStack: self.dataStack)
}
dispatchGroup.leave()
}
dispatchGroup.wait()
let mainSB = UIStoryboard(name: "UserInputs", bundle: nil)
let mainVC = mainSB.instantiateViewController(withIdentifier: "userInputs")
appDelegate.window?.rootViewController = mainVC
}
i want to localize my apps between English and Arabic version included RTL. but it not return to English Version with left to right after translate to Arabic, it just repeats to Arabic version when i click navigation item UIButton, also navigation item flag not show properly it is just repeating Arabic flag.
// here is my code
import UIKit
import Foundation
// homeCollectionViewController
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout{
let navLanguageBtn = UIButton()
static var language = ""
override func viewDidLoad() {
super.viewDidLoad()
setupHomeNavBarBtn()
}
// NavigationBar
func setupHomeNavBarBtn() {
navLanguageBtn.frame = CGRect(x: 0, y: 0, width: CollectionViewSize.width / 15, height: CollectionViewSize.width / 15)
navLanguageBtn.contentEdgeInsets = UIEdgeInsetsMake(0, 0, CollectionViewSize.width / 60, 0)
navLanguageBtn.setImage(UIImage(named: "arab_flag")?.withRenderingMode(.alwaysOriginal), for: .normal)
navLanguageBtn.contentMode = .scaleAspectFit
navLanguageBtn.addTarget(self, action: #selector(navLanguageBtnClick), for: .touchUpInside)
let navLanguageBtnItem = UIBarButtonItem(customView: navLanguageBtn)
self.navigationItem.setRightBarButtonItems([ navLanguageBtnItem], animated: true)
}
// ButtonClick
public func navLanguageBtnClick(){
if (navLanguageBtn.isSelected == true)
{
print("language btn click true English flag")
navLanguageBtn.setImage(UIImage(named: "english_flag")?.withRenderingMode(.alwaysOriginal), for: UIControlState.normal)
UIView.appearance().semanticContentAttribute = .forceRightToLeft
navLanguageBtn.isSelected = false
let path = Bundle.main.path(forResource: "ar-SA", ofType: "lproj")
let bundal = Bundle.init(path: path!)! as Bundle
HomeCollectionViewController.language = "ar"
navigationItem.title = bundal.localizedString(forKey: "home", value: nil, table: nil)
SecondHomeCell.newProductTrans = bundal.localizedString(forKey: "newProduct", value: nil, table: nil)
SecondHomeCell.populerStoriesTrans = bundal.localizedString(forKey: "populerStories", value: nil, table: nil)
let navController: UINavigationController? = (UIApplication.shared.keyWindow?.rootViewController as? UINavigationController)
let layout = UICollectionViewFlowLayout()
let control = HomeCollectionViewController(collectionViewLayout: layout)
navController?.pushViewController(control, animated: true)
let AR_POST_PARAMETERS = ["language": HomeCollectionViewController.language]
self.secondHomeCell?.secondHomeCellDataLoad(POST_PARAMETERS: AR_POST_PARAMETERS as! Dictionary<String, String>)
}
else
{
print("language btn click true arabic flag")
navLanguageBtn.setImage(UIImage(named: "arab_flag")?.withRenderingMode(.alwaysOriginal), for: UIControlState.selected)
UIView.appearance().semanticContentAttribute = .forceLeftToRight
// UIView.appearance().transform = CGAffineTransform(scaleX: 1.0, y: -1.0)
navLanguageBtn.isSelected = true
// self.lblCountryName.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
let path = Bundle.main.path(forResource: "en", ofType: "lproj")
let bundal = Bundle.init(path: path!)! as Bundle
//home
HomeCollectionViewController.language = "en"
navigationItem.title = bundal.localizedString(forKey: "home", value: nil, table: nil)
SecondHomeCell.newProductTrans = bundal.localizedString(forKey: "newProduct", value: nil, table: nil)
SecondHomeCell.populerStoriesTrans = bundal.localizedString(forKey: "populerStories", value: nil, table: nil)
let navController: UINavigationController? = (UIApplication.shared.keyWindow?.rootViewController as? UINavigationController)
let layout = UICollectionViewFlowLayout()
let control = HomeCollectionViewController(collectionViewLayout: layout)
navController?.pushViewController(control, animated: true)
let AR_POST_PARAMETERS = ["language": HomeCollectionViewController.language]
self.secondHomeCell?.secondHomeCellDataLoad(POST_PARAMETERS: AR_POST_PARAMETERS as! Dictionary<String, String>)
}
}
}
For changing the language of application, from a button click within the appliaction, i suggest you to ask the user to relaunch the application, for the change to happen.
In my application, i am changing the language like this.
//On Button click:
if applicationLanguage() == "ar" {
setApplicationLanguage(languageCode: "en-US")
}
else if applicationLanguage() == "en" {
setApplicationLanguage(languageCode: "ar")
}
func applicationLanguage() -> String
{
let languages : NSArray = UserDefaults.standard.object(forKey: "AppleLanguages") as! NSArray
let selectedLanguage : String = languages[0] as! String
return selectedLanguage.components(separatedBy: "-").first!
}
func setApplicationLanguage(languageCode : String)
{
// Show Alert to restart the application. Present it in your view controller.
UserDefaults.standard.set([languageCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
}
The user should manually quit the application, for the change to happen.
I am trying to get the previous/next arrow button on top of the keyboard when ever a textField is selected.
I have tried the following
textField0.tag = 0 // textField0 : TextField
textField0.addPreviousNextRightOnKeyboardWithTarget(self, rightButtonTitle: "Submit", previousAction: #selector(previousTextField(sender:)), nextAction: #selector(nextTextField(sender:)), rightButtonAction: #selector(saveNewPlan), titleText: nil)
textField1.tag = 1
textField1.addPreviousNextRightOnKeyboardWithTarget(self, rightButtonTitle: "Submit", previousAction: #selector(previousTextField(sender:)), nextAction: #selector(nextTextField(sender:)), rightButtonAction: #selector(saveNewPlan), titleText: nil)
The previous/next action selector defined as such.
func previousTextField(sender: AnyObject){
// I actually get the sender as IQBarButtonItem (subclass of UIBarButtonItem) instead of UITextField here
if let previousField = textField.superview?.viewWithTag(textField.tag - 1) as? UITextField {
_ = previousField.becomeFirstResponder()
}
}
func nextTextField(sender: AnyObject){
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
_ = nextField.becomeFirstResponder()
}
}
It's throwing this error whenever I click on the button.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[IQKeyboardManagerSwift.IQBarButtonItem superview]: unrecognized selector sent to instance 0x7fc3775658e0'
Here's what the method do.
public func addPreviousNextRightOnKeyboardWithTarget( _ target : AnyObject?, rightButtonTitle : String, previousAction : Selector, nextAction : Selector, rightButtonAction : Selector, titleText : String?) {
//If can't set InputAccessoryView. Then return
if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) {
// Creating a toolBar for phoneNumber keyboard
let toolbar = IQToolbar()
toolbar.doneTitle = rightButtonTitle
var items : [UIBarButtonItem] = []
let prev : IQBarButtonItem
let next : IQBarButtonItem
// Get the top level "bundle" which may actually be the framework
var bundle = Bundle(for: IQKeyboardManager.self)
if let resourcePath = bundle.path(forResource: "IQKeyboardManager", ofType: "bundle") {
if let resourcesBundle = Bundle(path: resourcePath) {
bundle = resourcesBundle
}
}
var imageLeftArrow : UIImage!
var imageRightArrow : UIImage!
if #available(iOS 10.0, *) {
imageLeftArrow = UIImage(named: "IQButtonBarArrowUp", in: bundle, compatibleWith: nil)
imageRightArrow = UIImage(named: "IQButtonBarArrowDown", in: bundle, compatibleWith: nil)
} else {
imageLeftArrow = UIImage(named: "IQButtonBarArrowLeft", in: bundle, compatibleWith: nil)
imageRightArrow = UIImage(named: "IQButtonBarArrowRight", in: bundle, compatibleWith: nil)
}
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
if #available(iOS 9.0, *) {
imageLeftArrow = imageLeftArrow?.imageFlippedForRightToLeftLayoutDirection()
imageRightArrow = imageRightArrow?.imageFlippedForRightToLeftLayoutDirection()
}
prev = IQBarButtonItem(image: imageLeftArrow, style: UIBarButtonItemStyle.plain, target: target, action: previousAction)
prev.accessibilityLabel = "Toolbar Previous Button"
next = IQBarButtonItem(image: imageRightArrow, style: UIBarButtonItemStyle.plain, target: target, action: nextAction)
next.accessibilityLabel = "Toolbar Next Button"
//Previous button
items.append(prev)
//Fixed space
let fixed = IQBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
if #available(iOS 10.0, *) {
fixed.width = 6
} else {
fixed.width = 20
}
items.append(fixed)
//Next button
items.append(next)
//Flexible space
items.append(UIView.flexibleBarButtonItem())
//Title button
let title = IQTitleBarButtonItem(title: shouldHidePlaceholderText == true ? nil : titleText)
items.append(title)
//Flexible space
items.append(UIView.flexibleBarButtonItem())
//Right button
let doneButton = IQBarButtonItem(title: rightButtonTitle, style: UIBarButtonItemStyle.done, target: target, action: rightButtonAction)
items.append(doneButton)
// Adding button to toolBar.
toolbar.items = items
toolbar.toolbarTitleInvocation = self.titleInvocation
// Setting toolbar to keyboard.
if let textField = self as? UITextField {
textField.inputAccessoryView = toolbar
switch textField.keyboardAppearance {
case UIKeyboardAppearance.dark:
toolbar.barStyle = UIBarStyle.black
default:
toolbar.barStyle = UIBarStyle.default
}
} else if let textView = self as? UITextView {
textView.inputAccessoryView = toolbar
switch textView.keyboardAppearance {
case UIKeyboardAppearance.dark:
toolbar.barStyle = UIBarStyle.black
default:
toolbar.barStyle = UIBarStyle.default
}
}
}
}
When I rotate the app twice after selecting a few items, it crashes. I have overridden the sendEvent method and that's where the debugger stops. When I try to print the event type, it shows me something weird (I think it's a memory location that doesn't exist):
(lldb) print event.type
(UIEventType) $R10 = <invalid> (0xff)
Somehow I think this is related to how I handle the rotation. I have a master-detail style application, that uses a different type of navigation for pad-landscape, pad-portrait and phone. I have created a class named NavigationFlowController which handles all navigational events and sets up the views accordingly. On rotation, it breaks up the view trees and recomposes them with the correct navigation
func changeViewHierarchyForDevideAndOrientation(newOrientation:UIInterfaceOrientation? = nil){
print("MA - Calling master layout method")
UIApplication.myDelegate().window?.frame = UIScreen.mainScreen().bounds
let idiom = UIDevice.currentDevice().userInterfaceIdiom
var orientation:UIInterfaceOrientation!
if let no = newOrientation{
orientation = no
}else{
orientation = UIApplication.sharedApplication().statusBarOrientation
}
print("MA - Breaking up view tree...")
breakupFormerViewTree([sidebarViewController, listViewController, detailViewController, loginViewController])
print("MA - Start init navbackbone")
initNavBackboneControllers()
guard let _ = UIApplication.myDelegate().currentUser else {
if idiom == UIUserInterfaceIdiom.Phone{
currentState = AppState.PHONE
}else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsLandscape(orientation){
currentState = AppState.PAD_LANDSCAPE
}else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsPortrait(orientation){
currentState = AppState.PAD_PORTRAIT
}
print("MA - Current user is nil - resetting")
mainViewController.addChildViewController(loginViewController)
return
}
if idiom == UIUserInterfaceIdiom.Phone{
currentState = AppState.PHONE
leftNavigationController?.viewControllers = [listViewController]
slideViewController?.rearViewController = sidebarViewController
slideViewController?.frontViewController = leftNavigationController
slideViewController?.rearViewRevealWidth = 267;
mainViewController.addChildViewController(slideViewController!)
}else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsLandscape(orientation){
currentState = AppState.PAD_LANDSCAPE
leftNavigationController!.viewControllers = [sidebarViewController, listViewController]
rightNavigationController!.viewControllers = [detailViewController]
detailViewController.navigationItem.leftBarButtonItems = []
detailViewController.initLayout()
print("MA - Init split view controller with VCs")
splitViewController!.viewControllers = [leftNavigationController!, rightNavigationController!]
mainViewController.addChildViewController(splitViewController!)
}else if idiom == UIUserInterfaceIdiom.Pad && UIInterfaceOrientationIsPortrait(orientation){
currentState = AppState.PAD_PORTRAIT
leftNavigationController!.pushViewController(sidebarViewController, animated: false)
leftNavigationController!.pushViewController(listViewController, animated: false)
rightNavigationController!.pushViewController(detailViewController, animated: false)
rightNavigationController?.setNavigationBarHidden(false, animated: false)
slideViewController!.rearViewController = leftNavigationController
slideViewController!.frontViewController = rightNavigationController
detailViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "< Documenten", style: UIBarButtonItemStyle.Bordered, target: slideViewController, action: "revealToggle:")
detailViewController.initLayout()
slideViewController!.rearViewRevealWidth = 350;
mainViewController.addChildViewController(slideViewController!)
}
}
func breakupFormerViewTree(vcs:[UIViewController?]){
for vc in vcs{
if let vcUnwrapped = vc, _ = vcUnwrapped.parentViewController {
vcUnwrapped.removeFromParentViewController()
vcUnwrapped.view.removeFromSuperview()
}
}
}
func initNavBackboneControllers(){
leftNavigationController = UINavigationController()
leftNavigationController?.navigationBar.barTintColor = UIColor(red: 0.25, green: 0.25, blue: 0.25, alpha: 1.0)
leftNavigationController?.navigationBar.tintColor = UIColor.whiteColor()
leftNavigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
leftNavigationController?.navigationBar.translucent = false
rightNavigationController = UINavigationController()
rightNavigationController?.navigationBar.barTintColor = UIColor(red: 0.25, green: 0.25, blue: 0.25, alpha: 1.0)
rightNavigationController?.navigationBar.tintColor = UIColor.whiteColor()
rightNavigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
rightNavigationController?.navigationBar.translucent = false
slideViewController = SWRevealViewController()
slideViewController?.rearViewRevealOverdraw = 0;
slideViewController?.bounceBackOnOverdraw = false;
slideViewController?.stableDragOnOverdraw = true;
slideViewController?.delegate = self
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad{
splitViewController = UISplitViewController()
}
}
EDIT (in response to Justin's questions):
1) I've experienced the crash on all iOS8 iPad simulators.
2) From a fresh start, if I select like 6-7 items and then I rotate twice, it crashes. But I can also select an item, rotate a few times, select some more and keep rotating and at some point it will crash.
3) When an item is selected, the following code is executed:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let document = getInfoForSection(indexPath.section).documents[indexPath.item]
if document.canOpen{
openDocument(document)
DataManager.sharedInstance.getDocument(document.uri, after: {
(document:Document?) -> () in
if let documentUnwrapped = document{
let detailVC = NavigationFlowController.sharedInstance.detailViewController;
if detailVC.document?.uri == documentUnwrapped.uri{
NavigationFlowController.sharedInstance.detailViewController.documentUpdated(documentUnwrapped)
}
}
})
}
}
And then in the detail view controller:
func initLayout(){
if viewForCard == nil{
// views not yet initialized, happens when initLayout if called from the document setter before this view has been loaded
// just return, the layouting will be done on viewDidLoad with the correct document instead
return
}
self.navigationItem.rightBarButtonItems = []
if document == nil{
// Removed code that handles no document selected
...
return
}
heightForCard.constant = NavigationFlowController.sharedInstance.currentState == AppState.PHONE ? CARD_HEIGHT_PHONE : CARD_HEIGHT_TABLET
viewForCard.hidden = false
removeAllSubviews(viewForCard)
removeAllSubviews(viewForDetails)
viewForDetails.translatesAutoresizingMaskIntoConstraints = false
self.metaVC?.document = document
//self.documentVC?.document = document
self.navigationItem.rightBarButtonItems = []
downloadDocumentIfNeeded()
if NavigationFlowController.sharedInstance.currentState == AppState.PAD_LANDSCAPE || NavigationFlowController.sharedInstance.currentState == AppState.PAD_PORTRAIT{
self.viewForDetails.backgroundColor = document?.senderStyling?.color
addChildViewController(self.metaVC!)
addChildViewController(self.documentVC!)
let metaView = self.metaVC!.view
let documentView:UIView = self.documentVC!.view
viewForDetails.addSubview(metaView)
viewForDetails.addSubview(documentView)
// whole lot of layouting code removed
...
let doubleTap = UITapGestureRecognizer(target: self, action: "toggleZoom")
documentVC!.view.addGestureRecognizer(doubleTap)
}else{
// Phone version code removed
...
}
}
EDIT2:
func downloadDocumentIfNeeded(){
var tmpPath:NSURL?
if let url = document?.contentUrl{
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
if let docName = self.document?.name,
safeName = disallowedCharacters?.stringByReplacingMatchesInString(docName, options: [], range: NSMakeRange(0, docName.characters.count), withTemplate: "-"){
tmpPath = directoryURL.URLByAppendingPathComponent("\(safeName)_\(DetailViewController.dateFormatter.stringFromDate(self.document!.creationDate!)).pdf")
}
if let urlString = tmpPath?.path{
if NSFileManager.defaultManager().fileExistsAtPath(urlString) {
// File is there, load it
loadDocumentInWebview(tmpPath!)
}else{
// Download file
let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
(temporaryURL, response) in
if let path = tmpPath{
return path
}
return temporaryURL
}
download(.GET, URLString: url, destination: destination).response {
(request, response, data, error) in
if error != nil && error?.code != 516{
ToastView.showToastInParentView(self.view, withText: "An error has occurred while loading the document", withDuaration: 10)
}else if let pathUnwrapped = tmpPath {
self.loadDocumentInWebview(pathUnwrapped)
}
}
}
}
}
}
func loadDocumentInWebview(path:NSURL){
if self.navigationItem.rightBarButtonItems == nil{
self.navigationItem.rightBarButtonItems = []
}
self.documentVC?.finalPath = path
let shareItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Action, target: self, action: "share")
shareItem.tag = SHARE_ITEM_TAG
addNavItem(shareItem)
}
func addNavItem(navItem:UIBarButtonItem){
var addIt = true
for item in self.navigationItem.rightBarButtonItems!{
if item.tag == navItem.tag{
addIt = false
}
}
if addIt{
self.navigationItem.rightBarButtonItems?.append(navItem)
self.navigationItem.rightBarButtonItems!.sortInPlace({ $0.tag > $1.tag })
}
}
EDIT3: I've overridden the sendEvent method to track whether or not a user is touching the app or not, but even if I take out this code, it still crashes, and the debugger then breaks on UIApplicationMain.
override func sendEvent(event: UIEvent)
{
super.sendEvent(event)
if event.type == UIEventType.Touches{
if let touches = event.allTouches(){
for item in touches{
if let touch = item as? UITouch{
if touch.phase == UITouchPhase.Began{
touchCounter++
}else if touch.phase == UITouchPhase.Ended || touch.phase == UITouchPhase.Cancelled{
touchCounter--
}
if touchCounter == 0{
receiver?.notTouching()
}
}
}
}
}
}
Tough one, a bit more insight in the events upto this bug might be helpful.
Does it happen on every device (if not, which devices gives you troubles)
It happens after "vigorously selecting" items. Did your device change orientation before that. Does it also happen before you once rotate?
What do you do in code when you "select an item".
Other then that, I'd start to get the flow of removing your child ViewControllers in breakupFormerViewTree() right. Based on the Apple Docs you want to tell the child it's being removed, before removing the view and then finally removing the child from the Parent ViewController
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
Here it actually says you want to call willMoveToParentViewController(nil) before doing the removing. It doesn't say what happens if you don't, but I can imagine the OS doing some lifecycle management there, preventing it from sending corrupt events at a later point.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/willMoveToParentViewController:
EDIT (After extra was code posted)
I don't see anything else in your code that might cause it to crash. It does look like a memory-error as you stated, but no idea where it's coming from. Try turning on Zombie objects and Guard Malloc (Scheme > Run > Diagnostics) and maybe you can get a bit more info on what's causing it.
Other then that, I'd just comment out loads of your implementation, swap Subclasses with empty ViewControllers until it doesn't happen again. You should be able to pinpoint what part of your implementation is involved in creating this event. Once you do that, well, pinpoint more and evaluate every single line of code in that implementation.