Profile Header with Auto Layout
MediumUIKitUIKitAccessibility 40 min read
Build the top part of a profile screen in UIKit, like in a social app. Write the layout in code with Auto Layout. No storyboards, no frame math.
+------------------------------------------+
| (avatar) Anna Lindqvist |
| @anna.codes |
| |
| iOS developer in Stockholm. I write |
| about SwiftUI, Auto Layout and good... |
| |
| [ + Follow ] |
+------------------------------------------+final class ProfileHeaderView: UIView {
var onFollowTapped: (() -> Void)?
func configure(with profile: Profile)
}
final class ProfileViewController: UIViewController {
init(profile: Profile = .sample)
}Requirements
- A round avatar (use the SF Symbol
person.crop.circle.fill), always square. - Name (bold) and username next to the avatar. The bio below. A full-width Follow button at the bottom.
- The button toggles between "Follow" and "Following" and changes its style.
- Every label wraps to as many lines as it needs. Nothing is cut with "…".
- Dynamic Type: all text grows with the user's text size, live, without a restart.
- At the accessibility text sizes, put the name and username under the avatar. There is not enough width next to it.
- Rotation: in landscape, nothing goes under the notch or the Dynamic Island.
- On iPad, the text lines do not get too long.
- If the text is too big for the screen, the user can scroll.
What a good answer handles
- Priorities. The avatar must not shrink when the name is long. Explain hugging and compression resistance.
- Custom fonts and Dynamic Type. A bold name font that still scales.
- No conflicts. No "Unable to simultaneously satisfy constraints" logs in any size or orientation.
- VoiceOver. The name is a heading. The avatar is not read as "image".
- No fixed sizes from the screen. Do not use
UIScreen.main.bounds.
Notes
- Target iOS 17 or later.
registerForTraitChangesis the iOS 17 way to react to trait changes. - Test with Xcode's Environment Overrides (text size) and by rotating the simulator.
Starter code
Copy this into a new Xcode project (or Swift Playgrounds) and build from here.
import UIKit
// MARK: - Model (done for you)
struct Profile {
let name: String
let username: String
let bio: String
var isFollowing: Bool
}
extension Profile {
static let sample = Profile(
name: "Anna Lindqvist",
username: "@anna.codes",
bio: "iOS developer in Stockholm. I write about SwiftUI, Auto Layout and good coffee. Talks, notes and side projects are linked below.",
isFollowing: false
)
}
// MARK: - Header view
final class ProfileHeaderView: UIView {
var onFollowTapped: (() -> Void)?
private let avatarView = UIImageView(image: UIImage(systemName: "person.crop.circle.fill"))
private let nameLabel = UILabel()
private let usernameLabel = UILabel()
private let bioLabel = UILabel()
private let followButton = UIButton(configuration: .filled())
override init(frame: CGRect) {
super.init(frame: frame)
// TODO: style the views and build the layout with Auto Layout.
// Support Dynamic Type. At accessibility text sizes, put the name under the avatar.
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with profile: Profile) {
// TODO: set the texts and the Follow / Following button state.
}
}
// MARK: - View controller
final class ProfileViewController: UIViewController {
private var profile: Profile
private let header = ProfileHeaderView()
init(profile: Profile = .sample) {
self.profile = profile
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Profile"
view.backgroundColor = .systemBackground
header.configure(with: profile)
// TODO: place the header (it must work in landscape and with huge text)
// and handle the follow button.
}
}
#Preview("Portrait") {
UINavigationController(rootViewController: ProfileViewController())
}
#Preview("Landscape", traits: .landscapeLeft) {
UINavigationController(rootViewController: ProfileViewController())
}Solution
Try to build it first. Then compare your design with ours.