element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Pi IoT
  • Challenges & Projects
  • Design Challenges
  • Pi IoT
  • More
  • Cancel
Pi IoT
Blog [Pi IoT] Thuis #11: Final implementation UI design
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: rhe123
  • Date Created: 21 Aug 2016 1:36 PM Date Created
  • Views 1171 views
  • Likes 8 likes
  • Comments 3 comments
  • user-interface
  • piiot
  • Design
  • ios
  • swift
  • thuis
Related
Recommended

[Pi IoT] Thuis #11: Final implementation UI design

rhe123
rhe123
21 Aug 2016

From my holiday location at the coast of Bulgaria I finished up the implementation of the UI of Thuis. It works nicely on both the iPhones and the iPad on the wall. This blog will give you a demo and bring you up-to-date with some of the changes I made since the last post.

 

image

 

Adding the Slider Tile

I already implemented the UICollectionViewController and several tiles in the last blog post, however it was lacking the SliderTile implementation. For this several changes were needed, for example a tile should be able to span multiple columns to create enough space for the slider. For this a new property is added to Tile called columns. It now looks like this:

class Tile {
    let title: String
    var icon: UIImage?
    var value: String?

    let columns: Int

    let topics: [TopicType: String]

    init(title: String, icon: UIImage, columns: Int, topics: [TopicType: String]) { ... }
    convenience init(title: String, icon: ionicon, columns: Int, topics: [TopicType: String]) { ... }
}

 

UICollectionViewController can handle cells of different sizes,  so making sure they display correctly is luckily easy:

extension TilesCollectionViewController: UICollectionViewDelegateFlowLayout {
   func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {   
      let tile = tiles[indexPath.row]
      let height = self.view.bounds.height / 6.0

      switch (tile.columns) {
      case 1:
         return CGSize(width: self.view.bounds.width/2.0 - 12.0, height: height);
      case 2:
         return CGSize(width: self.view.bounds.width - 16.0, height: height);
      default:
         print("Not supported amount of columns for button");
         return CGSize.zero
      }
   }
}

 

Now we can actually implement the SliderTile itself. First we add the slider to the UI:

image

 

The TileCollectionViewCell class got some more functionality now. To make things easier I moved some of the logic from the TilesCollectionViewController to here. It now takes care of deciding what to show and what to hide. For example the value will be hidden when there is no value (for buttons) or when a slider is used (the slider already visualizes the value).

 

To handle changes of the value when someone uses the slider a delegate method had to be added. The slider is linked to the delegate method in the TileCollectionViewCell, but this delegates the actual work to the SliderTile. This class also has some properties defining the slider, such as the minimum and maximum allowed values, and takes care of updating the slider when a MQTT message arrives.

class SliderTile: Tile, TileCollectionViewCellDelegate, MQTTSubscriber {
    let minimumValue: Float
    let maximumValue: Float
    let rounded: Bool

    var publishDelayTimer: Timer?
    
    init(title: String, icon: UIImage?, columns: Int, topic: String, minimumValue: Float, maximumValue: Float, rounded: Bool) {
        var topics = [TopicType: String]()
        topics[.status] = topic
        topics[.set] = "\(topic)/set"
        
        self.minimumValue = minimumValue
        self.maximumValue = maximumValue
        self.rounded = rounded
        
        super.init(title: title, icon: icon, columns: columns, topics: topics)
    }

    convenience init(title: String, icon: ionicon, columns: Int, topic: String, minimumValue: Float, maximumValue: Float, rounded: Bool) {
        self.init(title: title, icon: nil, columns: columns, topic: topic, minimumValue: minimumValue, maximumValue: maximumValue, rounded: rounded)
        self.icon = icon
    }

    func didReceiveMessage(_ message: MQTTMessage) {
        if topics.values.contains(message.topic) {
            guard let payloadString = message.payloadString else {
                print("Received empty message for topic '\(message.topic)'")
                return
            }
            
            if rounded {
                if let _ = Int(payloadString) {
                    self.value = payloadString
                } else {
                    print("Received invalid message for topic '\(message.topic)': \(payloadString)")
                }
            } else {
                self.value = payloadString
            }
        }
    }
    
    func sliderValueDidChange(_ sender: UISlider!) {
        if rounded {
            value = "\(Int(sender.value))"
        } else {
            value = "\(sender.value)"
        }

        if let topic = topics[.set],
           let payloadString = value {

            // Invalidate any previous requests
            if let timer = publishDelayTimer {
                timer.invalidate()
            }

            publishDelayTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in
                MQTT.sharedInstance.publish(payloadString, topic: topic, retain: false)
            }
        }
    }
}

 

Adding a optional postfix to values

Some of the InfoTiles show the weather, and for the temperature it's a lot clearer when we include a degree sign next to the value. So I've added an optional postfix to the value of a Tile. This is a simple String property which is added at the end of the value before display:

valueLabel.text = tile?.value

if let value = tile?.value, let valuePostfix = tile?.valuePostfix {
    valueLabel.text = value + valuePostfix
}

 

Changes to support iPad

The iPhone and iPad have a very different screen size and of course we don't want to duplicate any code, or add checks everywhere. Luckily for us Apple solved most of this by providing adaptive layouts and layout constraints. We'll use the latter to slightly change the layout of the TileCollectionViewCell to adapt to the bigger screen. Apple differentiates between devices by using so-called size classes, which are very well described in The Adaptive Model. There are two changes we have to make: change the icon size and the width of the slider. The icon will be 45 points on compact-width devices and 70 on regular-width devices. The slider will get a width of 200 points on compact-width devices and 400 on regular-width devices.

 

Determining the size of the cell was already working well on all devices as it's using a size based on the size of the superview. You can see the implementation of the slider above.

 

Demo

The implementation of the design Marina made for the Thuis app is now implemented, and how better to show it then with a demo:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

In the demo you can see the different screens on both an iPad and an iPhone. At the bottom you can see the MQTT messages being send and received. Notice that when clicking a button a message is send to the set-topic (for example Thuis/device/living/moodTop/set) by the iOS app. Then a message is received on the status topic for that device (Thuis/device/living/moodTop), which is sent by Zway.

 

When a scene is triggered (in this case Mood, which are the mood lights in the living room) you see the Core sends a message for each device in that scene. The status is also reflected on the iPhone. The slider for the main light in the dining sends out messages every time when you pause for a tenth of a second.

 

This should give you a good overview of the app and its design. Off to the next use case!

  • Sign in to reply

Top Comments

  • DAB
    DAB over 9 years ago +2
    Very nice update. It looks like you have a nice simple structure to add new improvements as they come up. Well done. DAB
  • fvan
    fvan over 9 years ago +1
    Looks great, very clean!
  • clem57
    clem57 over 9 years ago +1
    The pictures are clean and useful for the text given. Clem
Parents
  • clem57
    clem57 over 9 years ago

    The pictures are clean and useful for the text given.

    Clem

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • clem57
    clem57 over 9 years ago

    The pictures are clean and useful for the text given.

    Clem

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube