使用 MapKit ios 绘制渐变折线

发布于 2024-11-01 20:16:23 字数 3398 浏览 1 评论 0原文

我正在尝试使用叠加层(MKOverlay)跟踪 MKMapView 上的路线。 但是,根据当前的速度,如果颜色发生变化(例如,如果用户从 65 英里每小时行驶到 30 英里每小时,则从绿色变为橙色),我希望在跟踪路线时执行类似 Nike 应用程序的渐变操作。

这是我想要的屏幕截图:

gradient overlay image

因此,每隔 20 米,我就会添加从旧到新的叠加层坐标使用:

// Create a c array of points. 
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D) * 2);

// Create 2 points.
MKMapPoint startPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(oldLatitude, oldLongitude));
MKMapPoint endPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(newLatitude, newLongitude));

// Fill the array.
pointsArray[0] = startPoint;
pointsArray[1] = endPoint;

// Erase polyline and polyline view if not nil.
if (self.routeLine != nil)
    self.routeLine = nil;

if (self.routeLineView != nil)
    self.routeLineView = nil;

// Create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];

// Add overlay to map.
[self.mapView addOverlay:self.routeLine];

// clear the memory allocated earlier for the points.
free(pointsArray);

// Save old coordinates.
oldLatitude = newLatitude;
oldLongitude = newLongitude;

基本上我添加了很多小叠加层。然后我想在这个小线条图上创建渐变,所以我尝试在覆盖委托中这样做:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
    MKOverlayView* overlayView = nil;

    if(overlay == self.routeLine) {
        // If we have not yet created an overlay view for this overlay, create it now. 
        if(self.routeLineView == nil) {
            self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];

            if (speedMPH < 25.0) {
                self.routeLineView.fillColor = [UIColor redColor];
                self.routeLineView.strokeColor = [UIColor redColor];
            }
            else if (speedMPH >= 25.0 && speedMPH < 50.0) {
                self.routeLineView.fillColor = [UIColor orangeColor];
                self.routeLineView.strokeColor = [UIColor orangeColor];
            }
            else {
                self.routeLineView.fillColor = [UIColor greenColor];
                self.routeLineView.strokeColor = [UIColor greenColor];
            }

            // Size of the trace.
            self.routeLineView.lineWidth = routeLineWidth;

            // Add gradient if color changed.
            if (oldColor != self.routeLineView.fillColor) {
                CAGradientLayer *gradient = [CAGradientLayer layer];
                    gradient.frame = self.routeLineView.bounds;
                    gradient.colors = [NSArray arrayWithObjects:(id)[oldColor CGColor], (id)[self.routeLineView.fillColor CGColor], nil];
                    [self.routeLineView.layer insertSublayer:gradient atIndex:0];
            }

            // Record old color for gradient.
            if (speedMPH < 25.0)
                oldColor = [UIColor redColor];
            else if (speedMPH >= 25.0 && speedMPH < 50.0)
                oldColor = [UIColor orangeColor];
            else
                oldColor = [UIColor greenColor];
        }

        overlayView = self.routeLineView;
    }

    return overlayView;
}

我试图以这种方式添加渐变,但我想这不是这样做的方法,因为我无法让它工作。

我还可以在每次用户位置(在位置对象的委托中)更新时跟踪路线,或者如上所述每 20 米跟踪一次。

你能帮我解决这个问题吗,给我一些提示! 谢谢!

I'm trying to trace a route on a MKMapView using overlays (MKOverlay).
However, depending on the current speed, I want to do something like the Nike app with a gradient while tracing the route, if the color is changing (for example, from green to orange if a user is driving from 65mph to 30mph).

Here's a screenshot of what I want:

gradient overlay image

So every 20 meters, I adding an overlay from the old to the new coordinates using:

// Create a c array of points. 
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D) * 2);

// Create 2 points.
MKMapPoint startPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(oldLatitude, oldLongitude));
MKMapPoint endPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(newLatitude, newLongitude));

// Fill the array.
pointsArray[0] = startPoint;
pointsArray[1] = endPoint;

// Erase polyline and polyline view if not nil.
if (self.routeLine != nil)
    self.routeLine = nil;

if (self.routeLineView != nil)
    self.routeLineView = nil;

// Create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];

// Add overlay to map.
[self.mapView addOverlay:self.routeLine];

// clear the memory allocated earlier for the points.
free(pointsArray);

// Save old coordinates.
oldLatitude = newLatitude;
oldLongitude = newLongitude;

