<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Objective-C &#8211; 编程技术记录</title>
	<atom:link href="https://blog.z6z8.cn/category/%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5/objective-c/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.z6z8.cn</link>
	<description>世界你好!</description>
	<lastBuildDate>Fri, 01 Mar 2024 08:34:21 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>iOS13 同一App实现多窗口（iPad和Vision平台）</title>
		<link>https://blog.z6z8.cn/2024/02/26/ios13-%e5%90%8c%e4%b8%80app%e5%ae%9e%e7%8e%b0%e5%a4%9a%e7%aa%97%e5%8f%a3%ef%bc%88ipad%e5%92%8cvision%e5%b9%b3%e5%8f%b0%ef%bc%89/</link>
					<comments>https://blog.z6z8.cn/2024/02/26/ios13-%e5%90%8c%e4%b8%80app%e5%ae%9e%e7%8e%b0%e5%a4%9a%e7%aa%97%e5%8f%a3%ef%bc%88ipad%e5%92%8cvision%e5%b9%b3%e5%8f%b0%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Mon, 26 Feb 2024 09:54:22 +0000</pubDate>
				<category><![CDATA[iOS]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">https://blog.z6z8.cn/?p=1448</guid>

					<description><![CDATA[在Info.plist中配置多窗口支持 Enable Multiple Windows 置为 YES 新建一个 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>在Info.plist中配置多窗口支持</h1>
<ul>
<li>Enable Multiple Windows 置为 YES</li>
<li>新建一个Scene配置（如果只有一个窗口，则不需要此操作）</li>
</ul>
<p><img decoding="async" src="/wp-content/uploads/2024/02/1-300x94.png" alt="" /></p>
<h1>判断当前环境是否支持多窗口</h1>
<p>iOS13及以上支持多窗口；iPad设备和Vision设备支持，但iPhone不支持。</p>
<pre><code class="language-objc"> if (@available(iOS 13.0, *))
{
    if (UIApplication.sharedApplication.supportsMultipleScenes)
    {// Info.plist也配置了多窗口开关

        if  (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
            || UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomVision)
        {
            //支持多窗口
        }
    }
}</code></pre>
<h1>使用API创建新窗口</h1>
<h2>创建新的SceneDelegate</h2>
<p>当App启用Scene后，需要使用Scene管理Window。为了管理新窗口，我们新建一个SceneDelegate</p>
<pre><code class="language-objc">
@interface SecondSceneDelegate : UIResponder &lt; UIWindowSceneDelegate ,NSUserActivityDelegate&gt;

@property (nonatomic,strong) UIWindow * window;

@end

@implementation SecondSceneDelegate
// 将要连接到某个场景（Window需要依附在具体场景）
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
{
    if (self.window == nil)
    {
        SecondSceneViewController *vc = [[SecondSceneViewController alloc] init]; // 自己定义的页面
        UINavigationController *naviVc = [[UINavigationController alloc] initWithRootViewController:vc];//页面导航
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.windowScene = (UIWindowScene *)scene;
        self.window.rootViewController = naviVc;
        [self.window makeKeyAndVisible];
    }
}
@end
</code></pre>
<h2>创建新窗口</h2>
<p>使用<code>requestSceneSessionActivation</code></p>
<pre><code class="language-objc">NSUserActivity * activity = [[NSUserActivity alloc] initWithActivityType:@&quot;open_by_first&quot;];
[UIApplication.sharedApplication requestSceneSessionActivation:nil  //此参数为nil时，表示要求系统创建一个SceneSession
                                                  userActivity:activity
                                                       options:optios
                                                  errorHandler:^(NSError * _Nonnull error) {
    NSLog(@&quot;%@&quot;,error);
}];
</code></pre>
<h1>同时兼容iOS13及以上和iOS12及以下</h1>
<p>请先去除工程中的StoryBoard的相关配置（否则，以下代码可能不会完全生效）。</p>
<h2>在Info.plist中配置多窗口支持（参考上文）</h2>
<p>略</p>
<h2>修改AppDelegate</h2>
<pre><code class="language-objc">
@interface AppDelegate : UIResponder &lt;UIApplicationDelegate&gt;

@property (nullable, nonatomic, strong) UIWindow *window; // 添加属性，注意这是UIApplicationDelegate的协议实现

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if (@available(iOS 13.0, *))
    { //iOS 13，启用Scene后，窗口在Scene的实现里创建
        return YES;
    }
    else
    {
        FirstSceneViewController *vc = [[FirstSceneViewController alloc] init]; // 自己定义的页面
        UINavigationController *naviVc = [[UINavigationController alloc] initWithRootViewController:vc];//页面导航
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.rootViewController = naviVc;
        [self.window makeKeyAndVisible];
        return YES;
    }
}

- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options 
{
    NSString *cfgName = @&quot;FirstScene&quot;; //场景名字
    if ([options.userActivities.objectEnumerator.nextObject.activityType isEqualToString:@&quot;open_by_first&quot;]) //使用哪个场景
    {
        cfgName = @&quot;SecondScene&quot;;
    }
    return [[UISceneConfiguration alloc] initWithName:cfgName sessionRole:connectingSceneSession.role];
}
@end
</code></pre>
<h2>创建（修改）SceneDelegate</h2>
<p>为求简单，本文采用多组<code>Window+SceneDelegate</code>来管理多个窗口，即每个Window都有自己的SceneDelegate。本文实现了两个窗口的情形。</p>
<p>第一个SceneDelegate代码如下</p>
<pre><code class="language-objc">
@interface FirstSceneDelegate : UIResponder &lt;UIWindowSceneDelegate&gt;

@property (strong, nonatomic) UIWindow * window;

@end

@implementation FirstSceneDelegate

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions 
{
    session.userInfo = @{@&quot;userData&quot;:@&quot;first_session&quot;};
    if (self.window == nil)
    {
        FirstSceneViewController *vc = [[FirstSceneViewController alloc] init]; // 自己定义的页面
        UINavigationController *naviVc = [[UINavigationController alloc] initWithRootViewController:vc];//页面导航
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.windowScene = (UIWindowScene *)scene;
        self.window.rootViewController = naviVc;
        [self.window makeKeyAndVisible];
    }
}
@end
</code></pre>
<p>第二个SceneDelegate代码如下</p>
<pre><code class="language-objc">
@interface SecondSceneDelegate : UIResponder &lt; UIWindowSceneDelegate ,NSUserActivityDelegate&gt;

@property (nonatomic,strong) UIWindow * window;

@end

@implementation SecondSceneDelegate

// 将要连接到某个场景（Window需要依附在具体场景）
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
{
    session.userInfo = @{@&quot;userData&quot;:@&quot;second_session&quot;};
    if (self.window == nil)
    {
        SecondSceneViewController *vc = [[SecondSceneViewController alloc] init]; // 自己定义的页面
        UINavigationController *naviVc = [[UINavigationController alloc] initWithRootViewController:vc];//页面导航
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.windowScene = (UIWindowScene *)scene;
        self.window.rootViewController = naviVc;
        [self.window makeKeyAndVisible];
    }

}

@end
</code></pre>
<h2>在第一个窗口的页面下，使用按钮打开第二个窗口</h2>
<p>按钮事件响应</p>
<pre><code class="language-objc">- (void)onClickButton:(id)sender
{
    if (@available(iOS 13.0, *))
    {
        if (UIApplication.sharedApplication.supportsMultipleScenes)
        {
            if  (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
                 || UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomVision)
            {

                // 查找，确定是否已经打开过
                UISceneSession *foundSession = nil;
                NSString * foundStr = @&quot;second_session&quot;;
                for (UISceneSession * session in UIApplication.sharedApplication.openSessions)
                {
                    if (session.userInfo &amp;&amp; [foundStr isEqualToString:session.userInfo[@&quot;userData&quot;]])
                    {
                        foundSession = session;
                        break;
                    }
                }

                UISceneActivationRequestOptions *optios = nil;
                if (foundSession) // 已存在，激活即可
                {
                    [UIApplication.sharedApplication requestSceneSessionActivation:foundSession
                                                                      userActivity:nil
                                                                           options:optios
                                                                      errorHandler:^(NSError * _Nonnull error) {
                        NSLog(@&quot;%@&quot;,error);
                    }];
                }
                else // 新建场景
                {
                    NSUserActivity * activity = [[NSUserActivity alloc] initWithActivityType:@&quot;open_by_first&quot;];
                    [UIApplication.sharedApplication requestSceneSessionActivation:nil 
                                                                      userActivity:activity
                                                                           options:optios
                                                                      errorHandler:^(NSError * _Nonnull error) {
                        NSLog(@&quot;%@&quot;,error);
                    }];
                }
            }
        }
    }
}
</code></pre>
<h1>其他注意事项</h1>
<ul>
<li>启用Scene后，UIAlertView将无法使用（运行时崩溃），需要用UIAlertController进行替换</li>
<li>在iPad端，实现多窗口时，需要将Info.list里的<code>Requires full screen</code>置为false，同时要设置为支持所有屏幕方向。否则可能会收到类似错误“the delegate of workspace fbscenemanger declined to create scene”<br />
<img decoding="async" src="/wp-content/uploads/2024/02/截屏2024-03-01-16.29.54-300x99.png" alt="" /></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2024/02/26/ios13-%e5%90%8c%e4%b8%80app%e5%ae%9e%e7%8e%b0%e5%a4%9a%e7%aa%97%e5%8f%a3%ef%bc%88ipad%e5%92%8cvision%e5%b9%b3%e5%8f%b0%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>iOS运行时解析embedded.mobileprovison</title>
		<link>https://blog.z6z8.cn/2023/04/11/ios%e8%bf%90%e8%a1%8c%e6%97%b6%e8%a7%a3%e6%9e%90embedded-mobileprovison/</link>
					<comments>https://blog.z6z8.cn/2023/04/11/ios%e8%bf%90%e8%a1%8c%e6%97%b6%e8%a7%a3%e6%9e%90embedded-mobileprovison/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Tue, 11 Apr 2023 00:58:14 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<guid isPermaLink="false">https://blog.z6z8.cn/?p=1424</guid>

					<description><![CDATA[embedded.mobileprovison是由二进制的加密数据和普通文本字符串组成，我们使用NSPrope [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>embedded.mobileprovison是由二进制的加密数据和普通文本字符串组成，我们使用NSPropertyListSerialization类解析普通文本字符串</p>
<p>首先将普通文本字符串提前出来，这里用到了NSScanner</p>
<pre><code class="language-objc">NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@&quot;embedded&quot; ofType:@&quot;mobileprovision&quot;];
    NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSISOLatin1StringEncoding error:nil];
    NSScanner *scanner = [[NSScanner alloc] initWithString:embeddedProvisioning];
    if (![scanner scanUpToString:@&quot;&lt;plist&quot; intoString:nil]) {
        NSLog(@&quot;parse embedded.mobilerovision error, no plist markup&quot;);
        return nil;
    }

    NSString *extractedPlist = nil; //接收普通文本字符串
    if (![scanner scanUpToString:@&quot;&lt;/plist&gt;&quot; intoString:&amp;extractedPlist]) {
        NSLog(@&quot;parse embedded.mobilerovision error, no plist markup&quot;);
        return nil;
    }
    extractedPlist = [extractedPlist stringByAppendingString:@&quot;&lt;/plist&gt;&quot;]; //添加xml标签结束标记</code></pre>
<p>然后就可以使用NSPropertyListSerialization解析了</p>
<pre><code class="language-objc">    NSError *error;
    NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:[extractedPlist dataUsingEncoding:NSISOLatin1StringEncoding] options:NSPropertyListImmutable format:nil error:&amp;error];
    if (error || ![plist isKindOfClass:[NSDictionary class]])
    {
        NSLog(@&quot;parse embedded.mobilerovision error, %@&quot;,error);
        return nil;
    }
    return plist; //返回结果</code></pre>
<p>参考<br />
<a href="https://www.process-one.net/blog/reading-ios-provisioning-profile-in-swift/">https://www.process-one.net/blog/reading-ios-provisioning-profile-in-swift/</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2023/04/11/ios%e8%bf%90%e8%a1%8c%e6%97%b6%e8%a7%a3%e6%9e%90embedded-mobileprovison/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>iOS App页面置灰实现</title>
		<link>https://blog.z6z8.cn/2021/12/14/ios-app%e9%a1%b5%e9%9d%a2%e7%bd%ae%e7%81%b0%e5%ae%9e%e7%8e%b0/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Tue, 14 Dec 2021 02:19:00 +0000</pubDate>
				<category><![CDATA[iOS]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1153</guid>

					<description><![CDATA[App页面置灰，本质是将彩色图像转换为灰度图像，本文提供两种方法实现，一种是App整体置灰，一种是单个页面置灰 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>App页面置灰，本质是将彩色图像转换为灰度图像，本文提供两种方法实现，一种是App整体置灰，一种是单个页面置灰，可结合具体的业务场景使用。</p>
<h1>方法一：分别将图片和文字置灰</h1>
<p>一般情况下，App页面的颜色深度是24bit，也就是RGB各8bit；如果算上Alpha通道的话就是32bit，RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit，这8bit表示的颜色不是彩色，而是256种不同亮度的黑色或白色。<br />
说到灰度图像，在YUV颜色空间上---其中Y代表亮度，调整Y值就可以得到不同的灰度图像。<br />
理论上，颜色空间RGB和YUV是等价的，同一种颜色用RGB或YUV都可以表示。从RGB数值对应到亮度Y，一般采用公式Y = 0.299R+0.587G+0.114B，得到的结果再填充到RGB上就得到了对应的灰度RGB颜色。</p>
<pre><code>Y = 0.299R+0.587G+0.114B
Gray = RGB(Y,Y,Y)</code></pre>
<p>以上是方法一App页面置灰的原理基础。</p>
<h2>UIImage转成灰度图</h2>
<p>核心是创建一个灰度空间，然后将图像绘制到这个空间上</p>
<pre><code class="language-objc">－(UIImage*)getGrayImage:(UIImage*)sourceImage 
{ 
   int width = sourceImage.size.width; 
   int height = sourceImage.size.height; 

   // 创建灰度空间
   CGColorSpaceRef colorSpace =CGColorSpaceCreateDeviceGray(); 
   // 创建绘制上下文
   CGContextRef context =CGBitmapContextCreate(nil,width,height,8,0,colorSpace,kCGImageAlphaNone); 
   CGColorSpaceRelease(colorSpace); 

   if(context== NULL){ 
       return nil; 
   }

   // 绘制原始图像到新的上下文（灰度）
   CGContextDrawImage(context,CGRectMake(0,0, width, height), sourceImage.CGImage); 
   // 获取灰度图像
   CGImageRef grayImageRef =CGBitmapContextCreateImage(context);
   // CGImage -> UIImage
   UIImage*grayImage=[UIImage imageWithCGImage:grayImageRef];
   //回收资源
   CGContextRelease(context);
   CGImageRelease(grayImageRef);

   return grayImage; 
}</code></pre>
<h2>UIColor转成灰度颜色</h2>
<p>比较简单了，使用公式就可以了<code>Y = 0.299R+0.587G+0.114B</code></p>
<pre><code class="language-objc">UIColor *color = xxxx;
CGFloat r,g,b,a;
[color getRed:&r green:&g> blue:&b alpha:&a];
CGFloat y = 0.299*r+0.587*g+0.114*b;
UIColor *gray = [UIColor colorWithRed:y green:y blue:y alpha:a]</code></pre>
<h1>方法二：给App整体添加灰色滤镜</h1>
<p>这个方法可以是App整体置灰，包括WebView页面。<br />
原理就是把App页面当成一副图像，使用另一副偏灰图像和这个图像进行叠加运算，从而得到新的图像。<br />
iOS 提供了Core Image 滤镜，这些滤镜可以设置在UIView.layer上。<br />
我们要做的就是选取合适的滤镜，并将滤镜放置到App的最顶层</p>
<pre><code class="language-objc">
// 最顶层视图，承载滤镜，自身不接收、不拦截任何触摸事件
@interface UIViewOverLay : UIView
@end

@implementation UIViewOverLay
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return nil;
}
@end

UIWindow *window = App的Window;
UIViewOverLay *overlay = [[UIViewOverLay alloc]initWithFrame:self.window.bounds];
overlay.translatesAutoresizingMaskIntoConstraints = false;
overlay.backgroundColor = [UIColor lightGrayColor];
overlay.layer.compositingFilter = @"saturationBlendMode";
[window addSubview:overlay];

最后通过各种方法，要保证overlay在最顶层.</code></pre>
<p>上面使用的是UIView承载滤镜，其实看代码就知道了也可以直接使用CALayer来承载滤镜（需要注意的是在UIWindow上直接添加CALayer时，在某些特殊的场景可能会造成绘制异常）</p>
<h1>方法三: 给App整体添加灰色滤镜（私有API）</h1>
<p>灵感来自<code>UIVisualEffectView</code>这个公开的API。既然这个类能够实现App任意页面的毛玻璃效果--一个基于高斯模糊的滤镜，那么必然可以仿照这个类实现其他滤镜效果。于是找到了<code>CAFilter</code>这个私有API，示意代码如下</p>
<pre><code class="language-objc">        //获取RGBA颜色数值
        CGFloat r,g,b,a;
        [[UIColor lightGrayColor] getRed:&r green:&g blue:&b alpha:&a];
        //创建滤镜
        id cls = NSClassFromString(@"CAFilter");
        id filter = [cls filterWithName:@"colorMonochrome"];
        //设置滤镜参数
        [filter setValue:@[@(r),@(g),@(b),@(a)] forKey:@"inputColor"];
        [filter setValue:@(0) forKey:@"inputBias"];
        [filter setValue:@(1) forKey:@"inputAmount"];
        //设置给window
        window.layer.filters = [NSArray arrayWithObject:filter];</code></pre>
<h1>参考</h1>
<p><a href="https://www.cnblogs.com/zkwarrior/p/4957496.html">如何将真彩色图转换为各种灰度图</a></p>
<p><a href="https://www.cnblogs.com/justkong/p/6570914.html">RGB、YUV和HSV颜色空间模型</a></p>
<p><a href="https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CISubtractBlendMode">Core Image Filter</a></p>
<p><a href="https://iphonedev.wiki/index.php/CAFilter">CAFilter</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>小程序同层渲染&#8211;iOS WKWebView实现</title>
		<link>https://blog.z6z8.cn/2020/01/13/%e5%b0%8f%e7%a8%8b%e5%ba%8f%e5%90%8c%e5%b1%82%e6%b8%b2%e6%9f%93-ios-wkwebview%e5%ae%9e%e7%8e%b0/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Mon, 13 Jan 2020 08:18:33 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<category><![CDATA[学习笔记]]></category>
		<category><![CDATA[布局]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=769</guid>

					<description><![CDATA[先上代码 RenderWebViewWithNative 我只在模拟器环境中测了。 本文成文使用的环境 Xco [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>先上代码<br />
<a href="/wp-content/uploads/2020/01/RenderWebViewWithNative-1.zip" title="RenderWebViewWithNative">RenderWebViewWithNative</a><br />
我只在模拟器环境中测了。</p>
<p><img decoding="async" src="/wp-content/uploads/2020/01/RenderWebViewWithNative.png" alt="" /></p>
<h1>本文成文使用的环境</h1>
<ul>
<li>Xcode Version 11.3 (11C29)</li>
<li>模拟器版本Version 11.3 (SimulatorApp-912.5 SimulatorKit-570.3 CoreSimulator-681.17)，对应iOS 13.2</li>
</ul>
<h1>同层渲染步骤</h1>
<p>简单说下同层渲染效果，是将Native控件添加到WKWebView的子层级View中，最明显的是z-index 属性可以控制Native控件。详细请阅读https://developers.weixin.qq.com/community/develop/article/doc/000c4e433707c072c1793e56f5c813</p>
<h2>需要前端改造的地方</h2>
<p>为了让WKWebView为Native控件单独生成一个子层级的View，需要改变对应DOM节点的CSS样式,如</p>
<pre><code class="language-html">〈div style="position: fixed; font-size: 50px;overflow: scroll;-webkit-overflow-scrolling: touch" id="zl_native" 〉
            〈zl-native id="zl_native_node"〉world〈zl-native〉
〈/div〉</code></pre>
<p>本文成文环境中测试的结果是:</p>
<ul>
<li>WKWebView只为<code>position: fixed</code>的
<div>标签内容生成单独的子层级视图</li>
<li><code>overflow: scroll;-webkit-overflow-scrolling: touch</code>,在本文成文环境中不起作用；建议读者自行尝试更多的环境，以得到更准确的结论。</li>
</ul>
<h2>需要客户端改造的地方</h2>
<p>首先，当前端加载DOM时，检测到需要Native渲染的节点，需要通过jsbridge通知客户端，把节点的信息告诉客户端。客户端根据节点信息找到对应的子层级view</p>
<pre><code class="language-objc">-(UIView *)find_zl_native_view
{
    UIView *wkcontentview = [self get_wkcontentView];
    NSMutableArray *arr = [wkcontentview.subviews mutableCopy];
    while (arr.count > 0)
    {
        UIView *comp = arr.firstObject;
        [arr removeObjectAtIndex:0];
        if ([@"WKCompositingView" isEqualToString:NSStringFromClass([comp class])])
        {
            if ([comp.layer.name isEqualToString:@" <div> id='zl_native'"]) {
                return comp;
            }
        }

        if (comp.subviews.count > 0)
        {
            [arr addObjectsFromArray:comp.subviews];
        }
    }
    return nil;
}</code></pre>
<p>找到后，在这个子层级view上添加Native控件，渲染对应内容。</p>
<pre><code class="language-objc">-(void)startNativeRender_zl_native_node
{
    UIView *view = [self find_zl_native_view];
    UILabel *lbl = [[UILabel alloc] initWithFrame:view.bounds];
    [view addSubview:lbl];
    lbl.alpha = 0;
    [UIView animateWithDuration:3 animations:^{
        lbl.alpha = 0.6;
        lbl.text = @"world";
        lbl.textColor = [UIColor yellowColor];
        lbl.backgroundColor = [UIColor redColor];
        lbl.font = [UIFont systemFontOfSize:49.5];
    }];
}</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Objective-C在升序数组中使用二分法查找元素</title>
		<link>https://blog.z6z8.cn/2019/12/13/objective-c%e5%9c%a8%e5%8d%87%e5%ba%8f%e6%95%b0%e7%bb%84%e4%b8%ad%e4%bd%bf%e7%94%a8%e4%ba%8c%e5%88%86%e6%b3%95%e6%9f%a5%e6%89%be%e5%85%83%e7%b4%a0/</link>
					<comments>https://blog.z6z8.cn/2019/12/13/objective-c%e5%9c%a8%e5%8d%87%e5%ba%8f%e6%95%b0%e7%bb%84%e4%b8%ad%e4%bd%bf%e7%94%a8%e4%ba%8c%e5%88%86%e6%b3%95%e6%9f%a5%e6%89%be%e5%85%83%e7%b4%a0/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Fri, 13 Dec 2019 06:55:10 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=649</guid>

					<description><![CDATA[//在升序数组中，查找插入索引 ,插入索引是第一个恰当的位置（多个相同元素的第一个元素位置） //existS [&#8230;]]]></description>
										<content:encoded><![CDATA[<pre><code class="language-objc">
//在升序数组中，查找插入索引 ,插入索引是第一个恰当的位置（多个相同元素的第一个元素位置）
//existSame ,输出YES时，返回的索引是相等的元素索引，输出NO，返回的索引是需要插入的位置索引
NSUInteger FindInsertIndex(NSArray * ascSortedItmes , id obj, BOOL &amp;existSame,
                           int(^CompareTwoItem)(id item, id obj))
{
    existSame = NO;
    NSUInteger count = [ascSortedItmes count];
    if (count &gt; 0)
    {
        NSUInteger midIndex ,startIndex = 0 , endIndex = count -1 ,  safeN = count;
        while (safeN--) //safeN 保证while一定会结束 ,可以去掉这个判断
        {
            midIndex = ((startIndex + endIndex) &gt;&gt; 1); // (startIndex + endIndex) / 2
            id item = ascSortedItmes[midIndex];
            int cmp = CompareTwoItem(item ,obj);
            if (cmp &gt;0 ) //  item &gt; obj
            {
                if (endIndex == startIndex // 此时  endIndex == midIndex == startIndex
                    || midIndex == 0 )
                {
                    return midIndex;
                }
                endIndex = midIndex -1;
            }
            else if (cmp == 0) //处理相等情况，确保插入的索引是第一个恰当的索引(多个相同元素的第一个元素位置)
            {
                if (midIndex == startIndex) //此时，startIndex到endIndex最多两个元素
                {
                    existSame = YES;
                    return midIndex;
                }
                endIndex = midIndex;
            }
            else // item &lt; obj
            {
                startIndex = midIndex + 1;
                if (startIndex &gt; endIndex) // 此时 midIndex == endIndex
                {
                    return (startIndex &lt; count) ? startIndex : count;
                }
            }
        }
        ASSERT(0);//上面查找算法有问题
        return count;//表示附加到末尾
    }
    else
    {
        return 0;
    }
}
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/12/13/objective-c%e5%9c%a8%e5%8d%87%e5%ba%8f%e6%95%b0%e7%bb%84%e4%b8%ad%e4%bd%bf%e7%94%a8%e4%ba%8c%e5%88%86%e6%b3%95%e6%9f%a5%e6%89%be%e5%85%83%e7%b4%a0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Objective-C:动态创建新类（Class）</title>
		<link>https://blog.z6z8.cn/2019/11/29/objective-c%e5%8a%a8%e6%80%81%e5%88%9b%e5%bb%ba%e6%96%b0%e7%b1%bb%ef%bc%88class%ef%bc%89/</link>
					<comments>https://blog.z6z8.cn/2019/11/29/objective-c%e5%8a%a8%e6%80%81%e5%88%9b%e5%bb%ba%e6%96%b0%e7%b1%bb%ef%bc%88class%ef%bc%89/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Fri, 29 Nov 2019 03:10:40 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<category><![CDATA[iOS]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=592</guid>

					<description><![CDATA[动态创建一个类，NewClass ，继承自NSObject Class newClass = objc_all [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>动态创建一个类，NewClass ，继承自NSObject</p>
<pre><code class="language-objc">Class newClass = objc_allocateClassPair([NSObject class], "NewClass", 0);

//向运行时注册这个类
objc_registerClassPair(newClass);

//从运行时销毁这个类
objc_disposeClassPair(newClass);</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/11/29/objective-c%e5%8a%a8%e6%80%81%e5%88%9b%e5%bb%ba%e6%96%b0%e7%b1%bb%ef%bc%88class%ef%bc%89/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>学习笔记：macOS 选择文件对话框示例</title>
		<link>https://blog.z6z8.cn/2019/11/28/%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%9amacos-%e9%80%89%e6%8b%a9%e6%96%87%e4%bb%b6%e5%af%b9%e8%af%9d%e6%a1%86%e7%a4%ba%e4%be%8b/</link>
					<comments>https://blog.z6z8.cn/2019/11/28/%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%9amacos-%e9%80%89%e6%8b%a9%e6%96%87%e4%bb%b6%e5%af%b9%e8%af%9d%e6%a1%86%e7%a4%ba%e4%be%8b/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 28 Nov 2019 02:20:32 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<category><![CDATA[学习笔记]]></category>
		<category><![CDATA[macOS]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=584</guid>

					<description><![CDATA[NSOpenPanel控件 - (void)p_selectFileDialog:(void(^)(NSStr [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>NSOpenPanel控件</p>
<pre><code class="language-objc">- (void)p_selectFileDialog:(void(^)(NSString* filePath))block
{
    NSOpenPanel* panel = [NSOpenPanel openPanel];
    [panel setCanChooseDirectories:NO];
    [panel setAllowsMultipleSelection:NO];
    [panel setMessage:@"提示语"];

    // Display the panel attached to the document's window.
    [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result){
        if (result == NSModalResponseOK)
        {
            //点击确定后的代码
            block(panel.URL.path);
        }
        else
        {
            block(nil);
        }
    }];
}</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/11/28/%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%9amacos-%e9%80%89%e6%8b%a9%e6%96%87%e4%bb%b6%e5%af%b9%e8%af%9d%e6%a1%86%e7%a4%ba%e4%be%8b/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>iOS 获取开机启动后的累积时间</title>
		<link>https://blog.z6z8.cn/2019/11/20/ios-%e8%8e%b7%e5%8f%96%e5%bc%80%e6%9c%ba%e5%90%af%e5%8a%a8%e5%90%8e%e7%9a%84%e7%b4%af%e7%a7%af%e6%97%b6%e9%97%b4/</link>
					<comments>https://blog.z6z8.cn/2019/11/20/ios-%e8%8e%b7%e5%8f%96%e5%bc%80%e6%9c%ba%e5%90%af%e5%8a%a8%e5%90%8e%e7%9a%84%e7%b4%af%e7%a7%af%e6%97%b6%e9%97%b4/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Wed, 20 Nov 2019 10:13:04 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=579</guid>

					<description><![CDATA[[[NSProcessInfo processInfo] systemUptime]; 或者 #include [&#8230;]]]></description>
										<content:encoded><![CDATA[<pre><code class="language-objc">[[NSProcessInfo processInfo] systemUptime];</code></pre>
<p>或者</p>
<pre><code class="language-c">#include &lt;mach/mach.h&gt;
#include &lt;mach/mach_time.h&gt;
long long tm = mach_absolute_time();

static mach_timebase_info_data_t    sTimebaseInfo;
    if ( sTimebaseInfo.denom == 0 ) //静态整型变量初始化时默认为0
    {
        mach_timebase_info(&amp;sTimebaseInfo);
    }

    return tm * sTimebaseInfo.numer / sTimebaseInfo.denom  / (1000*1000);</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/11/20/ios-%e8%8e%b7%e5%8f%96%e5%bc%80%e6%9c%ba%e5%90%af%e5%8a%a8%e5%90%8e%e7%9a%84%e7%b4%af%e7%a7%af%e6%97%b6%e9%97%b4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Objective-C 网络对时的简单实现</title>
		<link>https://blog.z6z8.cn/2019/11/20/objective-c-%e7%bd%91%e7%bb%9c%e5%af%b9%e6%97%b6%e7%9a%84%e7%ae%80%e5%8d%95%e5%ae%9e%e7%8e%b0/</link>
					<comments>https://blog.z6z8.cn/2019/11/20/objective-c-%e7%bd%91%e7%bb%9c%e5%af%b9%e6%97%b6%e7%9a%84%e7%ae%80%e5%8d%95%e5%ae%9e%e7%8e%b0/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Wed, 20 Nov 2019 10:06:59 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=577</guid>

					<description><![CDATA[头文件 // // ZLNetworkTime.h // // Created by zxs.zl on 20 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>头文件</p>
<pre><code class="language-h">//
//  ZLNetworkTime.h
//
//  Created by zxs.zl on 2018/3/12.
//

#import &lt;Foundation/Foundation.h&gt;

@interface ZLNetworkTime : NSObject

+(void)startSyncTimeFromNetwork;
+(BOOL)syncTimeFromNetworkDone;

+(NSTimeInterval)timeIntervalSince1970;
@end
</code></pre>
<p>实现文件</p>
<pre><code class="language-objc">//
//  ZLNetworkTime.m
//
//  Created by zxs.zl on 2018/3/12.
//

#if ! __has_feature(objc_arc)
#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag
#endif

#import "ZLNetworkTime.h"

struct _ZLTimeBase_
{
    _ZLTimeBase_()
    {
        init_state = not_init;
        init_count = 0;
    }

    NSTimeInterval systemUptimeBase;
    NSTimeInterval networkTimBbase;
    enum
    {
        not_init,
        initing,
        inited,
    } init_state;
    int init_count;
}s_timeBase;

@implementation ZLNetworkTime

+(NSTimeInterval)timeIntervalSince1970
{
    if (s_timeBase.init_state == s_timeBase.inited)
        return s_timeBase.networkTimBbase + [[NSProcessInfo processInfo] systemUptime] - s_timeBase.systemUptimeBase;
    else
        return [[NSDate date] timeIntervalSince1970];
}

+(BOOL)syncTimeFromNetworkDone
{
    return s_timeBase.init_state == s_timeBase.inited;
}

+(void)startSyncTimeFromNetwork
{
    if (s_timeBase.init_state !=  s_timeBase.not_init)
        return;

    if (s_timeBase.init_count &gt;= 3)
        return;

    @synchronized(self){
        if (s_timeBase.init_state !=  s_timeBase.not_init)
            return;
        s_timeBase.init_state = s_timeBase.initing;
    }

    s_timeBase.init_count++;

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.timeoutIntervalForRequest = 3;
    config.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
    NSURLSession * session  = [NSURLSession sessionWithConfiguration:config];
    NSURL * url = [NSURL URLWithString:@"http://time.tianqi.com/"];

    NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
    NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse*)response;
        if ([httpResponse isKindOfClass:[NSHTTPURLResponse class]] &amp;&amp; httpResponse.statusCode == 200 )
        {
            NSString * dateStr = httpResponse.allHeaderFields[@"Date"];
            if (dateStr.length &gt; 0)
            {
                //Mon, 12 Mar 2018 07:21:32 GMT
                NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
                formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
                [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
                [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss 'GMT'"];

                NSDate *date = [formatter dateFromString:dateStr];
                if (date)
                {
                    s_timeBase.systemUptimeBase = [[NSProcessInfo processInfo] systemUptime];
                    s_timeBase.networkTimBbase = [date timeIntervalSince1970] + s_timeBase.systemUptimeBase - systemUptime;
                    s_timeBase.init_state = s_timeBase.init_state;
                    return;
                }

            }
        }
        s_timeBase.init_state = s_timeBase.not_init;
    }];
    [dataTask resume];
}

@end
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/11/20/objective-c-%e7%bd%91%e7%bb%9c%e5%af%b9%e6%97%b6%e7%9a%84%e7%ae%80%e5%8d%95%e5%ae%9e%e7%8e%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>iOS 推出指定的UIViewController</title>
		<link>https://blog.z6z8.cn/2019/11/19/ios-%e6%8e%a8%e5%87%ba%e6%8c%87%e5%ae%9a%e7%9a%84uiviewcontroller/</link>
					<comments>https://blog.z6z8.cn/2019/11/19/ios-%e6%8e%a8%e5%87%ba%e6%8c%87%e5%ae%9a%e7%9a%84uiviewcontroller/#respond</comments>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Tue, 19 Nov 2019 02:28:31 +0000</pubDate>
				<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[代码片段]]></category>
		<category><![CDATA[UINavigationController]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=573</guid>

					<description><![CDATA[UINavigationController提供的常用接口是push和pop，但都是操作最顶层的页面topVi [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><code>UINavigationController</code>提供的常用接口是push和pop，但都是操作最顶层的页面<code>topViewController</code>，若要操作指定的页面，可以访问属性<code>viewControllers</code></p>
<pre><code class="language-objc">
@implementation UINavigationController(zlAddition)

- (void)zl_popTheViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (viewController.navigationController == self)
    {
        if (self.topViewController == viewController)
        {
            [self popViewControllerAnimated:animated];
        }
        else
        {
            NSMutableArray *arr = [self.viewControllers mutableCopy];
            if (arr.lastObject != viewController)
            {
                [arr removeObject:viewController];
                [self setViewControllers:arr animated:animated];
            }
        }
    }
}

@end</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.z6z8.cn/2019/11/19/ios-%e6%8e%a8%e5%87%ba%e6%8c%87%e5%ae%9a%e7%9a%84uiviewcontroller/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
