Difference between revisions of "Mobile App Dev 2022W: Tutorial 5"

From Soma-notes
Jump to navigation Jump to search
Line 19: Line 19:
==Questions==
==Questions==


# Why are we using a class rather than a struct?
# Why are we using a class (on line 10) rather than a struct?
#
# How are the @IBOutlet variables (lines 12-15) related to the contents of Main.storyboard?  What about the @IBAction function (line
59)?


==Tasks==
==Tasks==

Revision as of 22:45, 8 February 2022

This tutorial is still being developed.

In this tutorial you will be playing with textanalyzer-3, which implements the same text analyzer functionality as Tutorial 2's textanalyzer-1 and [Mobile App Dev 2022W: Assignment 1|Assignment 1's textanalyzer-2]. This version, however, uses UIKit/Storyboard, not SwiftUI.

Getting Started

Unlike past tutorials, here we are textanalyzer-3 supplying the contents of the entire project rather than just one file. If you have problems with this project you'll need to re-create the project as follows:

  1. Create a new iOS (not multiplatform) App. The interface should be "Storyboard" (NOT SwiftUI) and the language should be Swift.
  2. Replace the contents of ViewController.swift, either by overwriting the file or copying and pasting the code below. (Copy from the raw source file, not this page.)
  3. Replace the contents of Main.storyboard, either by overwriting the file or by copying and pasting in the source view in Xcode by right clicking on "Main" in the Navigator pane and selecting Open As -> Source Code.

Key Concepts & APIs

While there are web pages on UIKit and Storyboard in Apple's main documentation websites, today the clear emphasis is on SwiftUI and it is much harder to find specific documentation on these older technologies. So I mostly relied on other sources.

Appcoda has a lot of valuable resources on iOS UIKit. I finally figured out how to do menus through this article by Gabriel Theodoropoulos on UIKit additions in iOS 14, and I found Simon Ng's introduction to Storyboard layouts very helpful.

Questions

  1. Why are we using a class (on line 10) rather than a struct?
  2. How are the @IBOutlet variables (lines 12-15) related to the contents of Main.storyboard? What about the @IBAction function (line

59)?

Tasks

textanalyzer-3 Code

ViewController.swift

//
//  ViewController.swift
//  textanalyzer-3
//
//  Created by Anil Somayaji on 2/3/22.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var t: UITextField!
    @IBOutlet weak var analysisMenuButton: UIButton!
    @IBOutlet weak var analysisResult: UILabel!
    @IBOutlet weak var appTitle: UILabel!
    
    var analysisMode = "None"

//    override func viewDidLoad() {
//        super.viewDidLoad()
//    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        setupAnalysisMenu()
        updateAnalysis()
    }

    func setupAnalysisMenu() {
        var menuItems: [UIAction] = []
        
        for mode in analysis.keys {
            let item = UIAction(title: mode) {
                (action) in
                self.analysisMode = mode
                self.updateAnalysis()
            }
            menuItems.append(item)
        }
        
        let analysisMenu = UIMenu(title: "", children: menuItems)
        
        analysisMenuButton.showsMenuAsPrimaryAction = true
        analysisMenuButton.menu = analysisMenu
    }

    func updateAnalysis() {
        if let inputText = t.text {
            if let analysisFunc = analysis[analysisMode] {
                let r = analysisFunc(inputText)
                analysisResult.text = analysisMode + ": " + r
            } else {
                analysisResult.text = "Please choose an analysis mode."
            }
        }
    }
    
    @IBAction func doAnalysis(_ sender: Any) {
        updateAnalysis()
    }
}


func countUpper(s: String) -> String {
    var count = 0
    let upperCase: Set<Character> =
    ["A","B","C","D","E","F","G","H","I","J","K","L","M",
     "N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
    
    for c in s {
        if (upperCase.contains(c)) {
            count += 1
        }
    }
    
    return String(count)
}

let analysis: [String: (String) -> String] = [
    "Count": countCharacters,
    "Upper Case": countUpper
]

func countCharacters(s: String) -> String {
    let charcount = s.count
    return String(charcount)
}

Main.storyboard

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina6_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="textanalyzer_3" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="428" height="926"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Text Analyzer" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZtZ-ps-7Ye">
                                <rect key="frame" x="107.33333333333333" y="130" width="213.33333333333337" height="41"/>
                                <fontDescription key="fontDescription" type="boldSystem" pointSize="34"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Result Area" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Vv-DZ-Mly">
                                <rect key="frame" x="170.66666666666666" y="498" width="86.999999999999972" height="21"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Enter Text" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="lai-Vo-ERj">
                                <rect key="frame" x="167.66666666666666" y="446" width="92.999999999999972" height="34"/>
                                <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
                                <textInputTraits key="textInputTraits"/>
                                <connections>
                                    <action selector="doAnalysis:" destination="BYZ-38-t0r" eventType="editingChanged" id="nO1-M0-6Er"/>
                                </connections>
                            </textField>
                            <button opaque="NO" contentMode="scaleToFill" ambiguous="YES" showsMenuAsPrimaryAction="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OZw-vK-PXJ">
                                <rect key="frame" x="164.66666666666666" y="46" width="98.999999999999972" height="30"/>
                                <state key="normal" title="Analysis Menu"/>
                            </button>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <constraints>
                            <constraint firstItem="OZw-vK-PXJ" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="5v2-LY-EP2"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="1Vv-DZ-Mly" secondAttribute="bottom" constant="10" id="Qa6-s1-ne8"/>
                            <constraint firstItem="ZtZ-ps-7Ye" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="UGF-av-neF"/>
                            <constraint firstItem="OZw-vK-PXJ" firstAttribute="top" relation="lessThanOrEqual" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="10" id="XYA-0a-ubh"/>
                            <constraint firstItem="ZtZ-ps-7Ye" firstAttribute="top" relation="greaterThanOrEqual" secondItem="OZw-vK-PXJ" secondAttribute="bottom" constant="10" id="cFf-s5-fgz"/>
                            <constraint firstItem="1Vv-DZ-Mly" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="psz-eR-tP5"/>
                            <constraint firstItem="lai-Vo-ERj" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="rod-O1-UBb"/>
                            <constraint firstItem="lai-Vo-ERj" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="z38-IR-hwi"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="analysisMenuButton" destination="OZw-vK-PXJ" id="1Ec-3D-09d"/>
                        <outlet property="analysisResult" destination="1Vv-DZ-Mly" id="rO4-z7-K7C"/>
                        <outlet property="appTitle" destination="ZtZ-ps-7Ye" id="xVP-yj-fvd"/>
                        <outlet property="t" destination="lai-Vo-ERj" id="AkQ-xR-U8T"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="29.600000000000001" y="83.208395802098963"/>
        </scene>
    </scenes>
    <resources>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>