Basically I'm adding a lot of little overlays. Then I would like to create the gradient on this little line drawing, so I'm trying to do so in the overlay delegate:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
    MKOverlayView* overlayView = nil;

    if(overlay == self.routeLine) {
        // If we have not yet created an overlay view for this overlay, create it now. 
        if(self.routeLineView == nil) {
            self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];

            if (speedMPH < 25.0) {
                self.routeLineView.fillColor = [UIColor redColor];
                self.routeLineView.strokeColor = [UIColor redColor];
            }
            else if (speedMPH >= 25.0 && speedMPH < 50.0) {
                self.routeLineView.fillColor = [UIColor orangeColor];
                self.routeLineView.strokeColor = [UIColor orangeColor];
            }
            else {
                self.routeLineView.fillColor = [UIColor greenColor];
                self.routeLineView.strokeColor = [UIColor greenColor];
            }

            // Size of the trace.
            self.routeLineView.lineWidth = routeLineWidth;

            // Add gradient if color changed.
            if (oldColor != self.routeLineView.fillColor) {
                CAGradientLayer *gradient = [CAGradientLayer layer];
                    gradient.frame = self.routeLineView.bounds;
                    gradient.colors = [NSArray arrayWithObjects:(id)[oldColor CGColor], (id)[self.routeLineView.fillColor CGColor], nil];
                    [self.routeLineView.layer insertSublayer:gradient atIndex:0];
            }

            // Record old color for gradient.
            if (speedMPH < 25.0)
                oldColor = [UIColor redColor];
            else if (speedMPH >= 25.0 && speedMPH < 50.0)
                oldColor = [UIColor orangeColor];
            else
                oldColor = [UIColor greenColor];
        }

        overlayView = self.routeLineView;
    }

    return overlayView;
}

I'm trying to add the gradient this way, but I guess it is not the way to do it because I can't make it to work.

I can also trace the route every time there is an update on the user's location (in the location object's delegate), or as above every 20 meters.

Can you please help me on that one, giving me tips!
Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

绝不放开 2024-11-08 20:16:23

我提出的想法之一是创建一个 CGPath,并在每次调用 drawMapRect 方法时用渐变描边它,因为 MKPolylineViewMKPlolylineRenderer< /code> 在 ios7 中。

我尝试通过子类化 MKOverlayPathRenderer 来实现此目的,但我未能挑选出单独的 CGPath,然后我发现了一个名为 -(void) strokePath:(CGPathRef)path inContext:(CGContextRef) 的神秘方法context 听起来像是我需要的,但是如果您在重写 drawMapRect 时不调用 super 方法,则不会调用它。

这就是我现在正在努力的。

我会继续尝试,所以如果我解决了一些问题,我会回来更新答案。

========更新=================================== ===============

在此处输入图像描述

这就是我的意思这些天的工作,我几乎实现了上面提到的基本想法,但是,是的,我仍然无法根据特定的mapRect挑选出单独的PATH,所以我只是在所有路径的boundingBox与当前相交时同时绘制带有渐变的所有路径地图矩形。糟糕的技巧,但现在可以使用。

在渲染类的 -(void) drawMapRect:(MKMapRect)mapRect ZoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context 方法中,我这样做:

CGMutablePathRef fullPath = CGPathCreateMutable();
BOOL pathIsEmpty = YES;

//merging all the points as entire path
for (int i=0;i< polyline.pointCount;i++){
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    if (pathIsEmpty){
        CGPathMoveToPoint(fullPath, nil, point.x, point.y);
        pathIsEmpty = NO;
    } else {
        CGPathAddLineToPoint(fullPath, nil, point.x, point.y);
    }
}

//get bounding box out of entire path.
CGRect pointsRect = CGPathGetBoundingBox(fullPath);
CGRect mapRectCG = [self rectForMapRect:mapRect];
//stop any drawing logic, cuz there is no path in current rect.
if (!CGRectIntersectsRect(pointsRect, mapRectCG))return;

然后我将整个路径逐点分割为单独绘制其渐变。
请注意,hues 数组包含映射每个位置速度的色调值。

for (int i=0;i< polyline.pointCount;i++){
    CGMutablePathRef path = CGPathCreateMutable();
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    ccolor = [UIColor colorWithHue:hues[i] saturation:1.0f brightness:1.0f alpha:1.0f];
    if (i==0){
        CGPathMoveToPoint(path, nil, point.x, point.y);
    } else {
        CGPoint prevPoint = [self pointForMapPoint:polyline.points[i-1]];
        CGPathMoveToPoint(path, nil, prevPoint.x, prevPoint.y);
        CGPathAddLineToPoint(path, nil, point.x, point.y);
        CGFloat pc_r,pc_g,pc_b,pc_a,
        cc_r,cc_g,cc_b,cc_a;
        [pcolor getRed:&pc_r green:&pc_g blue:&pc_b alpha:&pc_a];
        [ccolor getRed:&cc_r green:&cc_g blue:&cc_b alpha:&cc_a];
        CGFloat gradientColors[8] = {pc_r,pc_g,pc_b,pc_a,
                                    cc_r,cc_g,cc_b,cc_a};

        CGFloat gradientLocation[2] = {0,1};
        CGContextSaveGState(context);
        CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){self.lineWidth,self.lineWidth}).width;
        CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, self.lineCap, self.lineJoin, self.miterLimit);
        CGContextAddPath(context, pathToFill);
        CGContextClip(context);//<--clip your context after you SAVE it, important!
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocation, 2);
        CGColorSpaceRelease(colorSpace);
        CGPoint gradientStart = prevPoint;
        CGPoint gradientEnd = point;
        CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation);
        CGGradientRelease(gradient);
        CGContextRestoreGState(context);//<--Don't forget to restore your context.
    }
    pcolor = [UIColor colorWithCGColor:ccolor.CGColor];
}

这就是所有核心绘图方法,当然您在覆盖类中需要 pointsvelocity 并使用 CLLocationManager 提供它们。

最后一点是如何从速度中获取色调值,嗯,我发现如果色调范围从0.03~0.3正好代表从红色到绿色,所以我做了一些按比例映射到色调和速度。

最后一个,这是这个演示的完整源代码:https://github.com/wdanxna/GradientPolyline

如果看不到您绘制的线,请不要惊慌,我只是将地图区域定位在我的位置上:)

One of the idea I came up is to create a CGPath and stroke it with gradient every time when drawMapRect method been called, since the MKPolylineView is replaced by MKPlolylineRenderer in ios7.

I tried to implement this by subclassing a MKOverlayPathRenderer but I failed to pick out individual CGPath, then I find a mysterious method named-(void) strokePath:(CGPathRef)path inContext:(CGContextRef)context which sounds like what I need, but it will not be called if you don't call the super method when you override your drawMapRect.

thats what Im working out for now.

I'll keep trying so if I work out something I'll come back and update the answer.

=========UPDATE================================================

enter image description here

So that is what I'm worked out these days, I almost implemented the basic idea mentioned above but yes, I still cannot pick out an individual PATH according to specific mapRect, so I just draw all paths with gradient at the same time when the boundingBox of all paths intersects with current mapRect. poor trick, but work for now.

In the -(void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context method in render class, I do this:

CGMutablePathRef fullPath = CGPathCreateMutable();
BOOL pathIsEmpty = YES;

//merging all the points as entire path
for (int i=0;i< polyline.pointCount;i++){
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    if (pathIsEmpty){
        CGPathMoveToPoint(fullPath, nil, point.x, point.y);
        pathIsEmpty = NO;
    } else {
        CGPathAddLineToPoint(fullPath, nil, point.x, point.y);
    }
}

//get bounding box out of entire path.
CGRect pointsRect = CGPathGetBoundingBox(fullPath);
CGRect mapRectCG = [self rectForMapRect:mapRect];
//stop any drawing logic, cuz there is no path in current rect.
if (!CGRectIntersectsRect(pointsRect, mapRectCG))return;

Then I split the entire path point by point to draw its gradient individually.
note that the hues array containing hue value mapping each velocity of location.

for (int i=0;i< polyline.pointCount;i++){
    CGMutablePathRef path = CGPathCreateMutable();
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    ccolor = [UIColor colorWithHue:hues[i] saturation:1.0f brightness:1.0f alpha:1.0f];
    if (i==0){
        CGPathMoveToPoint(path, nil, point.x, point.y);
    } else {
        CGPoint prevPoint = [self pointForMapPoint:polyline.points[i-1]];
        CGPathMoveToPoint(path, nil, prevPoint.x, prevPoint.y);
        CGPathAddLineToPoint(path, nil, point.x, point.y);
        CGFloat pc_r,pc_g,pc_b,pc_a,
        cc_r,cc_g,cc_b,cc_a;
        [pcolor getRed:&pc_r green:&pc_g blue:&pc_b alpha:&pc_a];
        [ccolor getRed:&cc_r green:&cc_g blue:&cc_b alpha:&cc_a];
        CGFloat gradientColors[8] = {pc_r,pc_g,pc_b,pc_a,
                                    cc_r,cc_g,cc_b,cc_a};

        CGFloat gradientLocation[2] = {0,1};
        CGContextSaveGState(context);
        CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){self.lineWidth,self.lineWidth}).width;
        CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, self.lineCap, self.lineJoin, self.miterLimit);
        CGContextAddPath(context, pathToFill);
        CGContextClip(context);//<--clip your context after you SAVE it, important!
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocation, 2);
        CGColorSpaceRelease(colorSpace);
        CGPoint gradientStart = prevPoint;
        CGPoint gradientEnd = point;
        CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation);
        CGGradientRelease(gradient);
        CGContextRestoreGState(context);//<--Don't forget to restore your context.
    }
    pcolor = [UIColor colorWithCGColor:ccolor.CGColor];
}

That is all the core drawing method and of course you need points, velocity in your overlay class and feed them with CLLocationManager.

the last point is how to get hue value out of velocity, well, I found that if hue ranging from 0.03~0.3 is exactly represent from red to green, so I do some proportionally mapping to hue and velocity.

last of the last, here you are this is full source of this demo:https://github.com/wdanxna/GradientPolyline

don't panic if can't see the line you draw, I just position the map region on my position :)

苍暮颜 2024-11-08 20:16:23

我实现了受上述 @wdanxna 解决方案启发的 Swift 4 版本。有些事情已经改变,路径已经在超类中创建。

我没有将色调存储在渲染器中,而是创建了 MKPolyline 的子类,用于在构造函数中计算色调。然后我使用渲染器中的值获取折线。我将它映射到速度,但我想你可以将梯度映射到你想要的任何东西。

GradientPolyline

class GradientPolyline: MKPolyline {
    var hues: [CGFloat]?
    public func getHue(from index: Int) -> CGColor {
        return UIColor(hue: (hues?[index])!, saturation: 1, brightness: 1, alpha: 1).cgColor
    }
}

extension GradientPolyline {
    convenience init(locations: [CLLocation]) {
        let coordinates = locations.map( { $0.coordinate } )
        self.init(coordinates: coordinates, count: coordinates.count)

        let V_MAX: Double = 5.0, V_MIN = 2.0, H_MAX = 0.3, H_MIN = 0.03

        hues = locations.map({
            let velocity: Double = $0.speed

            if velocity > V_MAX {
                return CGFloat(H_MAX)
            }

            if V_MIN <= velocity || velocity <= V_MAX {
                return CGFloat((H_MAX + ((velocity - V_MIN) * (H_MAX - H_MIN)) / (V_MAX - V_MIN)))
            }

            if velocity < V_MIN {
                return CGFloat(H_MIN)
            }

            return CGFloat(velocity)
        })
    }
}

GradientPolylineRenderer

class GradidentPolylineRenderer: MKPolylineRenderer {

    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        let boundingBox = self.path.boundingBox
        let mapRectCG = rect(for: mapRect)

        if(!mapRectCG.intersects(boundingBox)) { return }


        var prevColor: CGColor?
        var currentColor: CGColor?

        guard let polyLine = self.polyline as? GradientPolyline else { return }

        for index in 0...self.polyline.pointCount - 1{
            let point = self.point(for: self.polyline.points()[index])
            let path = CGMutablePath()


            currentColor = polyLine.getHue(from: index)

            if index == 0 {
               path.move(to: point)
            } else {
                let prevPoint = self.point(for: self.polyline.points()[index - 1])
                path.move(to: prevPoint)
                path.addLine(to: point)

                let colors = [prevColor!, currentColor!] as CFArray
                let baseWidth = self.lineWidth / zoomScale

                context.saveGState()
                context.addPath(path)

                let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: [0, 1])

                context.setLineWidth(baseWidth)
                context.replacePathWithStrokedPath()
                context.clip()
                context.drawLinearGradient(gradient!, start: prevPoint, end: point, options: [])
                context.restoreGState()
            }
            prevColor = currentColor
        }
    }
}

如何使用

CLLocations数组创建一条线

let runRoute = GradientPolyline(locations: locations)
self.mapView.addOverlay(runRoute)

在委托中传递 GradientPolylineRenderer

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is GradientPolyline {
        let polyLineRender = GradientMKPolylineRenderer(overlay: overlay)
        polyLineRender.lineWidth = 7
        return polyLineRender
    }
}

结果

渐变折线

I implemented a Swift 4 version inspired of @wdanxna solution above. Some thing have changed, the path is already created in the superclass.

Instead of storing the hues in the renderer I made a subclass of MKPolyline that calculates the hues in the constructor. Then I grab the polyline with the values from the renderer. I mapped it to the speed but I guess you can map the gradient to whatever you want.

GradientPolyline

class GradientPolyline: MKPolyline {
    var hues: [CGFloat]?
    public func getHue(from index: Int) -> CGColor {
        return UIColor(hue: (hues?[index])!, saturation: 1, brightness: 1, alpha: 1).cgColor
    }
}

extension GradientPolyline {
    convenience init(locations: [CLLocation]) {
        let coordinates = locations.map( { $0.coordinate } )
        self.init(coordinates: coordinates, count: coordinates.count)

        let V_MAX: Double = 5.0, V_MIN = 2.0, H_MAX = 0.3, H_MIN = 0.03

        hues = locations.map({
            let velocity: Double = $0.speed

            if velocity > V_MAX {
                return CGFloat(H_MAX)
            }

            if V_MIN <= velocity || velocity <= V_MAX {
                return CGFloat((H_MAX + ((velocity - V_MIN) * (H_MAX - H_MIN)) / (V_MAX - V_MIN)))
            }

            if velocity < V_MIN {
                return CGFloat(H_MIN)
            }

            return CGFloat(velocity)
        })
    }
}

GradidentPolylineRenderer

class GradidentPolylineRenderer: MKPolylineRenderer {

    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        let boundingBox = self.path.boundingBox
        let mapRectCG = rect(for: mapRect)

        if(!mapRectCG.intersects(boundingBox)) { return }


        var prevColor: CGColor?
        var currentColor: CGColor?

        guard let polyLine = self.polyline as? GradientPolyline else { return }

        for index in 0...self.polyline.pointCount - 1{
            let point = self.point(for: self.polyline.points()[index])
            let path = CGMutablePath()


            currentColor = polyLine.getHue(from: index)

            if index == 0 {
               path.move(to: point)
            } else {
                let prevPoint = self.point(for: self.polyline.points()[index - 1])
                path.move(to: prevPoint)
                path.addLine(to: point)

                let colors = [prevColor!, currentColor!] as CFArray
                let baseWidth = self.lineWidth / zoomScale

                context.saveGState()
                context.addPath(path)

                let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: [0, 1])

                context.setLineWidth(baseWidth)
                context.replacePathWithStrokedPath()
                context.clip()
                context.drawLinearGradient(gradient!, start: prevPoint, end: point, options: [])
                context.restoreGState()
            }
            prevColor = currentColor
        }
    }
}

How to use

Create a line from an array of CLLocations

let runRoute = GradientPolyline(locations: locations)
self.mapView.addOverlay(runRoute)

Pass the GradientPolylineRenderer in the delegate

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is GradientPolyline {
        let polyLineRender = GradientMKPolylineRenderer(overlay: overlay)
        polyLineRender.lineWidth = 7
        return polyLineRender
    }
}

Result

Gradient Polyline

寄人书 2024-11-08 20:16:23

自 iOS 14 起 MKGradientPolyline 可能是最好的选择。它还可以与 StrokeStartStrokeEnd 配合使用,以便您可以为路径添加动画效果,而之前的解决方案目前尚不支持这一点。

Since iOS 14 MKGradientPolyline is probably the best option. It also works with strokeStart and strokeEnd so you can animate the path, which previous solutions don't currently support.

や三分注定 2024-11-08 20:16:23

在我看来,线条绘图视图中的drawRect缺少渐变的设置。绘制可能发生在覆盖层的不同线程上。请发布代码。

sounds to me that your drawRect in the line drawing view is missing the setting of the gradient. drawing might occur on a different thread for the overlays. please post the code.

染柒℉ 2024-11-08 20:16:23

gugge的代码非常有用。
不过这样改比较好。

return CGFloat((H_MIN + ((velocity - V_MIN) * (H_MAX - H_MIN)) / (V_MAX - V_MIN)))

gugge’s code is very useful.
But it's better to change it like this.

return CGFloat((H_MIN + ((velocity - V_MIN) * (H_MAX - H_MIN)) / (V_MAX - V_MIN)))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文