iPhone Support

This commit is contained in:
Charles Magahern
2016-07-24 00:14:04 -07:00
parent 2f5c807dc6
commit 56de300889
17 changed files with 143 additions and 59 deletions

View File

@@ -109,8 +109,8 @@
0C20E2B61C33514100BA789B /* Controllers */ = { 0C20E2B61C33514100BA789B /* Controllers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0C41EF491C34B51500AF847F /* SwitchesViewController.swift */,
0C20E2BA1C33529700BA789B /* MainViewController.swift */, 0C20E2BA1C33529700BA789B /* MainViewController.swift */,
0C41EF491C34B51500AF847F /* SwitchesViewController.swift */,
0C20E2BC1C33534F00BA789B /* VisualizationViewController.swift */, 0C20E2BC1C33534F00BA789B /* VisualizationViewController.swift */,
0CAA32781C361A2400B353CF /* WemoServer.swift */, 0CAA32781C361A2400B353CF /* WemoServer.swift */,
); );
@@ -427,7 +427,7 @@
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_OBJC_BRIDGING_HEADER = "XIONControlPanel/SupportingFiles/XIONControlPanel-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "XIONControlPanel/SupportingFiles/XIONControlPanel-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = 2; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@@ -444,7 +444,7 @@
PRODUCT_NAME = XION; PRODUCT_NAME = XION;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_OBJC_BRIDGING_HEADER = "XIONControlPanel/SupportingFiles/XIONControlPanel-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "XIONControlPanel/SupportingFiles/XIONControlPanel-Bridging-Header.h";
TARGETED_DEVICE_FAMILY = 2; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };

View File

@@ -9,7 +9,8 @@
import UIKit import UIKit
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow? var window: UIWindow?
var mainViewController: MainViewController = MainViewController() var mainViewController: MainViewController = MainViewController()
@@ -36,6 +37,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask
{ {
return UIInterfaceOrientationMask.Landscape var mask: UIInterfaceOrientationMask = .Portrait
if (UIDevice.currentDevice().userInterfaceIdiom == .Phone) {
mask = UIInterfaceOrientationMask.Portrait
} else {
mask = UIInterfaceOrientationMask.All
}
return mask
} }
} }

View File

@@ -8,7 +8,8 @@
import UIKit import UIKit
class MainViewController: UIViewController, SwitchesViewControllerDelegate { class MainViewController: UIViewController, SwitchesViewControllerDelegate
{
private var _server: WemoServer private var _server: WemoServer
private var _visualizationController: VisualizationViewController = VisualizationViewController() private var _visualizationController: VisualizationViewController = VisualizationViewController()
private var _switchesController: SwitchesViewController = SwitchesViewController() private var _switchesController: SwitchesViewController = SwitchesViewController()
@@ -53,11 +54,13 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
let bounds = self.view.bounds let bounds = self.view.bounds
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
let headerBounds = CGRect( let headerBounds = CGRect(
x: 0.0, x: 0.0,
y: 0.0, y: 0.0,
width: bounds.size.width, width: bounds.size.width,
height: rint(bounds.size.height / 10.0) height: rint(bounds.size.height / 8.0)
) )
let bodyBounds = CGRect( let bodyBounds = CGRect(
x: 0.0, x: 0.0,
@@ -76,10 +79,20 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
) )
_visualizationController.view.frame = visualizationFrame _visualizationController.view.frame = visualizationFrame
var switchesOriginX: CGFloat = 0.0
var switchesWidth: CGFloat = 0.0
if (horizontalSizeClass == .Regular) {
switchesOriginX = CGRectGetMaxX(visualizationFrame)
switchesWidth = bodyBounds.size.width - visualizationFrame.size.width
} else {
switchesOriginX = 0.0
switchesWidth = bodyBounds.size.width
}
let switchesControllerFrame = CGRect( let switchesControllerFrame = CGRect(
x: CGRectGetMaxX(visualizationFrame), x: switchesOriginX,
y: bodyBounds.origin.y, y: bodyBounds.origin.y,
width: bodyBounds.size.width - visualizationFrame.size.width, width: switchesWidth,
height: bodyBounds.size.height height: bodyBounds.size.height
) )
_switchesController.view.frame = switchesControllerFrame _switchesController.view.frame = switchesControllerFrame
@@ -104,6 +117,12 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
} }
} }
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
_updateSizeClassPresentation()
}
override func prefersStatusBarHidden() -> Bool override func prefersStatusBarHidden() -> Bool
{ {
return true return true
@@ -133,6 +152,16 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
// MARK: Internal // MARK: Internal
internal func _updateSizeClassPresentation()
{
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
if (horizontalSizeClass == .Regular) {
_visualizationController.view.hidden = false
} else {
_visualizationController.view.hidden = true
}
}
internal func _updateVisualization(animated: Bool) internal func _updateVisualization(animated: Bool)
{ {
var activatedDevicesCount = 0 var activatedDevicesCount = 0
@@ -152,6 +181,7 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
{ {
dispatch_async(dispatch_get_main_queue()) { () -> Void in dispatch_async(dispatch_get_main_queue()) { () -> Void in
self._headerView.connectionStatusView.connectivityStatus = status self._headerView.connectionStatusView.connectivityStatus = status
self._headerView.setNeedsLayout()
self._visualizationController.connectionStatus = status self._visualizationController.connectionStatus = status
} }
} }

View File

@@ -10,11 +10,13 @@ import Darwin
import Foundation import Foundation
import UIKit import UIKit
protocol SwitchesViewControllerDelegate: class { protocol SwitchesViewControllerDelegate: class
{
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice]) func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice])
} }
extension SwitchesViewControllerDelegate { extension SwitchesViewControllerDelegate
{
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice]) {} func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice]) {}
} }
@@ -30,9 +32,9 @@ class SwitchesViewController: UIViewController,
static private let collectionViewDeviceSwitchCellReuseIdentifier = "DeviceSwitchReuseID" static private let collectionViewDeviceSwitchCellReuseIdentifier = "DeviceSwitchReuseID"
static private let collectionViewActionCellReuseIdentifier = "ActionCellReuseID" static private let collectionViewActionCellReuseIdentifier = "ActionCellReuseID"
static private let collectionViewCellsSpacing: CGFloat = 5.0 static private let collectionViewCellsSpacing: CGFloat = 5.0
static private let collectionViewCellsPerRow = 3
private enum ActionCell: Int { private enum ActionCell: Int
{
case AllOn case AllOn
case AllOff case AllOff
@@ -181,9 +183,20 @@ class SwitchesViewController: UIViewController,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{ {
let spacing = SwitchesViewController.collectionViewCellsSpacing let spacing = SwitchesViewController.collectionViewCellsSpacing
let cellsPerRow = CGFloat(SwitchesViewController.collectionViewCellsPerRow) let bounds = collectionView.bounds
let dimensions = floor((collectionView.bounds.size.width / cellsPerRow) - ((spacing * (cellsPerRow - 1.0)) / cellsPerRow)) var cellsPerRow: CGFloat = 0.0
switch (self.traitCollection.horizontalSizeClass) {
case .Regular, .Compact where (bounds.size.width >= 400.0):
cellsPerRow = 3.0
break
case .Compact:
cellsPerRow = 2.0
default:
cellsPerRow = 2.0
}
let dimensions = floor((collectionView.bounds.size.width / cellsPerRow) - ((spacing * (cellsPerRow - 1.0)) / cellsPerRow))
if (indexPath.item < ActionCell.count) { if (indexPath.item < ActionCell.count) {
return CGSize(width: collectionView.bounds.size.width, height: rint(dimensions / 2.0)) return CGSize(width: collectionView.bounds.size.width, height: rint(dimensions / 2.0))
} else { } else {

View File

@@ -14,7 +14,8 @@ import UIKit
let π = CGFloat(M_PI) let π = CGFloat(M_PI)
class VisualizationViewController: UIViewController { class VisualizationViewController: UIViewController
{
private var _scene: SCNScene = SCNScene() private var _scene: SCNScene = SCNScene()
private var _sceneView: SCNView? private var _sceneView: SCNView?
private var _cameraNode: SCNNode = SCNNode() private var _cameraNode: SCNNode = SCNNode()

View File

@@ -8,14 +8,16 @@
import Foundation import Foundation
enum ConnectionStatus { enum ConnectionStatus
{
case Disconnected case Disconnected
case Connecting case Connecting
case Connected case Connected
case Error case Error
} }
class WemoServer { class WemoServer
{
private(set) var baseURL: NSURL private(set) var baseURL: NSURL
private(set) var connected: Bool = false private(set) var connected: Bool = false
@@ -108,7 +110,8 @@ class WemoServer {
} }
} }
internal class WemoOperation : NSOperation { internal class WemoOperation : NSOperation
{
var baseURL: NSURL var baseURL: NSURL
var session: NSURLSession var session: NSURLSession
@@ -121,7 +124,8 @@ internal class WemoOperation : NSOperation {
} }
} }
internal class ConnectOperation : WemoOperation { internal class ConnectOperation : WemoOperation
{
override func main() override func main()
{ {
let semaphore = Semaphore(value: 0) let semaphore = Semaphore(value: 0)
@@ -140,7 +144,8 @@ internal class ConnectOperation : WemoOperation {
} }
} }
internal class FetchDevicesOperation : WemoOperation { internal class FetchDevicesOperation : WemoOperation
{
internal(set) var devices: [WemoDevice] = [] internal(set) var devices: [WemoDevice] = []
override func main() override func main()
@@ -176,7 +181,8 @@ internal class FetchDevicesOperation : WemoOperation {
} }
} }
internal class ToggleDeviceOperation : WemoOperation { internal class ToggleDeviceOperation : WemoOperation
{
var device: WemoDevice var device: WemoDevice
var state: WemoDevice.State var state: WemoDevice.State

View File

@@ -8,13 +8,16 @@
import Foundation import Foundation
class WemoDevice: Hashable { class WemoDevice: Hashable
enum State { {
enum State
{
case Off case Off
case On case On
} }
enum Type { enum Type
{
case Switch case Switch
} }

View File

@@ -45,6 +45,8 @@
<array> <array>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>

View File

@@ -8,12 +8,14 @@
import Foundation import Foundation
enum XIONErrorCode: Int { enum XIONErrorCode: Int
{
case Unknown case Unknown
case ConnectionError case ConnectionError
} }
extension NSError { extension NSError
{
private static let XIONErrorDomain = "com.xionsf.controlpanel" private static let XIONErrorDomain = "com.xionsf.controlpanel"
class func xionError(code: XIONErrorCode) -> NSError class func xionError(code: XIONErrorCode) -> NSError

View File

@@ -7,8 +7,10 @@
import Foundation import Foundation
extension NSURL { extension NSURL
var requestParameters: [String : String]? { {
var requestParameters: [String : String]?
{
get get
{ {
let urlComponents = self.absoluteString.componentsSeparatedByString("/") let urlComponents = self.absoluteString.componentsSeparatedByString("/")

View File

@@ -8,7 +8,8 @@
import Foundation import Foundation
class Semaphore { class Semaphore
{
private var _semaphore: dispatch_semaphore_t private var _semaphore: dispatch_semaphore_t
init(value: Int) init(value: Int)

View File

@@ -8,8 +8,10 @@
import Foundation import Foundation
class StandardErrorOutputStream: OutputStreamType { class StandardErrorOutputStream: OutputStreamType
func write(string: String) { {
func write(string: String)
{
let stderr = NSFileHandle.fileHandleWithStandardError() let stderr = NSFileHandle.fileHandleWithStandardError()
stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!) stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!)
} }

View File

@@ -8,7 +8,8 @@
import UIKit import UIKit
class ConnectionStatusView: UIView { class ConnectionStatusView: UIView
{
private var _connectivityStatus: ConnectionStatus = .Disconnected private var _connectivityStatus: ConnectionStatus = .Disconnected
private var _japaneseLabel: UILabel = UILabel() private var _japaneseLabel: UILabel = UILabel()
private var _englishLabel: UILabel = UILabel() private var _englishLabel: UILabel = UILabel()

View File

@@ -10,7 +10,8 @@ import Darwin
import Foundation import Foundation
import UIKit import UIKit
class HeaderView: UIView { class HeaderView: UIView
{
var xionLogoView: XIONLogoView = XIONLogoView() var xionLogoView: XIONLogoView = XIONLogoView()
var connectionStatusView: ConnectionStatusView = ConnectionStatusView() var connectionStatusView: ConnectionStatusView = ConnectionStatusView()
@@ -58,31 +59,39 @@ class HeaderView: UIView {
) )
self.xionLogoView.frame = logoFrame self.xionLogoView.frame = logoFrame
let titleJPVerticalMargin: CGFloat = 5.0 if (self.traitCollection.horizontalSizeClass == .Compact) {
let titleLabelSize = _xionTitleLabel.sizeThatFits(bounds.size) _xionTitleLabel.hidden = true
let jpLabelSize = _xionJapaneseLabel.sizeThatFits(bounds.size) _xionJapaneseLabel.hidden = true
let totalLabelsHeight = titleLabelSize.height + titleJPVerticalMargin + jpLabelSize.height } else {
_xionTitleLabel.hidden = false
_xionJapaneseLabel.hidden = false
let titleFrame = CGRect( let titleJPVerticalMargin: CGFloat = 5.0
x: CGRectGetMaxX(logoFrame) + hpadding * 2.0, let titleLabelSize = _xionTitleLabel.sizeThatFits(bounds.size)
y: rint(bounds.size.height / 2.0 - totalLabelsHeight / 2.0), let jpLabelSize = _xionJapaneseLabel.sizeThatFits(bounds.size)
width: titleLabelSize.width, let totalLabelsHeight = titleLabelSize.height + titleJPVerticalMargin + jpLabelSize.height
height: titleLabelSize.height
)
_xionTitleLabel.frame = titleFrame
let jpTitleFrame = CGRect( let titleFrame = CGRect(
x: titleFrame.origin.x, x: CGRectGetMaxX(logoFrame) + hpadding * 2.0,
y: CGRectGetMaxY(titleFrame) + titleJPVerticalMargin, y: rint(bounds.size.height / 2.0 - totalLabelsHeight / 2.0),
width: jpLabelSize.width, width: titleLabelSize.width,
height: jpLabelSize.height + 2.0 height: titleLabelSize.height
) )
_xionJapaneseLabel.frame = jpTitleFrame _xionTitleLabel.frame = titleFrame
let connectionStatusDimensions = CGSize(width: rint((1.0 / 8.0) * bounds.size.width), height: bounds.size.height) let jpTitleFrame = CGRect(
x: titleFrame.origin.x,
y: CGRectGetMaxY(titleFrame) + titleJPVerticalMargin,
width: jpLabelSize.width,
height: jpLabelSize.height + 2.0
)
_xionJapaneseLabel.frame = jpTitleFrame
}
let connectionStatusDimensions = self.connectionStatusView.sizeThatFits(bounds.size)
let connectionStatusFrame = CGRect( let connectionStatusFrame = CGRect(
x: bounds.size.width - connectionStatusDimensions.width, x: bounds.size.width - connectionStatusDimensions.width - hpadding,
y: 0.0, y: rint(bounds.size.height / 2.0 - connectionStatusDimensions.height / 2.0),
width: connectionStatusDimensions.width, width: connectionStatusDimensions.width,
height: connectionStatusDimensions.height height: connectionStatusDimensions.height
) )

View File

@@ -8,7 +8,8 @@
import UIKit import UIKit
class SwitchIndicatorView: UIView { class SwitchIndicatorView: UIView
{
private var _status: Bool = false private var _status: Bool = false
private var _foregroundColor: UIColor = UIColor.whiteColor() private var _foregroundColor: UIColor = UIColor.whiteColor()
private var _outerCircleLayer: CAShapeLayer = CAShapeLayer() private var _outerCircleLayer: CAShapeLayer = CAShapeLayer()

View File

@@ -8,7 +8,8 @@
import UIKit import UIKit
public class WemoCellView: UICollectionViewCell { public class WemoCellView: UICollectionViewCell
{
private var _selectionOverlayView: UIView = UIView() private var _selectionOverlayView: UIView = UIView()
static private var disabledBackgroundColor = UIColor(white: 0.2, alpha: 1.0) static private var disabledBackgroundColor = UIColor(white: 0.2, alpha: 1.0)

View File

@@ -8,7 +8,8 @@
import UIKit import UIKit
class XIONLogoView: UIView { class XIONLogoView: UIView
{
private var _xionLogoImageView: UIImageView = UIImageView() private var _xionLogoImageView: UIImageView = UIImageView()
private var _logoInnerRingImageView: UIImageView = UIImageView() private var _logoInnerRingImageView: UIImageView = UIImageView()
private var _logoOuterRingImageView: UIImageView = UIImageView() private var _logoOuterRingImageView: UIImageView = UIImageView()