Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
Material
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
Material
Commits
77c252e5
Commit
77c252e5
authored
Jan 13, 2018
by
Orkhan Alikhanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Dialogs
parent
88afd83a
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
467 additions
and
0 deletions
+467
-0
Material.xcodeproj/project.pbxproj
+20
-0
Sources/iOS/DialogBuilder.swift
+68
-0
Sources/iOS/DialogController.swift
+83
-0
Sources/iOS/DialogView.swift
+296
-0
No files found.
Material.xcodeproj/project.pbxproj
View file @
77c252e5
...
@@ -174,6 +174,9 @@
...
@@ -174,6 +174,9 @@
96E3C39A1D3A1CC20086A024
/* ErrorTextField.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
961F18E71CD93E3E008927C5
/* ErrorTextField.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96E3C39A1D3A1CC20086A024
/* ErrorTextField.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
961F18E71CD93E3E008927C5
/* ErrorTextField.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96E3C39C1D3A1CC20086A024
/* Offset.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
968C99461D377849000074FF
/* Offset.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96E3C39C1D3A1CC20086A024
/* Offset.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
968C99461D377849000074FF
/* Offset.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96F1A5531F24F17A001D8CAF
/* TabsController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E09DC71F2287E50000B121
/* TabsController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96F1A5531F24F17A001D8CAF
/* TabsController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E09DC71F2287E50000B121
/* TabsController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9D13671A2006A8170004DE2D
/* DialogView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9D1367192006A8170004DE2D
/* DialogView.swift */
;
};
9D13671C2006A8D80004DE2D
/* DialogController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9D13671B2006A8D80004DE2D
/* DialogController.swift */
;
};
9D13671E2006A9450004DE2D
/* DialogBuilder.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9D13671D2006A9450004DE2D
/* DialogBuilder.swift */
;
};
/* End PBXBuildFile section */
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
/* Begin PBXContainerItemProxy section */
...
@@ -285,6 +288,9 @@
...
@@ -285,6 +288,9 @@
96E09DC71F2287E50000B121
/* TabsController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
TabsController.swift
;
sourceTree
=
"<group>"
;
};
96E09DC71F2287E50000B121
/* TabsController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
TabsController.swift
;
sourceTree
=
"<group>"
;
};
96E3C3931D397AE90086A024
/* Material+UIView.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"Material+UIView.swift"
;
sourceTree
=
"<group>"
;
};
96E3C3931D397AE90086A024
/* Material+UIView.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"Material+UIView.swift"
;
sourceTree
=
"<group>"
;
};
96F1DC871D654FDF0025F925
/* Material+CALayer.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"Material+CALayer.swift"
;
sourceTree
=
"<group>"
;
};
96F1DC871D654FDF0025F925
/* Material+CALayer.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"Material+CALayer.swift"
;
sourceTree
=
"<group>"
;
};
9D1367192006A8170004DE2D
/* DialogView.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
DialogView.swift
;
sourceTree
=
"<group>"
;
};
9D13671B2006A8D80004DE2D
/* DialogController.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
DialogController.swift
;
sourceTree
=
"<group>"
;
};
9D13671D2006A9450004DE2D
/* DialogBuilder.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
DialogBuilder.swift
;
sourceTree
=
"<group>"
;
};
/* End PBXFileReference section */
/* End PBXFileReference section */
/* Begin PBXGroup section */
/* Begin PBXGroup section */
...
@@ -523,6 +529,7 @@
...
@@ -523,6 +529,7 @@
96BCB8001CB40F0300C806FE
/* Color */
,
96BCB8001CB40F0300C806FE
/* Color */
,
96328B9A1E05C135009A4C90
/* Data */
,
96328B9A1E05C135009A4C90
/* Data */
,
96BCB80B1CB410CC00C806FE
/* Device */
,
96BCB80B1CB410CC00C806FE
/* Device */
,
9D1367172006A5730004DE2D
/* Dialogs */
,
96230AB61D6A51FD00AF47DC
/* Divider */
,
96230AB61D6A51FD00AF47DC
/* Divider */
,
96BCB80A1CB410A100C806FE
/* Extension */
,
96BCB80A1CB410A100C806FE
/* Extension */
,
963FBF021D6696D0008F8512
/* FABMenu */
,
963FBF021D6696D0008F8512
/* FABMenu */
,
...
@@ -729,6 +736,16 @@
...
@@ -729,6 +736,16 @@
name
=
Animation
;
name
=
Animation
;
sourceTree
=
"<group>"
;
sourceTree
=
"<group>"
;
};
};
9D1367172006A5730004DE2D
/* Dialogs */
=
{
isa
=
PBXGroup
;
children
=
(
9D1367192006A8170004DE2D
/* DialogView.swift */
,
9D13671B2006A8D80004DE2D
/* DialogController.swift */
,
9D13671D2006A9450004DE2D
/* DialogBuilder.swift */
,
);
name
=
Dialogs
;
sourceTree
=
"<group>"
;
};
/* End PBXGroup section */
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
/* Begin PBXHeadersBuildPhase section */
...
@@ -936,12 +953,14 @@
...
@@ -936,12 +953,14 @@
965E81171DD4D5C800D61E4B
/* TransitionController.swift in Sources */
,
965E81171DD4D5C800D61E4B
/* TransitionController.swift in Sources */
,
965E81181DD4D5C800D61E4B
/* Snackbar.swift in Sources */
,
965E81181DD4D5C800D61E4B
/* Snackbar.swift in Sources */
,
965E81191DD4D5C800D61E4B
/* SnackbarController.swift in Sources */
,
965E81191DD4D5C800D61E4B
/* SnackbarController.swift in Sources */
,
9D13671C2006A8D80004DE2D
/* DialogController.swift in Sources */
,
9618006D1F4D384200CD77A1
/* Material+UIViewController.swift in Sources */
,
9618006D1F4D384200CD77A1
/* Material+UIViewController.swift in Sources */
,
965E811A1DD4D5C800D61E4B
/* StatusBarController.swift in Sources */
,
965E811A1DD4D5C800D61E4B
/* StatusBarController.swift in Sources */
,
965E811B1DD4D5C800D61E4B
/* Switch.swift in Sources */
,
965E811B1DD4D5C800D61E4B
/* Switch.swift in Sources */
,
965E811C1DD4D5C800D61E4B
/* TabBar.swift in Sources */
,
965E811C1DD4D5C800D61E4B
/* TabBar.swift in Sources */
,
965E811D1DD4D5C800D61E4B
/* TableViewCell.swift in Sources */
,
965E811D1DD4D5C800D61E4B
/* TableViewCell.swift in Sources */
,
965E811E1DD4D5C800D61E4B
/* TextField.swift in Sources */
,
965E811E1DD4D5C800D61E4B
/* TextField.swift in Sources */
,
9D13671E2006A9450004DE2D
/* DialogBuilder.swift in Sources */
,
965E811F1DD4D5C800D61E4B
/* ErrorTextField.swift in Sources */
,
965E811F1DD4D5C800D61E4B
/* ErrorTextField.swift in Sources */
,
965E81211DD4D5C800D61E4B
/* TextStorage.swift in Sources */
,
965E81211DD4D5C800D61E4B
/* TextStorage.swift in Sources */
,
965E81221DD4D5C800D61E4B
/* TextView.swift in Sources */
,
965E81221DD4D5C800D61E4B
/* TextView.swift in Sources */
,
...
@@ -982,6 +1001,7 @@
...
@@ -982,6 +1001,7 @@
961E6BDF1DDA2A95004E6C93
/* Application.swift in Sources */
,
961E6BDF1DDA2A95004E6C93
/* Application.swift in Sources */
,
965E80D71DD4C50600D61E4B
/* Icon.swift in Sources */
,
965E80D71DD4C50600D61E4B
/* Icon.swift in Sources */
,
965E80FC1DD4D59500D61E4B
/* SearchBarController.swift in Sources */
,
965E80FC1DD4D59500D61E4B
/* SearchBarController.swift in Sources */
,
9D13671A2006A8170004DE2D
/* DialogView.swift in Sources */
,
965E80D81DD4C50600D61E4B
/* Layer.swift in Sources */
,
965E80D81DD4C50600D61E4B
/* Layer.swift in Sources */
,
965E80D91DD4C50600D61E4B
/* Layout.swift in Sources */
,
965E80D91DD4C50600D61E4B
/* Layout.swift in Sources */
,
965E80DA1DD4C50600D61E4B
/* Border.swift in Sources */
,
965E80DA1DD4C50600D61E4B
/* Border.swift in Sources */
,
...
...
Sources/iOS/DialogBuilder.swift
0 → 100644
View file @
77c252e5
//
// DialogBuilder.swift
// Material
//
// Created by Orkhan Alikhanov on 1/11/18.
// Copyright © 2018 CosmicMind, Inc. All rights reserved.
//
import
UIKit
public
typealias
Dialog
=
DialogBuilder
<
DialogView
>
open
class
DialogBuilder
<
T
:
DialogView
>
{
public
init
()
{}
open
let
controller
=
DialogController
<
T
>
()
open
func
title
(
_
text
:
String
?)
->
DialogBuilder
{
dialogView
.
titleLabel
.
text
=
text
return
self
}
open
func
details
(
_
text
:
String
?)
->
DialogBuilder
{
dialogView
.
detailsLabel
.
text
=
text
return
self
}
open
func
isCancelable
(
_
value
:
Bool
,
handler
:
(()
->
Void
)?
=
nil
)
->
DialogBuilder
{
controller
.
isCancelable
=
value
controller
.
canceledHandler
=
handler
return
self
}
open
func
shouldDismiss
(
handler
:
((
Button
?)
->
Bool
)?)
->
DialogBuilder
{
controller
.
shouldDismissHandler
=
handler
return
self
}
open
func
positiveButton
(
_
title
:
String
?,
handler
:
(()
->
Void
)?)
->
DialogBuilder
{
dialogView
.
positiveButton
.
title
=
title
controller
.
positiveHandler
=
handler
return
self
}
open
func
negativeButton
(
_
title
:
String
?,
handler
:
(()
->
Void
)?)
->
DialogBuilder
{
dialogView
.
negativeButton
.
title
=
title
controller
.
negativeHandler
=
handler
return
self
}
open
func
neutralButton
(
_
title
:
String
?,
handler
:
(()
->
Void
)?)
->
DialogBuilder
{
dialogView
.
neutralButton
.
title
=
title
controller
.
neutralHandler
=
handler
return
self
}
@discardableResult
open
func
show
(
_
vc
:
UIViewController
)
->
DialogBuilder
{
vc
.
present
(
controller
,
animated
:
true
,
completion
:
nil
)
return
self
}
}
extension
DialogBuilder
{
private
var
dialogView
:
T
{
return
controller
.
dialogView
}
}
Sources/iOS/DialogController.swift
0 → 100644
View file @
77c252e5
//
// DialogController.swift
// Material
//
// Created by Orkhan Alikhanov on 1/11/18.
// Copyright © 2018 CosmicMind, Inc. All rights reserved.
//
import
UIKit
open
class
DialogController
<
T
:
DialogView
>
:
UIViewController
{
open
let
dialogView
=
T
()
open
var
isCancelable
=
false
open
func
prepare
()
{
isMotionEnabled
=
true
motionTransitionType
=
.
fade
modalPresentationStyle
=
.
overFullScreen
}
open
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
view
=
UIControl
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.33
)
view
.
layout
(
dialogView
)
.
center
()
(
view
as?
UIControl
)?
.
addTarget
(
self
,
action
:
#selector(
didTapView
)
,
for
:
.
touchUpInside
)
dialogView
.
buttonArea
.
subviews
.
forEach
{
(
$0
as?
Button
)?
.
addTarget
(
self
,
action
:
#selector(
didTapButton(_:)
)
,
for
:
.
touchUpInside
)
}
}
open
override
func
viewWillLayoutSubviews
()
{
super
.
viewWillLayoutSubviews
()
dialogView
.
maxSize
=
CGSize
(
width
:
Screen
.
width
*
0.8
,
height
:
Screen
.
height
*
0.9
)
}
open
var
canceledHandler
:
(()
->
Void
)?
open
var
shouldDismissHandler
:
((
Button
?)
->
Bool
)?
open
var
positiveHandler
:
(()
->
Void
)?
open
var
negativeHandler
:
(()
->
Void
)?
open
var
neutralHandler
:
(()
->
Void
)?
@objc
private
func
didTapView
()
{
guard
isCancelable
else
{
return
}
dismiss
(
nil
)
canceledHandler
?()
}
@objc
private
func
didTapButton
(
_
sender
:
Button
)
{
switch
sender
{
case
dialogView
.
positiveButton
:
positiveHandler
?()
case
dialogView
.
negativeButton
:
negativeHandler
?()
case
dialogView
.
neutralButton
:
neutralHandler
?()
default
:
break
}
dismiss
(
sender
)
}
open
func
dismiss
(
_
button
:
Button
?)
{
if
shouldDismissHandler
?(
button
)
??
true
{
presentingViewController
?
.
dismiss
(
animated
:
true
,
completion
:
nil
)
}
}
public
override
init
(
nibName
nibNameOrNil
:
String
?,
bundle
nibBundleOrNil
:
Bundle
?)
{
super
.
init
(
nibName
:
nibNameOrNil
,
bundle
:
nibBundleOrNil
)
prepare
()
}
required
public
init
?(
coder
aDecoder
:
NSCoder
)
{
super
.
init
(
coder
:
aDecoder
)
prepare
()
}
}
Sources/iOS/DialogView.swift
0 → 100644
View file @
77c252e5
//
// DialogView.swift
// Material
//
// Created by Orkhan Alikhanov on 1/10/18.
// Copyright © 2018 CosmicMind, Inc. All rights reserved.
//
import
UIKit
open
class
DialogView
:
UIView
{
open
let
titleArea
=
UIView
()
open
let
titleLabel
=
UILabel
()
open
let
detailsLabel
=
UILabel
()
open
private(set)
lazy
var
scrollView
:
UIScrollView
=
{
class
DialogScrollView
:
UIScrollView
{
weak
var
dialogView
:
DialogView
?
override
func
layoutSubviews
()
{
super
.
layoutSubviews
()
dialogView
?
.
layoutDividers
()
}
}
let
scrollView
=
DialogScrollView
()
scrollView
.
dialogView
=
self
return
scrollView
}()
open
let
contentView
=
UIView
()
open
let
buttonArea
=
UIView
()
open
let
neutralButton
=
Button
()
open
let
positiveButton
=
Button
()
open
let
negativeButton
=
Button
()
/// Maximum size of the dialog
open
var
maxSize
=
CGSize
(
width
:
200
,
height
:
300
)
{
didSet
{
guard
oldValue
!=
maxSize
else
{
return
}
invalidateIntrinsicContentSize
()
}
}
public
override
init
(
frame
:
CGRect
)
{
super
.
init
(
frame
:
frame
)
prepare
()
}
required
public
init
?(
coder
aDecoder
:
NSCoder
)
{
super
.
init
(
coder
:
aDecoder
)
prepare
()
}
open
func
prepare
()
{
backgroundColor
=
Color
.
grey
.
lighten5
depthPreset
=
.
depth5
prepareTitleArea
()
prepareTitleLabel
()
prepareScrollView
()
prepareContentView
()
prepareDetailsLabel
()
prepareButtonArea
()
prepareButtons
()
}
open
override
var
intrinsicContentSize
:
CGSize
{
return
sizeThatFits
(
maxSize
)
}
open
override
func
sizeThatFits
(
_
size
:
CGSize
)
->
CGSize
{
var
w
:
CGFloat
=
0
func
setW
(
_
newW
:
CGFloat
)
{
w
=
max
(
w
,
newW
)
w
=
min
(
w
,
size
.
width
)
}
setW
(
titleAreaSizeThatFits
(
width
:
size
.
width
)
.
width
)
setW
(
buttonAreaSizeThatFits
(
width
:
size
.
width
)
.
width
)
setW
(
contentViewSizeThatFits
(
width
:
size
.
width
)
.
width
)
var
h
:
CGFloat
=
0
h
+=
titleAreaSizeThatFits
(
width
:
w
)
.
height
h
+=
buttonAreaSizeThatFits
(
width
:
w
)
.
height
h
+=
contentViewSizeThatFits
(
width
:
w
)
.
height
return
CGSize
(
width
:
w
,
height
:
min
(
h
,
size
.
height
))
}
open
override
func
layoutSubviews
()
{
super
.
layoutSubviews
()
layoutTitleArea
()
layoutButtonArea
()
layoutContentView
()
layoutScrollView
()
layoutDividers
()
// Position button area
buttonArea
.
frame
.
origin
.
y
=
scrollView
.
frame
.
maxY
}
/// Override this if you are using custom view in title area
open
func
titleAreaSizeThatFits
(
width
:
CGFloat
)
->
CGSize
{
guard
!
titleLabel
.
isEmpty
else
{
return
.
zero
}
var
size
=
titleLabel
.
sizeThatFits
(
CGSize
(
width
:
width
-
24
-
24
,
height
:
.
greatestFiniteMagnitude
))
size
.
width
+=
24
+
24
size
.
height
+=
24
+
20
return
size
}
open
func
buttonAreaSizeThatFits
(
width
:
CGFloat
)
->
CGSize
{
guard
!
nonHiddenButtons
.
isEmpty
else
{
return
.
zero
}
let
isStacked
=
requiredButtonAreaWidth
>
width
let
w
=
min
(
width
,
isStacked
?
requiredButtonAreaWidthForStacked
:
requiredButtonAreaWidth
)
let
h
=
isStacked
?
CGFloat
(
8
+
nonHiddenButtons
.
count
*
48
)
:
52
return
CGSize
(
width
:
w
,
height
:
h
)
}
open
func
contentViewSizeThatFits
(
width
:
CGFloat
)
->
CGSize
{
guard
!
detailsLabel
.
isEmpty
else
{
return
.
zero
}
var
size
=
detailsLabel
.
sizeThatFits
(
CGSize
(
width
:
width
-
24
-
24
,
height
:
.
greatestFiniteMagnitude
))
size
.
width
+=
24
+
24
let
additional
:
CGFloat
=
titleLabel
.
isEmpty
?
20
:
0
// if no title area, will be pushed 20 points below
size
.
height
+=
24
+
0
+
additional
return
size
}
}
private
extension
DialogView
{
func
layoutTitleArea
()
{
let
size
=
CGSize
(
width
:
frame
.
width
,
height
:
titleAreaSizeThatFits
(
width
:
frame
.
width
)
.
height
)
titleArea
.
frame
.
size
=
size
guard
!
titleLabel
.
isEmpty
else
{
return
}
titleLabel
.
frame
=
CGRect
(
x
:
24
,
y
:
24
,
width
:
size
.
width
-
24
-
24
,
height
:
size
.
height
-
24
-
20
)
}
func
layoutButtonArea
()
{
let
width
=
frame
.
width
buttonArea
.
frame
.
size
.
width
=
width
buttonArea
.
frame
.
size
.
height
=
buttonAreaSizeThatFits
(
width
:
width
)
.
height
let
buttons
=
nonHiddenButtons
guard
!
buttons
.
isEmpty
else
{
return
}
let
isStacked
=
requiredButtonAreaWidth
>
width
if
isStacked
{
buttons
.
forEach
{
let
w
=
min
(
$0
.
optimalWidth
,
width
-
8
-
8
)
$0
.
frame
.
size
=
CGSize
(
width
:
w
,
height
:
36
)
$0
.
frame
.
origin
.
x
=
width
-
8
-
w
}
positiveButton
.
frame
.
origin
.
y
=
6
let
belowPositive
=
positiveButton
.
isHidden
?
6
:
positiveButton
.
frame
.
maxY
+
6
+
6
negativeButton
.
frame
.
origin
.
y
=
belowPositive
neutralButton
.
frame
.
origin
.
y
=
negativeButton
.
isHidden
?
belowPositive
:
(
negativeButton
.
frame
.
maxY
+
6
+
6
)
}
else
{
buttons
.
forEach
{
$0
.
frame
.
size
=
CGSize
(
width
:
$0
.
optimalWidth
,
height
:
36
)
$0
.
frame
.
origin
.
y
=
8
}
neutralButton
.
frame
.
origin
.
x
=
8
positiveButton
.
frame
.
origin
.
x
=
width
-
8
-
positiveButton
.
frame
.
width
negativeButton
.
frame
.
origin
.
x
=
(
positiveButton
.
isHidden
?
width
:
positiveButton
.
frame
.
minX
)
-
8
-
negativeButton
.
frame
.
width
}
}
func
layoutContentView
()
{
let
size
=
CGSize
(
width
:
frame
.
width
,
height
:
contentViewSizeThatFits
(
width
:
frame
.
width
)
.
height
)
contentView
.
frame
.
size
=
size
guard
!
detailsLabel
.
isEmpty
else
{
return
}
let
additional
:
CGFloat
=
titleArea
.
frame
.
height
==
0
?
20
:
0
// if no title area, push 20 points below
detailsLabel
.
frame
=
CGRect
(
x
:
24
,
y
:
additional
,
width
:
size
.
width
-
24
-
24
,
height
:
size
.
height
-
24
)
}
func
layoutScrollView
()
{
let
h
=
titleArea
.
frame
.
height
+
buttonArea
.
frame
.
height
let
allowed
=
min
(
maxSize
.
height
-
h
,
contentView
.
frame
.
height
)
scrollView
.
frame
.
size
=
CGSize
(
width
:
frame
.
width
,
height
:
max
(
allowed
,
0
))
scrollView
.
frame
.
origin
.
y
=
titleArea
.
frame
.
maxY
scrollView
.
contentSize
=
contentView
.
frame
.
size
}
/// Lays out dividers
///
/// This method is also called (by scrollView) when scrolling happens
func
layoutDividers
()
{
let
isScrollable
=
contentView
.
frame
.
height
>
scrollView
.
frame
.
height
titleArea
.
isDividerHidden
=
titleLabel
.
isEmpty
||
!
isScrollable
||
scrollView
.
isAtTop
buttonArea
.
isDividerHidden
=
nonHiddenButtons
.
isEmpty
||
!
isScrollable
||
scrollView
.
isAtBottom
titleArea
.
layoutDivider
()
buttonArea
.
layoutDivider
()
}
}
private
extension
Button
{
var
optimalWidth
:
CGFloat
{
return
max
(
64
,
sizeThatFits
(
CGSize
(
width
:
.
max
,
height
:
36
))
.
width
)
}
}
private
extension
UILabel
{
var
isEmpty
:
Bool
{
let
empty
=
text
?
.
isEmpty
??
true
isHidden
=
empty
return
empty
}
}
private
extension
DialogView
{
var
requiredButtonAreaWidth
:
CGFloat
{
let
buttons
=
nonHiddenButtons
guard
!
buttons
.
isEmpty
else
{
return
0
}
let
buttonsWidth
:
CGFloat
=
buttons
.
reduce
(
0
)
{
$0
+
$1
.
optimalWidth
}
let
additional
:
CGFloat
=
neutralButton
.
isHidden
?
0
:
8
// additional spacing for neutral button
return
buttonsWidth
+
CGFloat
(
buttons
.
count
*
8
)
+
additional
}
var
requiredButtonAreaWidthForStacked
:
CGFloat
{
return
8
+
8
+
nonHiddenButtons
.
reduce
(
0
)
{
max
(
$0
,
$1
.
optimalWidth
)
}
}
var
nonHiddenButtons
:
[
Button
]
{
positiveButton
.
isHidden
=
positiveButton
.
title
(
for
:
.
normal
)?
.
isEmpty
??
true
negativeButton
.
isHidden
=
negativeButton
.
title
(
for
:
.
normal
)?
.
isEmpty
??
true
neutralButton
.
isHidden
=
neutralButton
.
title
(
for
:
.
normal
)?
.
isEmpty
??
true
return
[
positiveButton
,
negativeButton
,
neutralButton
]
.
filter
{
!
$0
.
isHidden
}
}
}
private
extension
DialogView
{
func
prepareTitleArea
()
{
addSubview
(
titleArea
)
titleArea
.
dividerColor
=
Color
.
darkText
.
dividers
titleArea
.
dividerThickness
=
1
titleArea
.
dividerAlignment
=
.
bottom
}
func
prepareTitleLabel
()
{
titleArea
.
addSubview
(
titleLabel
)
titleLabel
.
font
=
RobotoFont
.
bold
(
with
:
19
)
titleLabel
.
textColor
=
Color
.
darkText
.
primary
titleLabel
.
numberOfLines
=
0
}
func
prepareButtonArea
()
{
addSubview
(
buttonArea
)
buttonArea
.
dividerColor
=
Color
.
darkText
.
dividers
buttonArea
.
dividerThickness
=
1
buttonArea
.
dividerAlignment
=
.
top
}
func
prepareButtons
()
{
[
positiveButton
,
negativeButton
,
neutralButton
]
.
forEach
{
buttonArea
.
addSubview
(
$0
)
$0
.
contentEdgeInsets
=
UIEdgeInsets
(
top
:
0
,
left
:
8
,
bottom
:
0
,
right
:
8
)
}
}
func
prepareScrollView
()
{
addSubview
(
scrollView
)
}
func
prepareContentView
()
{
scrollView
.
addSubview
(
contentView
)
}
func
prepareDetailsLabel
()
{
contentView
.
addSubview
(
detailsLabel
)
detailsLabel
.
numberOfLines
=
0
detailsLabel
.
textColor
=
Color
.
darkText
.
secondary
}
}
private
extension
UIScrollView
{
var
isAtTop
:
Bool
{
return
contentOffset
.
y
<=
0
}
var
isAtBottom
:
Bool
{
return
contentOffset
.
y
>=
(
contentSize
.
height
-
frame
.
height
-
1
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment