In a recent lecture, we were taught about the importance of properly preparing project files for handing them over to a client. This has been mentioned previously but here I will go into more detail about the directory itself.
Below is a screenshot of Terminal. I navigated to the directory and typed ‘ls’ which lists all of the files and sub-folders inside. Here you can see the initial organisation of the files, and the essential readme.md which contains more details about each of the files and folders. Below that I used a wonderful homebrew function called ‘tree’ which creates a file tree of the directory and all of the subdirectories and files. As there are a lot of files here, it only shows a small snippet of it, but you get the idea. The indentations are used to show the nesting of files and folders so you can easily track their locations and parent directories.
We have tried our best to minimise duplicate files. This is essential so that there is only one location of the file as to not cause any confusion when working collaboratively. However there are some unavoidable duplicates in this directory. When we copy images into the application, it then makes a copy of the file in its directory, as well as the original inside the assets folder. This is pretty much unavoidable as all the files the app needs need to be in its directory. It is important to remember that if someone decides to update one of the image assets in the assets folder, they then need to update the copy of the image in the app folder to keep it up to date.
Throughout this whole project, we made sure that we used source control (otherwise known as version control) to keep track of all the changes and files used in the app code. Recording these changes allows you to jump backwards to older code, incase something breaks or the project files are lost. It allows you to compare changes over time and track the progress of the project as it evolves.
Using source control in todays day an age is an essential professional practice as it allows you to easily work collaboratively with other people – as was done in this project with two programmers.
We put our code in a private repository on Bitbucket. This repository is on a remote server and can only be accessed by people who have permission. This is great when you want to keep your code/ project a secret, and is something we had to do as mentioned in the contract signed at the beginning of this project. We use this repository as a backup of the code. When either one of the programmers makes any changes, we are able to push them up to the repository where they will be saved.
Below is a screenshot of our Commits page on the bit bucket repository. Here you can see all of the individual commits made and pushed to the server. A commit is made after some lines of code are changes, a comment is then added so we know what the changes were in the commit – making it easier to find where to jump back to if we ever need it. It also helps us keep track of what has been done so we don’t waste time repeating processes. If you were to click on one of the commits, it would take you to a new page were you can easily see which files were changes and which likes of code were edited.
Locally, this source control was used in a couple of ways – either through Xcode itself or using Terminal. Inside of Xcode it has options for you to commit changes, push changes to the remote repository or pull changes from the repository. This is all done with a pretty graphic interface making it nice any easy to use. My personal favourite way of doing it is through Terminal, using the command line to do the same functions to source control my work.
Below is a snippet of the same commit log above, but presented through terminal.
To be blunt, using source control for our code helps us meet the learning objective:
02 Originality, creativity and professionalism in the interpretation of a live brief, production conception, pitching, management and realisation of interactive media artefacts;
It is a professional practice which allows us to work swiftly (pun definitely intended) and collaboratively on this project. While I believe we did a fairly good job at making regular commits for the changes to the code, they could’ve definitely been more regular if it was possible. One of the biggest downfalls is that a lot of the code was only written on Thursdays and Fridays while we had workshops. Within these workshops we were able to quickly get help as and when we needed it, rather than searching endlessly on the internet wasting valuable learning time. As a lot of progress was often made in short stretches of time, remember to stop and commit the changes wasn’t done as often as it should, and would usually be done at the end of the day after too many changes were made to accurately keep track of. That being said, source control has proved itself invaluable to this project and was an essential part of the whole process.
Today we encountered a large problem when trying to load the app onto an iPad for testing. The problem was that the app was using too much memory for the iPad to handle loading the app. We were testing on an iPad 2 which only has ~1gb of RAM. We had to significantly reduce how much memory it was using to get it well below this 1gb threshold as the iPad still needs to run iOS as well as the app.
Once the app started up, it was using 1.53gb of RAM to run it. We never noticed this problem before as it never had an issue running on my laptop during the development as it has 16gb of RAM. During our short time learning how to program for iOS we weren’t taught about this kind of issue and what to look out for.
Xcode has some built in debugging tools which can be used to find issues within the app. With the help of the swift magician, Marc, we were able to find where about the problem was. We saw that as soon as the app opens, the memory spikes up to an exceedingly high value and they stays there for the duration of the app running. This make us think it was something to do with the Magna Carta image in the scroll view. Our suspicion was confirmed when we changed the image file to a far smaller one and it was using significantly less memory.
As mentioned in a previous post, we already had to compress the Magna Carta image as the original file was far too large. This file was compressed down to ~188mb, about half the size of the original. Apparently this image is still far too big. When loaded into to the scrollView to display the image, the memory needed vastly increases to approximately 3 times the size. We’re not entirely sure why it uses so much memory, but if we reduce the image file size enough, it will be able to run on the iPad.
The original image we were using was using over 60,000 colours. This gave us a good level of fidelity when zooming into the image. Unfortunately we had to vastly reduce this amount to compress the image to make it usable in the app. We reduced the image down to 128 colours, saving 73% on file size. This reduced image size meant that the app only used ~380mb of RAM to run. While this is still very high, it is just small enough for the iPad to be able to run it without crashing.
The client has said that they will be purchasing an iPad so they they can use our app in the exhibit. We are hoping that they will be buying one of the newer iPads as they have more memory than the one we’ve been testing with – meaning that the app will be more stable while running.
As we come up to the final week, we’re just adding the final touches to the application to make sure that it is the best product we can provide for the client.
Last week we were given the final translations and contextualisations to use in the app from the client, so the first step was to add these into the code. After we did this, we noticed that they were significantly smaller than before. Because of this, the detailView seemed far too tall compared to the text inside it.
Originally the detailView was 500 high. This value was just an estimate size as it stuck throughout most of the development. First we tried lowering this down to 300. This worked well with some of the shorter clauses as the box didn’t drown them, but with the preface and suffixes, it seemed to be far too small and required too much scrolling. We went for the final option of 400 high. This was a good middle ground between being small enough for the shorter clause translations and big enough for the larger ones.
While doing this, I went through and cleaned up the code. This meant adding comments to functions and deleting excess line breaks to make it neater and easier to read. Commenting the functions and code is essential when handing the project over to someone else, and is always good practice to do just for myself. It is a way of always knowing what the code does and why it works/ was included. Sometimes what seemed like a simple thing to write at the time can seem complex and difficult to understand a week later. Commenting the code speeds up the workflow and is a good professional practice to get into so the code is clear and easy to read, edit and understand.
I also removed the swiftyJSON.swift file from the code. This remained in there from when I experimented with using JSON to get the translations into the code. There were also some old image files left in the project folder from when I was testing for the best solution and images to use. It is good practice remove excess files and text to keep file sizes to a minimum. Its not good to bulk out the project with assets and code which aren’t even being implemented as it makes it cluttered takes up unnecessary space.
One of the big things in iOS is having small animations for transitions. These seemingly insignificant effects make a great different to user experience within the app.
First up was the detailView. This is essentially a view which we translate its y coordinate from off screen to on. The original for this was animating with duration. This a made the view smoothly slide up from the bottom. We changed this to be a spring animation so that when it popped up, it bounced at the top of its appearance. This made it a little more fun to use and looked a lot better in our opinion.
I mentioned in another post about making the overlays fade in and out. When a category filter is chosen from the menu, the ones not in that category fade out and the ones that are in the category fade in. This is done by animating the alpha channel of the overlays when the hide or show functions are run. Similarly, when a clause is chosen to show more detail, the ones that weren’t selected fade out, and fade back in when the detailView is closed.
All these effects can be seen in the gfycat video below.
Upon playing with the app, I noticed that if you were zoomed in and then were to change the category, you might not see that anything on the app had changed as the overlays update of screen. I decided to add an animation which zoomed out the view when a category filter is chosen, this forces the user to see the changes and gives a smooth transition back to an overview of the document.
Here you can view it in action, apologies for the sideways video and shoddy filming (it was done on my phone to make things quicker)
As Magna Carta is a solid block of text, one of the ideas we’ve had from near the start is to be able to filter which overlays are on the screen. The main aim of this was to increase usability and give the user an opportunity to read certain clauses that they might be interested in (based on which filter they choose).
Below is an image of the initial state of the app before starting this process. It shows how it looks with all the clauses highlighted. While we think it actually looks pretty cool, it can sometimes be difficult to touch the icon you want without zooming in, so adding the filter option should help with this problem.
To filter the overlays, we wanted a menu that the user could pull out from the side, and it would be placed on top of the Magna Carta image. To do this we used a sideViewController (loosely following a tutorial here). The sideViewController is a separate viewController, but when it shows, it only shows on part of the screen on top of the current viewController – funnily enough this is exactly what we wanted.
To start with we started with playing with icons to act as a button for showing the menu. While doing it, we thought it could be interesting to use the cross icon from the assets made by RedBalloon – keeping with a consistent style. We put it in and realised it didn’t look as good as we had hoped. We also thought that users might not realise it is actually a button used to show the menu.
After that we went back to our original idea of using the more traditional burger icon and it was just as beautiful as we had hoped. We felt the burger icon was the best choice to use in the app as recently it has become synonymous with representing a menu or to show more details, which is what we wanted it to do.
When the sideViewController is first set up, it consists of a blank tableView. We added some colours to our tableView before we screenshot the blank version (We didn’t think it would be worth it to go back and remove the colours, so just imagine what it would look like in white).
Now it comes to the fun part – filling the tableView with information!
The prototype cell (and the colours) was set up in the ever difficult to use storyboard. While we had done many of the other things programmatically (The detailView for example), neither we or our tutors had worked with the splitViewController before so it would be easier to let Xcode hold our hand through the process. We added a label which would be used to hold the title for each filter. This label was given a unique identifier so that it could referenced in the code to populate the table.
An array of strings was created with the 15 categories used to filter the overlays, [“Key Clauses”, “Church”, “Feudal”, “Forest”, “Jews”, “Justice”, “King’s Officers”, “Miscellaneous”, “Money”, “Peace”, “Trade”, “Wales & Scotland”, “Women”, “all”, “Hide Overlays”]. Similarly, another array filled with the numbers 1-15 was created. This array would be used to references the images. All of the icon images files were names 1.png, 2.png etc. I did this because of a certain way that Swift works. When accessing an image file, the extension doesn’t needed to be added to the end (i.e. 1 would be associated with 1.png) – a specific advantage over using a .jpg file in the app. The .png files also allow for transparent backgrounds which is what we needed.
A function is used to make the length of the table the same length of the array. This make it easy if we ever need to add of remove a category from the array – making it more responsive to change and is good practice to get into rather than setting static values. After this, another function iterates through the array/table and changes the label text to be the same as the current index of the array. A similar thing is done to place the images next to the text label.
After this a function is used to similarly iterate through the table and detect which one is selected when touched. Here is where a specific function was added to get the filter action to work.
Turns out, it’s more difficult than we thought to get one viewController to communicate with another. This is where we had to show creativity and originality in our programming. We wanted to use the sideViewController to do something it wasn’t supposed to do. The usual implementation would use the menu to travel to different view controllers. However, we wanted a function on one viewController to affect something on another.
To do this we had to use NSNotificationCenter (following this tutorial). This feature works similarly to a radio broadcast tower. One view is set up to transmit a unique signal and can send data, then the other view is set up to recognise and receive this unique signal then do something with the data it receives.
The first step was creating a special notification key – something unique to this app and the data it was sending. For this I used:
let mySpecialNotificationKey = “This category was chosen”
I then made a function which would be used to send the data across. This function takes a specific type of data, an array of strings. This string is the data used to filter the clauses. I’ll get into more detail about why its an array later.
In the same function that detects which row is selected, the function is called. When a filter row is selected, the string from that row is send off in the function as the piece of data. For example, if the user tapped the “church” category. the string “church” would be sent off by the function to be received by the other viewController.
This line was then added to the original view so that it can listen out for the special notification key. Once it hears the key (when a filter is chosen) it will run a specific function as a reaction.
This is the function it runs. It has to convert the data it receives back into an array of strings so that it can be used. It then places this information into the onlyShowClausesWithCategory function so that it can search for which clauses have this category, then show them. as it is an array, it needs to know which part of the array to select, so .first is added to the end so it always selects the first item in the array. The array will always only have one item in it because only one category can be selected at once. I added a print line in the log so I could see when/if the information was being sent across.
Here is the onlyShowClausesWithCategory function which is used to filter the clauses. It looks very complicated – it is – but I will try my best to explain the basic premise of how it works and why it is needed. The function accepts one value, which is the category. This is the category which will be shown when selected. Inside the function, two arrays are made, overlaysToHide and overlaysToShow. As you can probably guess, this function will sort though the overlays to decide which ones are in the right category to be shown and which aren’t. The for loop iterates through the clauses and overlays, and searches the clause “category” array for the right category. If it finds the category in the array it will add it to the overlaysToShow array, and if it doesn’t, it goes into the overlaysToHide array. The arrays are then iterated through an animated, changing the opacity with a duration so that the hidden overlays will fade out, and the show overlays will fade in (if not already shown)
Similar to this, a function called onlyShowClause was made which has a similar sorting feature. However the difference here is that it is run when a clause is selected to see more detail. What this does is makes the ones that aren’t selected hide, and while the selected one remains. Below the two images show the Key Clauses category. Then the preface (first block of text) was selected which caused the other clauses to hide so that only one is shown.
We really liked this feature as it made it even clearer which clause the user is looking at. However there is one minor issue with this. If a clause is selected in the lower half of the screen, it be seen as still selected as the detailView is on top it of. At this point we don’t have enough time to come up with a work around as there are many other things that need fixing/ making and are more important than this small cosmetic issue.
If there was more time we would be able to play around with different ways to mitigate this issue. For example, we could make it so that if a clause on the lower half of the screen was selected, the detail view come down from the top instead. This seems simple enough but would require a lot more thinking and programming to get this to work.
Up until now we only ever had one overlay for each clause. This was because we didn’t actually know where the clauses were on the document so we built it using this method. After we got send the information of where the clauses were, we were able to mark down the coordinates and size of each overlay. As some clauses span over multiple lines, it meant that multiple lines per clause needed to be drawn – completely changing how the app functions.
Each overlay has an x & y coordinate, and a width and height. Each clause may consist of up to 4 overlays. Each overlay in each clause must go to the same detailView for the correct clause details. These were the basic steps of what was needed to get this to work.
First step was to create a “Clause” class, this has a name, a category, a colour, and the details in the detailView. Similarly, an “Overlay” class was needed which would work with the Clause class. The Overlay class has a member of Clause, where it gets it’s colour and so it knows which clause it is associated with. The overlay is also given a frame, the x & y, width and height, so that the box can be drawn on the document image.
In the main viewController an array is made where the clauses are mapped to the overlays. Before, when there was only one overlay per clause, the array was iterated through adding a tapGestureRecogniser to each overlay so that each go to different clauses. Similarly here, the overlays are iterated through and added to the clauses (I’m not entirely sure how this works, but I have a vague understanding. Marc helped a lot with this part as it was very confusing, and needed to be done quickly due to the deadline).
After this is was a matter of filling the arrays so the overlays and clauses can be made. Here is an example of what it looks like:
At the top, two variables were made to store the colours. This was done for two reasons: Firstly, to make it easier so the RGB values didn’t have to be repeatedly typed out, and secondly, so that the colour values could easily be changed without having to do it for each clause. As you can see by the comment, at this stage the colours aren’t fully decided on; we have something close to what we want but as they have different opacities, it doesn’t look precisely what we want it to look like. Minute attention to detail like this is a good professional practice to have so that small mistakes aren’t repeatedly made.
Each clause in the array has a number of details. The name is used for the title in the detailView, the category (often categories) will be used to filter the clauses once this part is created, the detail is the body text for the detailView and the colour is the colour of the overlays for that clause. Below that comes the overlays for which CGRects are used – similar to what we had before but this new method allows us to have more than one.