This is going to show the development of my solution to the Edutainment challenge.
First I used a VStack and HStack to create a table layout.
In this blog I will share insights and information concerning software development. The target audience includes Software Engineers, Software Project personnel, Software Product personnel, Management, and Executives.
This is going to show the development of my solution to the Edutainment challenge.
First I used a VStack and HStack to create a table layout.
Place the following code in an XCode Play Ground:
import UIKit
struct Person {
let birthYear : Int
let name : String
let tag : String
}
//The build function allows a builder function to be passed in. That function
//can create a new person based on the paramaters passed in
extension Person {
static func build(birthYear: Int, name: String, tag: String, builder: (_ birthYear: Int, _ name : String, _ tag: String) -> Person) -> Person {
return builder(birthYear, name, tag)
}
}
//Functions can have argument labels and parameter names
//Argument labels and Parameter names
func personBuilderOne(theBirthYear birthYear: Int, theName name: String, theTag tag: String) -> Person {
return Person(birthYear: birthYear, name: name, tag: tag + " Builder One")
}
//No Argument labels, just parameter names
func personBuilderTwo(birthYear: Int, name: String, tag: String) -> Person {
return Person(birthYear: birthYear, name: name, tag: tag + " Builder Two")
}
//Parameter names are not needed when calling
func personBuilderThree(_ birthYear: Int, _ name: String, _ tag: String) -> Person {
return Person(birthYear: birthYear, name: name, tag: tag + " Builder Three")
}
//----------------------------------------------------------
//Notice how the signature changes for the function paramter
var person = Person.build(birthYear: 1900, name: "John", tag: "tag", builder: personBuilderOne(theBirthYear:theName:theTag:))
print(person)
person = Person.build(birthYear: 1900, name: "John", tag: "tag", builder: personBuilderTwo(birthYear:name:tag:))
print(person)
person = Person.build(birthYear: 1900, name: "John", tag: "tag", builder: personBuilderThree(_:_:_:))
print(person)
//----------------------------------------------------------
//The builder can be done by using a closure
//The closure params are contrived just to show how they are used
person = Person.build(birthYear: 1910, name: "Mary", tag: "tag") { bYear, theName, theTag in
return Person(birthYear: bYear, name: theName, tag: theTag + " Closure")
}
print(person)
Person(birthYear: 1900, name: "John", tag: "tag Builder One")
Person(birthYear: 1900, name: "John", tag: "tag Builder Two")
Person(birthYear: 1900, name: "John", tag: "tag Builder Three")
Person(birthYear: 1910, name: "Mary", tag: "tag Closure")
Here are the steps to make a Scroll View that has an embedded UIView that specifies the height of the contents of the scrollable area.
Create a new app with a View Controller.
Next place a UIView inside the Scroll View. This example will use a "Container View".
Use the sizing handles to make the Container View the same size as the Scroll View.
Now run it in the iPhone Simulator. This example uses an iPhone 8.
Drag in the simulator to see the label.
And that's it!
These are the steps to layout a StackView's contained views by constraining the height of the contained views.
Step 1: Add Vertical Stack View
Hav you have ever put a UIView in a StackView and wondered why "Fill Proportionally" wasn't working when you set the UIView's "Intrinsic Size"?
It's because the UIView doesn't let the intrinsic value be set, and therefore it can't be read.
So, I am thinking, there must be a way to add a property to the UIView via a subclass that will get the job done.
The approach is to use @IBInspectable.
Here is the source:
//
// UIViewExtended.swift
//
// Created by Geoffrey Slinker on 5/28/22.
// Copyright © 2022 Geoffrey Slinker. All rights reserved.
//
import Foundation
import UIKit
class UIViewExtended : UIView {
enum Keys: String {
case proportionalSize = "ProportionalSize"
}
var proportionalSize : CGSize
override init(frame: CGRect) {
proportionalSize = CGSize(width:1.0, height:1.0)
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
let tempSize : CGSize? = aDecoder.decodeCGSize(forKey: Keys.proportionalSize.rawValue) as? CGSize
if tempSize != nil {
proportionalSize = tempSize!
}
else {
proportionalSize = CGSize(width:1.0, height:1.0)
}
super.init(coder: aDecoder)
}
override func encode(with coder: NSCoder) {
coder.encode(self.proportionalSize, forKey: Keys.proportionalSize.rawValue)
super.encode(with: coder)
}
@IBInspectable var intrinsicSize: CGSize {
get {
return proportionalSize
}
set {
proportionalSize = newValue
}
}
override open var intrinsicContentSize : CGSize {
get {
return proportionalSize
}
}
}
Just set the intrinsic size in IB (Interface Builder). If you want to have the simulated view in IB look the same, keep the custom intrinsic size the same as the intrinsic size placeholder values. Yes, you have to manually keep the values the same, but at least you can do it all in IB.
Set the values here:
func testUIViewExtended() throws {
let view : UIViewExtended = UIViewExtended.init(frame: CGRect(x: 0,y: 0, width: 100, height: 50))
view.intrinsicSize = CGSize(width: 5,height: 5)
//Look at the xml in the debugger
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.outputFormat = .xml
archiver.encodeRootObject(view)
let someData = archiver.encodedData
let stringData = String(decoding: someData, as: UTF8.self)
let codedData = try! NSKeyedArchiver.archivedData(withRootObject: view,
requiringSecureCoding: false)
let result = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(codedData) as?
UIViewExtended
XCTAssert(result?.frame.width == 100)
XCTAssert(result?.frame.height == 50)
XCTAssert(result?.intrinsicSize.width == 5)
XCTAssert(result?.intrinsicSize.height == 5)
}
See also: