Annoying that our apps must go through a review process but Apple will happily push out their own buggy software!
I threw together this hacky script that will transform all the button bar items in a storyboard to change the icons to 'cancel'. It records the original button bar items to a file you provide. This means you can now edit the storyboard file in the ui. Except all the button bar items will be cancel icons. To restore these run the script again and the original button bar icons will be restored.
We keep all our storyboard items in one huge storyboard file so this script was not created with multiple storyboards in mind! Use at own risk 😀
import os
import termcolor
# your storyboard file here!
currentStoryboardFilename = YOUR ORIGINAL STORYBOARD FILE HERE
originalStoryboardWithMetadataFileName = A FILE NAME FOR STORING THE ORIGINAL BUTTON BAR ITEMS
# temp file that gets deleted after script is run
transformedStoryboardFileName = "transformedStoryboard"
barButtonItem = "barButtonItem"
systemItem = "systemItem"
systemItemWithEqualsAndQuote = systemItem + "=\""
requiredItems = [barButtonItem, systemItem]
cancel = "cancel"
flexibleSpace = "flexibleSpace"
fixedSpace = "fixedSpace"
systemItemIsCancel = "systemItem=\"cancel\""
idString = "id=\""
skipItems = [flexibleSpace, fixedSpace, systemItemIsCancel]
metadataHeader = "###metadata#"
def getOriginalBarButtons(storyboardWithMetadata):
isInMetadataSection = False
idList = []
# get list of (id, original line) tuples
for line in storyboardWithMetadata:
if isInMetadataSection:
idIndex = line.find(idString) + len(idString)
endQuoteIndex = line.find("\"", idIndex)
idValue = line[idIndex:endQuoteIndex]
idList.append((idValue, line))
if metadataHeader in line:
isInMetadataSection = True
return idList
def isLineABarButtonItem(line):
return all(item in line for item in requiredItems)
def isLineABarButtonToSkip(line):
return any(item in line for item in skipItems)
def getOriginalBarButtonItem(line, idList):
for idTuple in idList:
if idTuple[0] in line:
return idTuple[1]
# else
return line
def transformBarButtonToCancelIcon(line):
indexOfSystemItemValue = line.find(systemItemWithEqualsAndQuote)
indexOfSystemItemValue += len(systemItemWithEqualsAndQuote)
indexOfLastQuoteAroundSystemItemValue = line.find("\"", indexOfSystemItemValue)
firstPart = line[:indexOfSystemItemValue]
secondPart = line[indexOfLastQuoteAroundSystemItemValue:]
return firstPart + cancel + secondPart
def makeStoryboardSourceOnlyEditable():
currentStoryboardFile = open(currentStoryboardFilename, 'r')
originalStoryboardWithMetadata = open(originalStoryboardWithMetadataFileName, 'r')
transformedFile = open(transformedStoryboardFileName, 'w')
idList = getOriginalBarButtons(originalStoryboardWithMetadata)
for line in currentStoryboardFile:
if isLineABarButtonItem(line):
line = getOriginalBarButtonItem(line, idList)
transformedFile.write(line)
originalStoryboardWithMetadata.close()
currentStoryboardFile.close()
transformedFile.close()
os.remove(originalStoryboardWithMetadataFileName)
os.rename(transformedStoryboardFileName, currentStoryboardFilename)
termcolor.cprint("Transformed storyboard to source editing only mode", "green")
def makeStoryboardUIEditable():
# we don't have a copy of the original storyboard file so make a copy
originalStoryboardWithMetadata = open(originalStoryboardWithMetadataFileName, 'w')
currentStoryboardFile = open(currentStoryboardFilename, 'r')
transformedStoryboard = open(transformedStoryboardFileName, 'w')
originalItemsFooter = metadataHeader + "\n"
for line in currentStoryboardFile:
originalStoryboardWithMetadata.write(line)
# if line is a bar button item and has a systemItem then record original line at the top of the file as a comment
if isLineABarButtonItem(line) and not isLineABarButtonToSkip(line):
safeToOpenUILine = transformBarButtonToCancelIcon(line)
transformedStoryboard.write(safeToOpenUILine)
originalItemsFooter += "%s" % line
else:
transformedStoryboard.write(line)
originalStoryboardWithMetadata.write(originalItemsFooter)
originalStoryboardWithMetadata.close()
currentStoryboardFile.close()
transformedStoryboard.close()
os.rename(transformedStoryboardFileName, currentStoryboardFilename)
termcolor.cprint("Transformed storyboard to ui safe mode", "green")
if os.path.isfile(originalStoryboardWithMetadataFileName):
makeStoryboardSourceOnlyEditable()
else:
makeStoryboardUIEditable()