pbind 0.6.2 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pbind/command.rb +2 -0
- data/lib/pbind/command/mock.rb +1 -1
- data/lib/pbind/command/serv.rb +78 -16
- data/lib/pbind/command/view.rb +86 -0
- data/lib/pbind/gem_version.rb +1 -1
- data/lib/pbind/minify.rb +52 -0
- data/source/PBLiveLoader/NSInputStream+Reader.m +10 -0
- data/source/PBLiveLoader/PBLLInspector.h +2 -2
- data/source/PBLiveLoader/PBLLInspector.m +385 -22
- data/source/PBLiveLoader/PBLLInspectorController.h +2 -0
- data/source/PBLiveLoader/PBLLInspectorController.m +71 -17
- data/source/PBLiveLoader/PBLLInspectorTipsController.h +23 -0
- data/source/PBLiveLoader/PBLLInspectorTipsController.m +140 -0
- data/source/PBLiveLoader/PBLLOptions.h +1 -1
- data/source/PBLiveLoader/PBLLRemoteWatcher.h +6 -0
- data/source/PBLiveLoader/PBLLRemoteWatcher.m +155 -29
- data/source/PBLiveLoader/PBLLResource.h +25 -0
- data/source/PBLiveLoader/PBLLResource.m +208 -0
- data/source/PBLiveLoader/PBLiveLoader.m +44 -8
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5f96f2b098154b4bb59743bed848868acd9d808
|
4
|
+
data.tar.gz: 630da57494ad83f41a9e20ba9f74e487ee456c6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb44831ec0c62561b94149992a5f69bd358a54edd076518afc34e02a5237e06990ba234cf5ef2313fa5473a80f805555fb889977110c25a2b18cb5fe54f825b8
|
7
|
+
data.tar.gz: 1bb0e99fce43907ab382814832ef081251ef2671dbe1eb9e36e0a386b267a058af15e49241f808e0bb830da60c67e2f46d3c520869d3c721a269229cb9efdfe8
|
data/lib/pbind/command.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'colored'
|
2
2
|
require 'claide'
|
3
3
|
|
4
|
+
require 'pbind/minify'
|
4
5
|
require 'pbind/user_interface'
|
5
6
|
require 'pbind/gem_version.rb'
|
6
7
|
|
@@ -10,6 +11,7 @@ module Pbind
|
|
10
11
|
require_relative 'command/watch'
|
11
12
|
require_relative 'command/mock'
|
12
13
|
require_relative 'command/serv'
|
14
|
+
require_relative 'command/view'
|
13
15
|
|
14
16
|
self.abstract_command = true
|
15
17
|
self.command = 'pbind'
|
data/lib/pbind/command/mock.rb
CHANGED
@@ -27,7 +27,7 @@ module Pbind
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def run
|
30
|
-
@action = @action.gsub(/\//, '
|
30
|
+
@action = @action.gsub(/\//, ':')
|
31
31
|
@api_name = 'PBLocalhost'
|
32
32
|
@project_root = File.dirname(@project_path)
|
33
33
|
@api_install_dir = File.absolute_path(File.join(@project_root, @api_name))
|
data/lib/pbind/command/serv.rb
CHANGED
@@ -27,6 +27,7 @@ module Pbind
|
|
27
27
|
@project_root = File.dirname(@project_path)
|
28
28
|
@src_install_dir = File.absolute_path(File.join(@project_root, @src_name))
|
29
29
|
@api_install_dir = File.absolute_path(File.join(@project_root, @api_name))
|
30
|
+
@api_ignores_file = File.absolute_path(File.join(@api_install_dir, 'ignore.h'))
|
30
31
|
@project = Xcodeproj::Project.open(@project_path)
|
31
32
|
@changed = false
|
32
33
|
|
@@ -59,6 +60,7 @@ module Pbind
|
|
59
60
|
server = TCPServer.new 8082 # Server bind to port
|
60
61
|
@server = server
|
61
62
|
@clients = []
|
63
|
+
@client_names = Hash.new
|
62
64
|
|
63
65
|
addr = server.addr
|
64
66
|
addr.shift
|
@@ -79,10 +81,24 @@ module Pbind
|
|
79
81
|
end
|
80
82
|
|
81
83
|
def handle_request(client, req)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
type = req.bytes[0]
|
85
|
+
msg = req[1..-1]
|
86
|
+
|
87
|
+
if type == 0xC0
|
88
|
+
# Connected
|
89
|
+
@client_names[client] = msg
|
90
|
+
print_client_msg(client, "Connected")
|
91
|
+
elsif type == 0xC1
|
92
|
+
# Request API
|
93
|
+
print_client_msg(client, "Request API '#{msg}'")
|
94
|
+
send_json([client], msg)
|
95
|
+
elsif type == 0xC2
|
96
|
+
# Log
|
97
|
+
print_client_msg(client, msg)
|
98
|
+
elsif type == 0xF1
|
99
|
+
print_client_msg(client, "Apply changed '#{msg}'")
|
100
|
+
elsif type == 0xD0
|
101
|
+
print_client_msg(client, "Got response '#{msg}'")
|
86
102
|
end
|
87
103
|
end
|
88
104
|
|
@@ -116,16 +132,38 @@ module Pbind
|
|
116
132
|
@listener.start # not blocking
|
117
133
|
end
|
118
134
|
|
119
|
-
def send_json(clients,
|
135
|
+
def send_json(clients, api)
|
136
|
+
# Check if ignores
|
137
|
+
file = File.open(@api_ignores_file, "r")
|
138
|
+
file.each_line { |line|
|
139
|
+
if line.start_with? '//'
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
ignore = line.chomp
|
144
|
+
if ignore.include? api
|
145
|
+
print_serv_msg "Ignores API '#{api}'"
|
146
|
+
clients.each { |client|
|
147
|
+
write_byte client, 0xE0
|
148
|
+
}
|
149
|
+
return
|
150
|
+
end
|
151
|
+
}
|
152
|
+
|
153
|
+
# Read content and minify
|
154
|
+
json_file = "#{api}.json"
|
120
155
|
file = File.open(File.join(@api_install_dir, json_file), "r")
|
121
156
|
content = file.read
|
122
157
|
file.close
|
123
158
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
159
|
+
content = Minify.json(content)
|
160
|
+
|
161
|
+
# Send content
|
162
|
+
print_serv_msg("Send API '#{api}'")
|
163
|
+
clients.each { |client|
|
164
|
+
write_byte client, 0xD0
|
165
|
+
write_string client, api
|
166
|
+
write_string client, content
|
129
167
|
}
|
130
168
|
end
|
131
169
|
|
@@ -145,12 +183,12 @@ module Pbind
|
|
145
183
|
if file_name == nil
|
146
184
|
file_name = File.basename(file_path)
|
147
185
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
186
|
+
|
187
|
+
print_serv_msg("Update file \"#{file_name}\"")
|
188
|
+
clients.each { |client|
|
189
|
+
write_byte(client, 0xF1)
|
190
|
+
write_string(client, file_name)
|
191
|
+
write_string(client, file_content)
|
154
192
|
}
|
155
193
|
}
|
156
194
|
end
|
@@ -172,6 +210,30 @@ module Pbind
|
|
172
210
|
end
|
173
211
|
end
|
174
212
|
|
213
|
+
def print_serv_msg(msg)
|
214
|
+
print_time
|
215
|
+
print "[Pbind] ".yellow
|
216
|
+
puts msg
|
217
|
+
end
|
218
|
+
|
219
|
+
def print_client_msg(client, msg)
|
220
|
+
print_time
|
221
|
+
device = @client_names[client]
|
222
|
+
if (device == nil)
|
223
|
+
device = "unknown"
|
224
|
+
end
|
225
|
+
print "[#{device}] ".green
|
226
|
+
puts msg
|
227
|
+
end
|
228
|
+
|
229
|
+
def print_time
|
230
|
+
t = Time.now
|
231
|
+
print t.strftime("%H:%M:%S")
|
232
|
+
print '.'
|
233
|
+
print '%03d' % ((t.to_f * 1000).to_i % 1000) # ms
|
234
|
+
print ' '
|
235
|
+
end
|
236
|
+
|
175
237
|
end
|
176
238
|
end
|
177
239
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
require 'pathname'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Pbind
|
6
|
+
class Command
|
7
|
+
class View < Command
|
8
|
+
self.abstract_command = false
|
9
|
+
self.summary = 'Generate the objective-c view code from Pbind layout.'
|
10
|
+
self.description = <<-DESC
|
11
|
+
This will parse the `PLIST` file and translate it to objective-c code.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
self.arguments = [
|
15
|
+
CLAide::Argument.new(%(PLIST), true),
|
16
|
+
]
|
17
|
+
|
18
|
+
def initialize(argv)
|
19
|
+
super
|
20
|
+
@plist = argv.shift_argument
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
help! 'The plist is required.' unless @plist
|
25
|
+
help! 'The plist is not exists.' unless File.exists?(@plist)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
parse_plist(@plist)
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
#----------------------------------------#
|
36
|
+
|
37
|
+
# !@group Private helpers
|
38
|
+
|
39
|
+
# Parse the plist and generate code
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
#
|
43
|
+
def parse_plist(plist_path)
|
44
|
+
plist = Xcodeproj::Plist.read_from_path(plist_path)
|
45
|
+
names = []
|
46
|
+
parents = []
|
47
|
+
|
48
|
+
puts '// Create views'
|
49
|
+
plist["views"].each { |name, view|
|
50
|
+
clazz = view['clazz']
|
51
|
+
properties = view['properties']
|
52
|
+
|
53
|
+
puts "#{clazz} *#{name} = [[#{clazz} alloc] init]; {"
|
54
|
+
|
55
|
+
properties.each { |key, value|
|
56
|
+
puts " #{name}.#{key} = [PBValueParser valueWithString:@\"#{value}\"];"
|
57
|
+
}
|
58
|
+
puts " [self addSubview:#{name}];"
|
59
|
+
|
60
|
+
puts '}'
|
61
|
+
names.push name
|
62
|
+
}
|
63
|
+
|
64
|
+
puts ''
|
65
|
+
puts '// Auto layout'
|
66
|
+
puts 'NSDictionary *views = @{'
|
67
|
+
names.each { |name|
|
68
|
+
puts " @\"#{name}\": #{name},"
|
69
|
+
}
|
70
|
+
puts '};'
|
71
|
+
puts 'NSArray *formats = @['
|
72
|
+
plist["formats"].each { |format|
|
73
|
+
puts " @\"#{format}\","
|
74
|
+
}
|
75
|
+
puts "];"
|
76
|
+
puts <<-CODE
|
77
|
+
for (NSString *format in formats) {
|
78
|
+
NSArray *constraints = [PBLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:metrics views:views];
|
79
|
+
[view addConstraints:constraints];
|
80
|
+
}
|
81
|
+
CODE
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/pbind/gem_version.rb
CHANGED
data/lib/pbind/minify.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Pbind
|
2
|
+
module Minify
|
3
|
+
|
4
|
+
require 'strscan'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def json(str)
|
9
|
+
|
10
|
+
ss = StringScanner.new(str)
|
11
|
+
buf = ''
|
12
|
+
|
13
|
+
until ss.eos?
|
14
|
+
# Remove whitespace
|
15
|
+
if s = ss.scan(/\s+/)
|
16
|
+
|
17
|
+
# Scan punctuation
|
18
|
+
elsif s = ss.scan(/[{},:\]\[]/)
|
19
|
+
buf << s
|
20
|
+
|
21
|
+
# Scan strings
|
22
|
+
elsif s = ss.scan(/""|(".*?[^\\])?"/)
|
23
|
+
buf << s
|
24
|
+
|
25
|
+
# Scan reserved words
|
26
|
+
elsif s = ss.scan(/(true|false|null)/)
|
27
|
+
buf << s
|
28
|
+
|
29
|
+
# Scan numbers
|
30
|
+
elsif s = ss.scan(/-?\d+([.]\d+)?([eE][-+]?[0-9]+)?/)
|
31
|
+
buf << s
|
32
|
+
|
33
|
+
# Remove C++ style comments
|
34
|
+
elsif s = ss.scan(%r{//})
|
35
|
+
ss.scan_until(/(\r?\n|$)/)
|
36
|
+
|
37
|
+
# Remove C style comments
|
38
|
+
elsif s = ss.scan(%r{/[*]})
|
39
|
+
ss.scan_until(%r{[*]/}) || raise(SyntaxError, "Unterminated /*...*/ comment - #{ss.rest}")
|
40
|
+
|
41
|
+
# Anything else is invalid JSON
|
42
|
+
else
|
43
|
+
raise SyntaxError, "Unable to pre-scan string: #{ss.rest}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
buf
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -21,8 +21,18 @@
|
|
21
21
|
|
22
22
|
- (NSData *)readData {
|
23
23
|
int length = [self readInt];
|
24
|
+
if (length < 0) {
|
25
|
+
return nil;
|
26
|
+
}
|
24
27
|
uint8_t *bytes = malloc(length);
|
25
28
|
NSInteger len = [self read:bytes maxLength:length];
|
29
|
+
while (len < length) {
|
30
|
+
len += [self read:bytes + len maxLength:length - len];
|
31
|
+
}
|
32
|
+
if (len != length) {
|
33
|
+
NSLog(@"Failed to read data in length %i, only got %i bytes", length, (int)len);
|
34
|
+
return nil;
|
35
|
+
}
|
26
36
|
return [NSData dataWithBytes:bytes length:len];
|
27
37
|
}
|
28
38
|
|
@@ -9,7 +9,7 @@
|
|
9
9
|
#import "PBLLOptions.h"
|
10
10
|
#include <targetconditionals.h>
|
11
11
|
|
12
|
-
#if (PBLIVE_ENABLED
|
12
|
+
#if (PBLIVE_ENABLED)
|
13
13
|
|
14
14
|
#import <UIKit/UIKit.h>
|
15
15
|
|
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
+ (instancetype)sharedInspector;
|
19
19
|
|
20
|
-
|
20
|
+
- (void)updateConnectState:(BOOL)connected;
|
21
21
|
|
22
22
|
@end
|
23
23
|
|
@@ -7,44 +7,407 @@
|
|
7
7
|
|
8
8
|
#import "PBLLInspector.h"
|
9
9
|
|
10
|
-
#if (PBLIVE_ENABLED
|
10
|
+
#if (PBLIVE_ENABLED)
|
11
11
|
|
12
12
|
#import "PBLLInspectorController.h"
|
13
|
+
#import "PBLLInspectorTipsController.h"
|
14
|
+
#import "PBLLResource.h"
|
13
15
|
#import <Pbind/Pbind.h>
|
16
|
+
#import <objc/runtime.h>
|
14
17
|
|
15
|
-
@interface
|
18
|
+
@interface PBLLTipsTapper : UITapGestureRecognizer
|
19
|
+
|
20
|
+
@property (nonatomic, strong) NSString *tips;
|
21
|
+
|
22
|
+
@property (nonatomic, weak) UIView *owner;
|
23
|
+
|
24
|
+
@property (nonatomic, copy) NSString *keyPath;
|
25
|
+
|
26
|
+
@property (nonatomic, assign) BOOL ownerUserInteractionEnabled;
|
27
|
+
|
28
|
+
@end
|
29
|
+
|
30
|
+
@implementation PBLLTipsTapper
|
31
|
+
|
32
|
+
- (NSString *)tips {
|
33
|
+
if (_tips != nil) {
|
34
|
+
return _tips;
|
35
|
+
}
|
36
|
+
|
37
|
+
NSMutableString *s = [NSMutableString string];
|
38
|
+
[s appendString:[_owner valueForAdditionKey:[NSString stringWithFormat:@"~%@", _keyPath]]];
|
39
|
+
if ([_owner isKindOfClass:[UIImageView class]]) {
|
40
|
+
UIImage *image = [(UIImageView *)_owner image];
|
41
|
+
[s appendFormat:@"\nsize: %.1f x %.1f @%.1fx", image.size.width, image.size.height, image.scale];
|
42
|
+
}
|
43
|
+
return s;
|
44
|
+
}
|
45
|
+
|
46
|
+
@end
|
47
|
+
|
48
|
+
@interface PBLLInspector () <UIPopoverPresentationControllerDelegate>
|
49
|
+
{
|
50
|
+
NSString *_identifier;
|
51
|
+
}
|
52
|
+
|
53
|
+
+ (void)showInspectorForView:(UIView *)view;
|
54
|
+
|
55
|
+
@end
|
56
|
+
|
57
|
+
@implementation UIViewController (PBLLInspector)
|
58
|
+
|
59
|
+
static inline void pbll_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
|
60
|
+
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
61
|
+
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
62
|
+
if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
|
63
|
+
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
|
64
|
+
} else {
|
65
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
+ (void)load {
|
70
|
+
static dispatch_once_t onceToken;
|
71
|
+
dispatch_once(&onceToken, ^{
|
72
|
+
pbll_swizzleSelector([UIViewController class], @selector(viewDidAppear:), @selector(pbll_viewDidAppear:));
|
73
|
+
});
|
74
|
+
}
|
75
|
+
|
76
|
+
- (void)pbll_viewDidAppear:(BOOL)animated {
|
77
|
+
[self pbll_viewDidAppear:animated];
|
78
|
+
|
79
|
+
if ([self isKindOfClass:[UINavigationController class]] || [self isKindOfClass:[PBLLInspectorController class]]) {
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
|
83
|
+
UIView *plistView = [self pbll_findPlistView:self.view];
|
84
|
+
if (plistView == nil) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
[PBLLInspector showInspectorForView:plistView];
|
88
|
+
}
|
89
|
+
|
90
|
+
- (UIView *)pbll_findPlistView:(UIView *)view {
|
91
|
+
if (view.plist != nil) {
|
92
|
+
return view;
|
93
|
+
}
|
94
|
+
|
95
|
+
UIView *plistView = nil;
|
96
|
+
for (UIView *subview in view.subviews) {
|
97
|
+
plistView = [self pbll_findPlistView:subview];
|
98
|
+
if (plistView != nil) {
|
99
|
+
break;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return plistView;
|
103
|
+
}
|
16
104
|
|
17
105
|
@end
|
18
106
|
|
19
107
|
@implementation PBLLInspector
|
20
108
|
|
109
|
+
static const CGFloat kWidth = 44.f;
|
110
|
+
static const CGFloat kHeight = 44.f;
|
111
|
+
|
112
|
+
static BOOL kDisplaysExpression = NO;
|
113
|
+
|
114
|
+
+ (void)load {
|
115
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pbactionWillDispatch:) name:PBActionStoreWillDispatchActionNotification object:nil];
|
116
|
+
[Pbind registerViewValueSetter:^id(UIView *view, NSString *keyPath, id value, BOOL *canceld, UIView *contextView, NSString *contextKeyPath) {
|
117
|
+
if (!kDisplaysExpression) {
|
118
|
+
return value;
|
119
|
+
}
|
120
|
+
|
121
|
+
PBExpression *exp = [[contextView pb_expressions] objectForKey:contextKeyPath];
|
122
|
+
if (exp == nil) {
|
123
|
+
return value;
|
124
|
+
}
|
125
|
+
|
126
|
+
if ([keyPath isEqualToString:@"text"]) {
|
127
|
+
return [exp stringValue];
|
128
|
+
}
|
129
|
+
|
130
|
+
return value;
|
131
|
+
}];
|
132
|
+
|
133
|
+
[Pbind registerViewValueAsyncSetter:^(UIView *view, NSString *keyPath, id value, CGSize viewSize, UIView *contextView, NSString *contextKeyPath) {
|
134
|
+
PBExpression *exp = [[contextView pb_expressions] objectForKey:contextKeyPath];
|
135
|
+
if (exp == nil) {
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
|
139
|
+
if ([view isKindOfClass:[UIImageView class]] && [keyPath isEqualToString:@"image"]) {
|
140
|
+
CALayer *imageBgInspectorLayer = [view valueForAdditionKey:@"pb_bg_inspector"];
|
141
|
+
CATextLayer *textInspectorLayer = [view valueForAdditionKey:@"pb_text_inspector"];
|
142
|
+
PBLLTipsTapper *tapper = [view valueForAdditionKey:@"pb_tap_inspector"];
|
143
|
+
if (kDisplaysExpression) {
|
144
|
+
CGRect bounds = (CGRect){.origin = CGPointZero, .size = viewSize};
|
145
|
+
if (imageBgInspectorLayer == nil) {
|
146
|
+
imageBgInspectorLayer = [[CALayer alloc] init];
|
147
|
+
imageBgInspectorLayer.backgroundColor = [[PBLLResource pbindColor] colorWithAlphaComponent:.5f].CGColor;
|
148
|
+
imageBgInspectorLayer.bounds = bounds;
|
149
|
+
imageBgInspectorLayer.position = CGPointZero;
|
150
|
+
imageBgInspectorLayer.anchorPoint = CGPointZero;
|
151
|
+
[view.layer addSublayer:imageBgInspectorLayer];
|
152
|
+
[view setValue:imageBgInspectorLayer forAdditionKey:@"pb_bg_inspector"];
|
153
|
+
}
|
154
|
+
|
155
|
+
if (textInspectorLayer == nil) {
|
156
|
+
textInspectorLayer = [[CATextLayer alloc] init];
|
157
|
+
textInspectorLayer.bounds = CGRectMake(0, 0, viewSize.width, 36);
|
158
|
+
textInspectorLayer.position = CGPointMake(0, viewSize.height / 2);
|
159
|
+
textInspectorLayer.anchorPoint = CGPointMake(0, 0.5);
|
160
|
+
textInspectorLayer.foregroundColor = [UIColor whiteColor].CGColor;
|
161
|
+
textInspectorLayer.alignmentMode = kCAAlignmentCenter;
|
162
|
+
textInspectorLayer.contentsScale = [UIScreen mainScreen].scale;
|
163
|
+
textInspectorLayer.fontSize = 14.f;
|
164
|
+
[view.layer addSublayer:textInspectorLayer];
|
165
|
+
[view setValue:textInspectorLayer forAdditionKey:@"pb_text_inspector"];
|
166
|
+
}
|
167
|
+
textInspectorLayer.string = [NSString stringWithFormat:@"%@\n%.1f x %.1f", exp.stringValue, viewSize.width, viewSize.height];
|
168
|
+
|
169
|
+
if (tapper == nil) {
|
170
|
+
tapper = [[PBLLTipsTapper alloc] initWithTarget:self action:@selector(handleShowTips:)];
|
171
|
+
tapper.owner = view;
|
172
|
+
tapper.keyPath = keyPath;
|
173
|
+
[view addGestureRecognizer:tapper];
|
174
|
+
[view setValue:tapper forAdditionKey:@"pb_tap_inspector"];
|
175
|
+
|
176
|
+
tapper.ownerUserInteractionEnabled = view.userInteractionEnabled;
|
177
|
+
view.userInteractionEnabled = YES;
|
178
|
+
}
|
179
|
+
} else {
|
180
|
+
if (imageBgInspectorLayer != nil) {
|
181
|
+
[imageBgInspectorLayer removeFromSuperlayer];
|
182
|
+
[view setValue:nil forAdditionKey:@"pb_bg_inspector"];
|
183
|
+
}
|
184
|
+
if (textInspectorLayer != nil) {
|
185
|
+
[textInspectorLayer removeFromSuperlayer];
|
186
|
+
[view setValue:nil forAdditionKey:@"pb_text_inspector"];
|
187
|
+
}
|
188
|
+
if (tapper != nil) {
|
189
|
+
view.userInteractionEnabled = tapper.ownerUserInteractionEnabled;
|
190
|
+
[view removeGestureRecognizer:tapper];
|
191
|
+
[view setValue:nil forAdditionKey:@"pb_tap_inspector"];
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
return;
|
197
|
+
}];
|
198
|
+
}
|
199
|
+
|
21
200
|
+ (instancetype)sharedInspector {
|
22
|
-
static PBLLInspector *
|
201
|
+
static PBLLInspector *inspector;
|
23
202
|
static dispatch_once_t onceToken;
|
24
203
|
dispatch_once(&onceToken, ^{
|
25
|
-
|
204
|
+
inspector = [[PBLLInspector alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
|
26
205
|
});
|
27
|
-
return
|
206
|
+
return inspector;
|
28
207
|
}
|
29
208
|
|
30
|
-
+ (void)
|
209
|
+
+ (void)addToController:(UIViewController *)controller withIdentifier:(NSString *)identifier {
|
210
|
+
if (controller == nil) return;
|
211
|
+
|
31
212
|
PBLLInspector *inspector = [self sharedInspector];
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
[
|
41
|
-
[
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
-
|
46
|
-
|
47
|
-
|
213
|
+
if ([inspector.superview isEqual:controller.view]) {
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
|
217
|
+
[inspector removeFromSuperview];
|
218
|
+
[controller.view addSubview:inspector];
|
219
|
+
|
220
|
+
CGPoint center = CGPointZero;
|
221
|
+
CGFloat x = [[NSUserDefaults standardUserDefaults] floatForKey:[NSString stringWithFormat:@"pbind.inspector.x@%@", identifier]];
|
222
|
+
CGFloat y = [[NSUserDefaults standardUserDefaults] floatForKey:[NSString stringWithFormat:@"pbind.inspector.y@%@", identifier]];
|
223
|
+
CGFloat minX = kWidth / 2;
|
224
|
+
CGFloat minY = kHeight / 2;
|
225
|
+
CGFloat maxX = controller.view.frame.size.width - minX;
|
226
|
+
CGFloat maxY = controller.view.frame.size.height - minY;
|
227
|
+
if (x >= minX && x <= maxX) {
|
228
|
+
center.x = x;
|
229
|
+
} else {
|
230
|
+
center.x = maxX - 6.f;
|
231
|
+
}
|
232
|
+
if (y >= minY && y <= maxY) {
|
233
|
+
center.y = y;
|
234
|
+
} else {
|
235
|
+
center.y = maxY - 70.f;
|
236
|
+
}
|
237
|
+
inspector.center = center;
|
238
|
+
inspector->_identifier = identifier;
|
239
|
+
}
|
240
|
+
|
241
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
242
|
+
if (self = [super initWithFrame:frame]) {
|
243
|
+
self.backgroundColor = [UIColor lightGrayColor];
|
244
|
+
self.layer.cornerRadius = 5.f;
|
245
|
+
[self setImage:[PBLLResource logoImage] forState:UIControlStateNormal];
|
246
|
+
// [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
247
|
+
// [self setTitle:@"Pbind" forState:UIControlStateNormal];
|
248
|
+
[self addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside];
|
249
|
+
|
250
|
+
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
251
|
+
[self addGestureRecognizer:pan];
|
252
|
+
|
253
|
+
#if !(TARGET_IPHONE_SIMULATOR)
|
254
|
+
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
255
|
+
[self addGestureRecognizer:longPress];
|
256
|
+
#endif
|
257
|
+
}
|
258
|
+
return self;
|
259
|
+
}
|
260
|
+
|
261
|
+
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
262
|
+
if (newWindow == nil) {
|
263
|
+
UIViewController *vc = [self supercontroller];
|
264
|
+
if (vc.navigationController.topViewController != vc) {
|
265
|
+
// pop
|
266
|
+
kDisplaysExpression = NO;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
#pragma mark - User Interaction
|
272
|
+
|
273
|
+
- (void)handleClick:(id)sender {
|
274
|
+
kDisplaysExpression = !kDisplaysExpression;
|
275
|
+
if (!kDisplaysExpression) {
|
276
|
+
UIViewController *curVC = [self supercontroller];
|
277
|
+
if (curVC.presentedViewController != nil) {
|
278
|
+
[curVC.presentedViewController dismissViewControllerAnimated:YES completion:nil];
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
[[self superview] pb_reloadPlist];
|
283
|
+
}
|
284
|
+
|
285
|
+
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
|
286
|
+
CGPoint translation = [recognizer translationInView:self];
|
287
|
+
CGPoint center = self.center;
|
288
|
+
center.x += translation.x;
|
289
|
+
center.y += translation.y;
|
290
|
+
self.center = center;
|
291
|
+
[recognizer setTranslation:CGPointZero inView:self];
|
292
|
+
|
293
|
+
[[NSUserDefaults standardUserDefaults] setFloat:center.x forKey:[NSString stringWithFormat:@"pbind.inspector.x@%@", _identifier]];
|
294
|
+
[[NSUserDefaults standardUserDefaults] setFloat:center.y forKey:[NSString stringWithFormat:@"pbind.inspector.y@%@", _identifier]];
|
295
|
+
}
|
296
|
+
|
297
|
+
#if !(TARGET_IPHONE_SIMULATOR)
|
298
|
+
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
299
|
+
if (sender.state != UIGestureRecognizerStateBegan) {
|
300
|
+
return;
|
301
|
+
}
|
302
|
+
|
303
|
+
UIViewController *currentController = [self supercontroller];
|
304
|
+
if (currentController == nil) {
|
305
|
+
return;
|
306
|
+
}
|
307
|
+
|
308
|
+
[PBLLInspectorController presentFromViewController:currentController];
|
309
|
+
}
|
310
|
+
#endif
|
311
|
+
|
312
|
+
+ (void)handleShowTips:(PBLLTipsTapper *)sender {
|
313
|
+
if (sender.tips == nil) {
|
314
|
+
return;
|
315
|
+
}
|
316
|
+
|
317
|
+
[[self sharedInspector] showTips:sender.tips code:nil onView:sender.owner];
|
318
|
+
}
|
319
|
+
|
320
|
+
#pragma mark - Notification
|
321
|
+
|
322
|
+
- (void)updateConnectState:(BOOL)connected {
|
323
|
+
self.backgroundColor = connected ? [PBLLResource pbindColor] : [UIColor lightGrayColor];
|
324
|
+
}
|
325
|
+
|
326
|
+
+ (void)pbviewDidStartLoad:(NSNotification *)notification {
|
327
|
+
UIView *view = notification.object;
|
328
|
+
[self showInspectorForView:view];
|
329
|
+
}
|
330
|
+
|
331
|
+
+ (void)showInspectorForView:(UIView *)view {
|
332
|
+
NSString *plist = view.plist;
|
333
|
+
if (plist == nil) {
|
334
|
+
return;
|
335
|
+
}
|
336
|
+
|
337
|
+
UIViewController *controller = [view supercontroller];
|
338
|
+
[self addToController:controller withIdentifier:plist];
|
339
|
+
}
|
340
|
+
|
341
|
+
+ (void)pbactionWillDispatch:(NSNotification *)notification {
|
342
|
+
PBAction *action = notification.object;
|
343
|
+
if (action.mapper == nil) {
|
344
|
+
return;
|
345
|
+
}
|
346
|
+
|
347
|
+
if (!kDisplaysExpression) {
|
348
|
+
return;
|
349
|
+
}
|
350
|
+
|
351
|
+
// Prevent default action
|
352
|
+
action.disabled = YES;
|
353
|
+
|
354
|
+
// Build up tips
|
355
|
+
NSMutableDictionary *temp = [NSMutableDictionary dictionary];
|
356
|
+
PBActionState *state = action.store.state;
|
357
|
+
[action.mapper initPropertiesForTarget:temp];
|
358
|
+
[action.mapper mapPropertiesToTarget:temp withData:state.data owner:state.sender context:state.context];
|
359
|
+
NSString *tips = [temp description];
|
360
|
+
|
361
|
+
// Build up code (expression)
|
362
|
+
NSString *code = [[action.mapper targetSource] description];
|
363
|
+
|
364
|
+
[[self sharedInspector] showTips:tips code:code onView:state.sender];
|
365
|
+
}
|
366
|
+
|
367
|
+
#pragma mark - Tips
|
368
|
+
|
369
|
+
- (void)showTips:(NSString *)tips code:(NSString *)code onView:(UIView *)view {
|
370
|
+
UIViewController *curVC = [self supercontroller];
|
371
|
+
if (curVC == nil) {
|
372
|
+
return;
|
373
|
+
}
|
374
|
+
|
375
|
+
PBLLInspectorTipsController *popVC = (id) curVC.presentedViewController;
|
376
|
+
UIPopoverPresentationController *popContainerVC = popVC.popoverPresentationController;
|
377
|
+
if (popContainerVC != nil) {
|
378
|
+
BOOL changed = popContainerVC.sourceView != view || ![tips isEqualToString:popVC.tips];
|
379
|
+
if (changed) {
|
380
|
+
[popVC dismissViewControllerAnimated:NO completion:^{
|
381
|
+
[self showTips:tips code:code onView:view];
|
382
|
+
}];
|
383
|
+
} else {
|
384
|
+
[popVC dismissViewControllerAnimated:YES completion:nil];
|
385
|
+
}
|
386
|
+
} else {
|
387
|
+
popVC = [[PBLLInspectorTipsController alloc] init];
|
388
|
+
popVC.modalPresentationStyle = UIModalPresentationPopover;
|
389
|
+
|
390
|
+
popContainerVC = popVC.popoverPresentationController;
|
391
|
+
popContainerVC.permittedArrowDirections = UIPopoverArrowDirectionAny;
|
392
|
+
popContainerVC.delegate = self;
|
393
|
+
popContainerVC.backgroundColor = [PBLLResource pbindColor];
|
394
|
+
popContainerVC.passthroughViews = @[curVC.view];
|
395
|
+
|
396
|
+
popVC.tips = tips;
|
397
|
+
popVC.code = code;
|
398
|
+
popContainerVC.sourceView = view;
|
399
|
+
popContainerVC.sourceRect = view.bounds;
|
400
|
+
|
401
|
+
[curVC presentViewController:popVC animated:YES completion:nil];
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller{
|
406
|
+
return UIModalPresentationNone;
|
407
|
+
}
|
408
|
+
|
409
|
+
- (BOOL)popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController{
|
410
|
+
return YES;
|
48
411
|
}
|
49
412
|
|
50
413
|
@end
|